Github API, decode "date" - php

Im using Github's api to get my latest commits, and the date format returned looks like this
2012-01-25T11:23:28-08:00
I tried to do it like this:
$date = explode('T', $next['commit']['author']['date']);
$time = strtotime($date[0] .' '. $date[1]);
$date = date('M j, Y g:i a', $time);
But it didnt turn out right as php thought I was subtracting 8 hours from the time (because of the timezone). I would like to keep the timezone but i have no clue how to parse that. Does anyone know how to have it where the time is correct and shows the time zone abbreviation (GMT, PST etc etc )?

It cannot get any simpler than this:
$a = new DateTime("2012-01-25T11:23:28-08:00");
echo $a->format("Y-m-d H:i:s");
//outputs 2012-01-25 11:23:28
See the documentation of the DateTime class for more info.

The simple, mechanical, solution is to break the date down yourself completely:
$date = substr($next['commit']['author']['date'], 0, 10);
$time = substr($next['commit']['author']['date'], 11, 9);
$zone = substr($next['commit']['author']['date'], 20, 6);
list($y, $m, $d) = explode('-', $date);
list($h, $i, $s) = explode(':', $time);
$zh = substr($zone, 1, 2);
$zm = substr($zone, 4, 2);
if (substr($zone, 0, 1) == '-'){
$h -= $zh;
$m -= $zm;
}else{
$h += $zh;
$m += $zm;
}
$ts = gmmktime($h,$i,$s,$m,$d,$y);
This will give you a timestamp in UTC.
The issue is with "shows the time zone abbreviation" - you can't find a abbreviation for a given offset, because there can be several - you can't tell which of the e.g. +01:00 timezones the date is in - could be european, african, or bristish summer time.
It really depends what you want to do with the data.

Related

PHP date format formatting a year as future date (like 2050) instead of 1950 when date is less than 1970

I am formatting a date like '121654' as below;
$date = DateTime::createFromFormat('mdy','121654');
echo $date->format('Y-m-d');
Now the output is;
2054-12-16
But I want it to be like 1954-12-16.
Up to 1970 it is giving correct result. But if year is less than 1970 it becomes a future date.
Is there any way to make things as I wanted.
Use Y as year to set 1954.
https://3v4l.org/4K7Ti
$in = "121654";
$in19 = Substr($in, 0,4) . "19". Substr($in, 4);
$date = DateTime::createFromFormat('mdY',$in19);
echo $date->format('Y-m-d');
You should use Y instead of y and specify 1954 here.
$date = DateTime::createFromFormat('mdY','12161954');
echo $date->format('Y-m-d');
Well you can check if the date generated is less than 1970 then you can use functions to add X year to it, so it will be in the future :
$date = DateTime::createFromFormat('mdY','12161960');
$date1970 = DateTime::createFromFormat('mdY','01011970');
if ($date < $date1970) {
$newDate = $date->modify('+500 years');
}
echo $newDate->format('Y-m-d');
If the format is guaranteed to be MMDDYY, I'd approach it by fixing the data itself:
list($m, $d, $y) = str_split('121654', 2);
$date = DateTime::createFromFormat('mdY', "$m{$d}19$y");
You can even add a step there in-between to test some custom cutoff year, e.g.:
if ($y > 20) { // or whatever your logic is
$y = "19$y";
}

PHP strtotime +1 month behaviour

I know about the unwanted behaviour of PHP's function
strtotime
For example, when adding a month (+1 month) to dates like: 31.01.2011 -> 03.03.2011
I know it's not officially a PHP bug, and that this solution has some arguments behind it, but at least for me, this behavior has caused a lot waste of time (in the past and present) and I personally hate it.
What I found even stranger is that for example in:
MySQL: DATE_ADD('2011-01-31', INTERVAL 1 MONTH) returns 2011-02-28
or
C# where new DateTime(2011, 01, 31).AddMonths(1); will return 28.02.2011
wolframalpha.com giving 31.01.2013 + 1 month as input; will return Thursday, February 28, 2013
It sees to me that others have found a more decent solution to the stupid question that I saw alot in PHP bug reports "what day will it be, if I say we meet in a month from now" or something like that. The answer is: if 31 does not exists in next month, get me the last day of that month, but please stick to next month.
So MY QUESTION IS: is there a PHP function (written by somebody) that resolves this not officially recognized bug? As I don't think I am the only one who wants another behavior when adding / subtracting months.
I am particulary interested in solutions what also work not just for the end of the month, but a complete replacement of strtotime. Also the case strotime +n months should be also dealt with.
Happy coding!
what you need is to tell PHP to be smarter
$the_date = strtotime('31.01.2011');
echo date('r', strtotime('last day of next month', $the_date));
$the_date = strtotime('31.03.2011');
echo date('r', strtotime('last day of next month', $the_date));
assuming you are only interesting on the last day of next month
reference - http://www.php.net/manual/en/datetime.formats.relative.php
PHP devs surely don't consider this as bug. But in strtotime's docs there are few comments with solutions for your problem (look for 28th Feb examples ;)), i.e. this one extending DateTime class:
<?php
// this will give us 2010-02-28 ()
echo PHPDateTime::DateNextMonth(strftime('%F', strtotime("2010-01-31 00:00:00")), 31);
?>
Class PHPDateTime:
<?php
/**
* IA FrameWork
* #package: Classes & Object Oriented Programming
* #subpackage: Date & Time Manipulation
* #author: ItsAsh <ash at itsash dot co dot uk>
*/
final class PHPDateTime extends DateTime {
// Public Methods
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Calculate time difference between two dates
* ...
*/
public static function TimeDifference($date1, $date2)
$date1 = is_int($date1) ? $date1 : strtotime($date1);
$date2 = is_int($date2) ? $date2 : strtotime($date2);
if (($date1 !== false) && ($date2 !== false)) {
if ($date2 >= $date1) {
$diff = ($date2 - $date1);
if ($days = intval((floor($diff / 86400))))
$diff %= 86400;
if ($hours = intval((floor($diff / 3600))))
$diff %= 3600;
if ($minutes = intval((floor($diff / 60))))
$diff %= 60;
return array($days, $hours, $minutes, intval($diff));
}
}
return false;
}
/**
* Formatted time difference between two dates
*
* ...
*/
public static function StringTimeDifference($date1, $date2) {
$i = array();
list($d, $h, $m, $s) = (array) self::TimeDifference($date1, $date2);
if ($d > 0)
$i[] = sprintf('%d Days', $d);
if ($h > 0)
$i[] = sprintf('%d Hours', $h);
if (($d == 0) && ($m > 0))
$i[] = sprintf('%d Minutes', $m);
if (($h == 0) && ($s > 0))
$i[] = sprintf('%d Seconds', $s);
return count($i) ? implode(' ', $i) : 'Just Now';
}
/**
* Calculate the date next month
*
* ...
*/
public static function DateNextMonth($now, $date = 0) {
$mdate = array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
list($y, $m, $d) = explode('-', (is_int($now) ? strftime('%F', $now) : $now));
if ($date)
$d = $date;
if (++$m == 2)
$d = (($y % 4) === 0) ? (($d <= 29) ? $d : 29) : (($d <= 28) ? $d : 28);
else
$d = ($d <= $mdate[$m]) ? $d : $mdate[$m];
return strftime('%F', mktime(0, 0, 0, $m, $d, $y));
}
}
?>
Here's the algorithm you can use. It should be simple enough to implement yourself.
Have the original date and the +1 month date in variables
Extract the month part of both variables
If the difference is greater than 1 month (or if the original is December and the other is not January) change the latter variable to the last day of the next month. You can use for example t in date() to get the last day: date( 't.m.Y' )
Had the same issue recently and ended up writing a class that handles adding/subtracting various time intervals to DateTime objects.
Here's the code:
https://gist.github.com/pavlepredic/6220041#file-gistfile1-php
I've been using this class for a while and it seems to work fine, but I'm really interested in some peer review. What you do is create a TimeInterval object (in your case, you would specify 1 month as the interval) and then call addToDate() method, making sure you set $preventMonthOverflow argument to true. The code will make sure that the resulting date does not overflow into next month.
Sample usage:
$int = new TimeInterval(1, TimeInterval::MONTH);
$date = date_create('2013-01-31');
$future = $int->addToDate($date, true);
echo $future->format('Y-m-d');
Resulting date is:
2013-02-28
Here is an implementation of an improved version of Juhana's answer above:
<?php
function sameDateNextMonth(DateTime $createdDate, DateTime $currentDate) {
$addMon = clone $currentDate;
$addMon->add(new DateInterval("P1M"));
$nextMon = clone $currentDate;
$nextMon->modify("last day of next month");
if ($addMon->format("n") == $nextMon->format("n")) {
$recurDay = $createdDate->format("j");
$daysInMon = $addMon->format("t");
$currentDay = $currentDate->format("j");
if ($recurDay > $currentDay && $recurDay <= $daysInMon) {
$addMon->setDate($addMon->format("Y"), $addMon->format("n"), $recurDay);
}
return $addMon;
} else {
return $nextMon;
}
}
This version takes $createdDate under the presumption that you are dealing with a recurring monthly period, such as a subscription, that started on a specific date, such as the 31st. It always takes $createdDate so late "recurs on" dates won't shift to lower values as they are pushed forward thru lesser-valued months (e.g., so all 29th, 30th or 31st recur dates won't eventually get stuck on the 28th after passing thru a non-leap-year February).
Here is some driver code to test the algorithm:
$createdDate = new DateTime("2015-03-31");
echo "created date = " . $createdDate->format("Y-m-d") . PHP_EOL;
$next = sameDateNextMonth($createdDate, $createdDate);
echo " next date = " . $next->format("Y-m-d") . PHP_EOL;
foreach(range(1, 12) as $i) {
$next = sameDateNextMonth($createdDate, $next);
echo " next date = " . $next->format("Y-m-d") . PHP_EOL;
}
Which outputs:
created date = 2015-03-31
next date = 2015-04-30
next date = 2015-05-31
next date = 2015-06-30
next date = 2015-07-31
next date = 2015-08-31
next date = 2015-09-30
next date = 2015-10-31
next date = 2015-11-30
next date = 2015-12-31
next date = 2016-01-31
next date = 2016-02-29
next date = 2016-03-31
next date = 2016-04-30
I have solved it by this way:
$startDate = date("Y-m-d");
$month = date("m",strtotime($startDate));
$nextmonth = date("m",strtotime("$startDate +1 month"));
if((($nextmonth-$month) > 1) || ($month == 12 && $nextmonth != 1))
{
$nextDate = date( 't.m.Y',strtotime("$initialDate +1 week"));
}else
{
$nextDate = date("Y-m-d",strtotime("$initialDate +1 month"));
}
echo $nextDate;
Somewhat similar to the Juhana's answer but more intuitive and less complications expected. Idea is like this:
Store original date and the +n month(s) date in variables
Extract the day part of both variables
If days do not match, subtract number of days from the future date
Plus side of this solution is that works for any date (not just the border dates) and it also works for subtracting months (by putting - instead of +).
Here is an example implementation:
$start = mktime(0,0,0,1,31,2015);
for ($contract = 0; $contract < 12; $contract++) {
$end = strtotime('+ ' . $contract . ' months', $start);
if (date('d', $start) != date('d', $end)) {
$end = strtotime('- ' . date('d', $end) . ' days', $end);
}
echo date('d-m-Y', $end) . '|';
}
And the output is following:
31-01-2015|28-02-2015|31-03-2015|30-04-2015|31-05-2015|30-06-2015|31-07-2015|31-08-2015|30-09-2015|31-10-2015|30-11-2015|31-12-2015|
function ldom($m,$y){
//return tha last date of a given month based on the month and the year
//(factors in leap years)
$first_day= strtotime (date($m.'/1/'.$y));
$next_month = date('m',strtotime ( '+32 day' , $first_day)) ;
$last_day= strtotime ( '-1 day' , strtotime (date($next_month.'/1/'.$y)) ) ;
return $last_day;
}

UNIX timestamp of the last time an entry was edited

In Expression Engine the {edit_date} tag would not return a UNIX timestamp like {entry_date} does. To work around that I used the approach below.
Does any one have an idea of how this could be made easier?
{exp:channel:entries channel="pieces" limit="1" track_views="three"}
<?php
$edit_date_string = {edit_date};
$edit_date = NULL;
if(!isset($edit_date_string)) {
$edit_date = {entry_date};
} else {
// Date format: 2011 05 25 00:53:44
// Raw: 20110525005344
$hour = substr($edit_date_string, -6, 2);
$minute = substr($edit_date_string, -4, 2);
$second = substr($edit_date_string, -2, 2);
$day = substr($edit_date_string, -8, 2);
$month = substr($edit_date_string, -10, 2);
$year = substr($edit_date_string, -12, 2);
$edit_date = mktime($hour, $minute, $second, $month, $day, $year);
}
echo $edit_date;
?>
{/exp:channel:entries}
As a side note it seems this is an inconsistency which makes it harder to process and compare dates. There would probably be a good reason for it. Does anyone know? Thanks
If you've got PHP 5.3, there's DateTime::createFromFormat:
$edit_date = DateTime::createFromFormat('YmdHis', $edit_date_string)->getTimestamp();
Or, even more directly, according to the Expression Engine docs:
$edit_date = {edit_date format="%U"}

php dateTime::createFromFormat in 5.2?

I have been developing on php 5.3.
However our production server is 5.2.6.
I have been using
$schedule = '31/03/2011 01:22 pm'; // example input
if (empty($schedule))
$schedule = date('Y-m-d H:i:s');
else {
$schedule = dateTime::createFromFormat('d/m/Y h:i a', $schedule);
$schedule = $schedule->format('Y-m-d H:i:s');
}
echo $schedule;
However that function is not available in 5.2
What is the easiest way to get around this (no chance of a php upgrade).
just include the next code
function DEFINE_date_create_from_format()
{
function date_create_from_format( $dformat, $dvalue )
{
$schedule = $dvalue;
$schedule_format = str_replace(array('Y','m','d', 'H', 'i','a'),array('%Y','%m','%d', '%I', '%M', '%p' ) ,$dformat);
// %Y, %m and %d correspond to date()'s Y m and d.
// %I corresponds to H, %M to i and %p to a
$ugly = strptime($schedule, $schedule_format);
$ymd = sprintf(
// This is a format string that takes six total decimal
// arguments, then left-pads them with zeros to either
// 4 or 2 characters, as needed
'%04d-%02d-%02d %02d:%02d:%02d',
$ugly['tm_year'] + 1900, // This will be "111", so we need to add 1900.
$ugly['tm_mon'] + 1, // This will be the month minus one, so we add one.
$ugly['tm_mday'],
$ugly['tm_hour'],
$ugly['tm_min'],
$ugly['tm_sec']
);
$new_schedule = new DateTime($ymd);
return $new_schedule;
}
}
if( !function_exists("date_create_from_format") )
DEFINE_date_create_from_format();
Because strtotime does poorly when confronted with D/M/Y and date_create_from_format isn't available, strptime may be your only hope here. It does some pretty oldschool things, like deal with years as if they are the number of years since 1900 and deal with months as if January was month zero. Here's some horrible example code that uses sprintf to reassemble the date into something DateTime understands:
$schedule = '31/03/2011 01:22 pm';
// %Y, %m and %d correspond to date()'s Y m and d.
// %I corresponds to H, %M to i and %p to a
$ugly = strptime($schedule, '%d/%m/%Y %I:%M %p');
$ymd = sprintf(
// This is a format string that takes six total decimal
// arguments, then left-pads them with zeros to either
// 4 or 2 characters, as needed
'%04d-%02d-%02d %02d:%02d:%02d',
$ugly['tm_year'] + 1900, // This will be "111", so we need to add 1900.
$ugly['tm_mon'] + 1, // This will be the month minus one, so we add one.
$ugly['tm_mday'],
$ugly['tm_hour'],
$ugly['tm_min'],
$ugly['tm_sec']
);
echo $ymd;
$new_schedule = new DateTime($ymd);
echo $new_schedule->format('Y-m-d H:i:s');
If it works, you should see the same, correct date and time printed twice.
I think it is much cleaner to extend the DateTime class and implement createFromFormat() yourself like this:-
class MyDateTime extends DateTime
{
public static function createFromFormat($format, $time, $timezone = null)
{
if(!$timezone) $timezone = new DateTimeZone(date_default_timezone_get());
$version = explode('.', phpversion());
if(((int)$version[0] >= 5 && (int)$version[1] >= 2 && (int)$version[2] > 17)){
return parent::createFromFormat($format, $time, $timezone);
}
return new DateTime(date($format, strtotime($time)), $timezone);
}
}
$dateTime = MyDateTime::createFromFormat('Y-m-d', '2013-6-13');
var_dump($dateTime);
var_dump($dateTime->format('Y-m-d'));
This will work in all versions of PHP >= 5.2.0.
See here for a demo http://3v4l.org/djucq
Since this is not really showing how to convert YYYY:DDD:HH:MM:SS time to unix seconds using the "z" option you have to create you own functions to convert the DOY to month and day of month. This is what I did:
function _IsLeapYear ($Year)
{
$LeapYear = 0;
# Leap years are divisible by 4, but not by 100, unless by 400
if ( ( $Year % 4 == 0 ) || ( $Year % 100 == 0 ) || ( $Year % 400 == 0 ) ) {
$LeapYear = 1;
}
return $LeapYear;
}
function _DaysInMonth ($Year, $Month)
{
$DaysInMonth = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
return ((_IsLeapYear($Year) && $Month == 2) ? 29 : $DaysInMonth[$Month - 1]);
}
function yydddhhssmmToTime($Year, $DOY, $Hour, $Min, $Sec)
{
$Day = $DOY;
for ($Month = 1; $Day > _DaysInMonth($Year, $Month); $Month++) {
$Day -= _DaysInMonth($Year, $Month);
}
$DayOfMonth = $Day;
return mktime($Hour, $Min, $Sec, $Month, $DayOfMonth, $Year);
}
$timeSec = yydddhhssmmToTime(2016, 365, 23, 23, 23);
$str = date("m/d/Y H:i:s", $timeSec);
echo "unix seconds: " . $timeis . " " . $str ."<br>";
The output on the page shows its working since I can convert back the seconds back to the original input values.
unix seconds: 1483140203 12/30/2016 23:23:23
$your_datetime_object=new DateTime($date);
$date_format_modified=date_format($your_datetime_object,'D M d Y');//Change the format of date time
I had the similar problem with the production server at 5.2, so I used the above datetime to create an object and then change the format to my liking as above.
Date and Time only
$dateTime = DateTime::createFromFormat('Y-m-d\TH:i:s', '2015-04-20T18:56:42');
ISO8601 no colons
$dateTime = DateTime::createFromFormat('Y-m-d\TH:i:sO', '2015-04-20T18:56:42+0000');
ISO8601 with colons
$date = $dateTime->format('c');
Salesforce ISO8601 format
DateTime::createFromFormat('Y-m-d\TH:i:s.uO', '2015-04-20T18:56:42.000+0000');
Hope this saves someone time!

Date problem in PHP

I have a piece of PHP that is trying to do this:
1) given a string like "h m s" (where h=hr, m=min, s=sec)
2) Add the time from 1) to time()
3) format the result to look like "y-mth-d-h-min-s"
So say the time is now 01-01-2011 1am, I want it to add "10 0 0", which should give me 01-01-2011 11am, but for some reason at the moment, it does seem to add the string, but it's not accurate.
This is the code I'm using:
$values_arr['regx_expdate'] = date("Y-m-d H:i:s", time()+$values_arr['regx_expdate']);
where $values_arr['regx_expdate'] is the string in the format "h m s", eg. "10 0 0".
The main question is how would time() know if "10 0 0" is actually 10hrs 0min 0min, and not 10days 0hr 0min??
It does not.
It will cast it to int, interpret it as seconds and add it to the result of time().
Some code that could do as you describe would be:
list ($h,$m,$s) = explode(' ', $values_arr['regx_expdate'], 3);
$difference = 60*60*$h + 60*$m + $s;
$values_arr['regx_expdate'] = date("Y-m-d H:i:s", time()+$difference);
Easiest method I can think of is to extract each token from $values_arr['regx_expdate'], add up the seconds and simple add it to time().
For example
if (preg_match('/^(\d{1,2}) (\d{1,2}) (\d{1,2})$/', $values_arr['regx_expdate'], $units)) {
$seconds = $units[3] + ($units[2] * 60) + ($units[1] * 3600);
$newTimestamp = time() + $seconds;
}
After parsing your input string into Hours Minutes and Seconds, it might be worth reorganizing said array of values into a string that PHP's strtotime can process.
This little function may help, you may customize it to suit your purpose:
function AddingDaysFromNow($number_of_days)
{
$today = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
// today is now time return in seconds
$addingTime = $today + (86400 * $number_of_days);
// adding to advance it
//choice a date form at here
return date("Y-m-d", $addingTime);
}
//use it as
$expireDate = AddingDaysFromNow(2); // assume the 2 is advance to 2 days ahead
// return the day in future
Good luck!
To handle and convert dates in php you should first force everything into unixtimestamp and then you give it the structure you want
$date = date("THE-DATE-FORMAT-YOU-WANT", "THE-DATE-YOU-WOULD-LIKE-TO-CONVERT-IN-SECONDS");
//For example.
$new_date = date("Y-m-d H:i:s", strtotime($old_date));
$now = date("Y-m-d H:i:s", time());

Categories