Relative Time Vs Absolute Time In PHP - php

Please check the following examples:
$date1 = strtotime("tomorrow 4:00 PM");
$date2 = strtotime("16:00:00");
$date3 = strtotime("10 hours");
$date4 = strtotime("+1 day");
echo date("Y m d H:i:s",$date1)."<br>";
echo date("Y m d H:i:s",$date2)."<br>";
echo date("Y m d H:i:s",$date3)."<br>";
echo date("Y m d H:i:s",$date4)."<br>";
It gives me the output as below:
2013 06 10 16:00:00
2013 06 09 16:00:00
2013 06 09 20:50:25
2013 06 10 10:50:25
I am considering first two example($date1 and $date2) as absolute data and the last two as relative date. Now, with only given the $date1/$date2/$date3/$date4 variables, is it possible to say whether it is relative time or an absolute time please?
I did get a solution on another thread: PHP datetime string differentiation
But that worked until I considered the 2nd example($date2 as an absolute value), where it doesn't work. Also, may suggested for regular expression checks, but that doesn't seem reliable either.
I was just wondering if php had some integrated way to tell this either from its functions or DateTime objects. I searched for, but didn't found anything.
Looking forward to listen for your suggestions/feedbacks/possible solutions. Thanks.

There is no direct way, AFAIK but there is a trick that you can use with the second parameter to the strtotime function.
function is_absolute_time($time_string) {
$time_shift = time() + 60; // 1 min from now
$time_normal = strtotime($time_string);
$time_shifted = strtotime($time_string, $time_shift);
return $time_normal == $time_shifted;
}
The rationale is simple: If the time is absolute, a 1 min difference won't change the calculation by strtotime and both $time_normal and $time_shifted will be same. For relative times, however, the difference will be one minute (the value in $time_shift variable).
There is a caveat with this code though. This function will return FALSE even for absolute times (but not absolute dates) less than 1 minute from midnight. You can minimize this by changing $time_shift to:
$time_shift = time() + 5; // 5 seconds from now.
This code will now work properly until 5 seconds from midnight. I think you can go safely to as low as 2. There is an edge case that 1 second in future might not work.
To fix this problem altogether, you can try a different approach:
function is_absolute_time($time_string) {
$epoch = 0; // Epoch
$time_shift = 60; // 1 min from epoch
$time_normal = strtotime($time_string, $epoch);
$time_shifted = strtotime($time_string, $time_shift);
return $time_normal == $time_shifted;
}
You can try this last solution directly. I am just building up the reason for the solution throughout this post.

Given only the value 2013 06 10 16:00:00 the answer is simple: it's absolute. Whether this absolute timestamp was created as "absolute" timestamp or based on relation to another date is impossible to tell. All you have is "2013 06 10 16:00:00", there's no "residual relativeness" or anything of that kind still in it.
Even this is relative to the supposed birth of Christ though, which is relative to the earth floating around in space since the Big Bang... *trollface*

Related

What is the difference between 2 days and 48 hours in php

$new_date = date('Y-m-d', strtotime("+2 days"));
or
$new_date = date('Y-m-d', strtotime("+48 hours"));
Are they same or different
Are they same or different
They are quite different. Objectively speaking, 2 days does not always equal 48 hours.
Consider crossing a daylight-savings boundary.
For example, 2019-04-07T02:00:00+1100 (AEDT -> AEST)
$twoDays = new DateInterval('P2D');
$fortyEightHours = new DateInterval('PT48H');
$ref = new DateTimeImmutable('2019-04-07T01:00:00',
new DateTimeZone('Australia/Melbourne'));
echo 'Reference: ', $ref->format('r'), PHP_EOL;
echo 'Plus 2 days: ', $ref->add($twoDays)->format('r'), PHP_EOL;
echo 'Plus 48 hours: ', $ref->add($fortyEightHours)->format('r'), PHP_EOL;
This produces
Reference: Sun, 07 Apr 2019 01:00:00 +1100
Plus 2 days: Tue, 09 Apr 2019 01:00:00 +1000
Plus 48 hours: Tue, 09 Apr 2019 00:00:00 +1000
Note, HHVM produces a different result for some reason
Demo ~ https://3v4l.org/L4tKo
This list of common date / time related falsehoods is worth checking out ~ https://github.com/kdeldycke/awesome-falsehood#dates-and-time
It's worth pointing out that using the same reference date and manipulating it with strtotime() produces different results to those above.
Demo ~ https://3v4l.org/O6MTd
I suspect this is because its relative time calculations aren't as nuanced as DateInterval.
Semantically they are different, as demonstrated by Phil in the other answer. This means that in some other libraries that account for this the difference is real.
However, the implementation of strtotime() does not take this into account, so given your example, it makes no difference.
I was interested if it can be followed up through source code, so here goes:
Implementation of strtotime() does this:
strtotime() starts, calling timelib_strtotime() to create the initial ts construct
timelib_strtotime() calls scan() to parse the string
it reads the string you've given to it, and one of the tokens it gets is your +2 days or +48 hours, which is considered as relativetext.
for relativetext, among other things, timelib_set_relative() is called
in timelib_set_relative() it does a lookup from using timelib_lookup_relunit(), which uses timelib_relunit_lookup array, finding that you've provided either a construct with type TIMELIB_DAY, value 1, multiplier 2, or construct TIMELIB_HOUR, value 1, multiplier 24
It saves the information from previous call to s->time->relative construct
this construct gets passed on, until strtotime calls timelib_update_ts with it (and with timezone info!)
in timelib_update_ts we call do_adjust_relative() to act on it. We do it without using time zone parameter, which I would think is the critical mistake
do_adjust_relative() calls timelib_do_normalize()
timelib_do_normalize() for hours does do_range_limit(0, 24, 24, &time->h, &time->d), increasing days and decreasing hours if hours are over the hardcoded limit which is 24, and for days does do_range_limit_days(), increasing months and decreasing days if we are over the limit, taking into account leap time (but NOT time zones!).
on returning to timelib_update_ts, we call do_adjust_timezone with timezone parameter, but at this point we've already made all the adjustments and we know no more if it was done with +48 hours or with +2 days. Thus it makes no difference.
TLDR: strtotime() does not take time zones into account when doing the conversion, so in that function it makes no difference, even though it really should.

PHP - Parsing String As Past Date

Currently, I'm trying to parse out dates when messages were received into timestamps. I have the month and day but the year is not specified. The event always occurs at the most recent (human) reading of the time. It works great in most cases to do this:
$time = strtotime("Jan 2 8:38pm");
That returns a date for this year, which is correct. Unfortunately, I get problems when I try to do for example:
$time = strtotime("Dec 31 8:38pm");
That returns a date which hasn't happened yet, and wont happen for the whole rest of the year. Obviously, my message was not sent in the future. I need it to return December 31st of last year.
For weekdays, I had a solution by prepending 'last' before the weekday like so:
$time = strtotime("Last Saturday 8:38pm");
That always returned the time of the last Saturday. However, trying to do the same thing here doesn't work:
$time = strtotime("Last Dec 31 8:38pm");
This returns false. I know to decrement a date by 1 year, I can do this:
$time = strtotime("Dec 31 8:38pm -1 year");
And that works great for Dec 31. However, Jan 2 will now fail:
$time = strtotime("Jan 2 8:38pm -1 year");
One solution I thought of was to subtract off a year (86400 * 365) from the resulting value if it is past today's date. However, this result will fail if we passed over February of a leap year. In that case, we would end up with a time that was ahead by a day.
The best solution I came up with so far is this:
$time = strtotime($raw_time);
if ($time > time()) {
$time = strtotime($raw_time." -1 year");
}
It seems kind of wasteful to make two calls to strtotime which I know is probably not a very efficient function. Is this the most elegant solution?
Is anyone aware of an option in strtotime which forces the dates to be in the past instead of in the future?
Is there another way to parse these dates that I should consider?
Efficiency is important for this because I am going to be parsing a lot of dates with it, but I would also like simple and readable code so I can understand it later.
Your approach is fine, as there is no date format to get what you want. Another approach could be using the DateTime class:
$datetime = new DateTime($raw_time);
if ($datetime > new DateTime()) {
$datetime->modify('-1 year');
}
You could test which one of the two approaches is faster. My guess is that this is a micro-optimization that won't make a lot of difference.

Go over 24 hours in a date?

I am working on project (a Google Transit feed) where I am required to provide the times for each stop on a bus route in the following common format: 21:00:00 and so forth.
Problem is, if times continue past midnight for a given trip, they require it to continue the hour counting accordingly. They explain quite specifically that 02:00:00 should become 26:00:00 and 03:45:00 should become 27:45:00 etc.
I am baffled on how to display such with any of the date() or strtotime() functions.
The only thing I can think of in my particular situation would be to function match and replace any strings in my output between 00:00:00 and 04:00:00, as that would clearly mean (again, for me only) that these are trips originating before midnight, but I don't feel that's the correct way.
Well seeing as it's only displaying on the page, you can
firstly get your date from where ever
Let's say $date = 00:00:00
$exploded_date = explode(":", $date);
This takes $date and puts it into an array so
$exploded_date[0] is hh
$exploded_date[1] is mm
$exploded_date[2] is ss
Then what you can do is use ltrim() to remove the leading 0 from 00 to 04 $exploded_date[0] - This makes it comparable in the if statement I'll do after
if($exploded_date[0] <= 4) {
$exploded_date[0] = ltrim($exploded_date[0], "0");
$exploded_date[0] = $exploded_date[0]+24;
}
Then you can implode the array back together into one string
$date = implode(":", $exploded_date);
// if the hour is 00 to 04 it will come out as 24 to 28
// e.g. 24:35:30
echo $date;
Despite giving you an answer. It's a silly thing to be doing, but it's not your choice so here you go :)
The way you display something doesn't necesarily has to be the same way you store something.
I don't know how you calculate the times, but assuming you have a start date and time, and some interval, you could calculate the end time as follows:
date_default_timezone_set('Europe/London');
$start_datetime = new DateTime('2014-11-11T21:00:00');
$next_stop = new DateTime('2014-11-12T02:00:00');
echo $start_datetime->format('Y-m-d H:i'); // 2014-11-11 21:00
echo $next_stop->format('Y-m-d H:i'); // 2014-11-12 02:00
$interval = $start_datetime->diff($next_stop);
// display next stop: 2014-11-11 26:00
echo ($start_datetime->format('Y') + $interval->y) .'-'
. ($start_datetime->format('m') + $interval->m) .'-'
. ($start_datetime->format('d') + $interval->d) .' '
. ($start_datetime->format('H') + $interval->h) .':'
. ($start_datetime->format('i') + $interval->i);
What I'm doing: create the start date (& time) and the datetime of the next stop. With the DateTime::diff() function I'm calculating the difference, and then, only for display (!) I add up each year, month, day, hour and minute to the datetime year, month etc. of the next stop.
This way you can still store your dates and times in a way every human being and computer system will understand (because let's be honest; to represent a time as 27:45 PM is quite ridiculous...)
I don't know if you only want the hours to be added up and roll over the 24 hour, or also days in a month etc. It's up to you how you handle these cases. Good luck!

PHP get current local time based on GMT offset

There is a function date_default_timezone_set() in php which accepts timezone string like "UK/London". But unfortunately I have the GMT offset of the user only.
Let's say I am on GMT +5 and I want to get current time based on GMT +5. I did a lot of search on google but did not help.
Since, I do not have the time zone string I am looking for something like
$current_date = date("Y M D H:S, "GMT +5");
Please help
You can only exclusively use GMT offsets for right now, i.e. it's completely useless if the user told you his GMT offset half a year ago and you want to know now what time it is at his location. It could basically be anything within a range of a few hours. Even if you got the offset a few seconds ago, it may already be different by the time you do the calculations if you happen to hit a DST change exactly.
Having said that, you'll want to add/subtract the offset hours from the formatted time, which is really the only thing you can do.
$offset = 5;
$hours = (gmdate('H') + $offset) % 24;
echo $hours . gmdate(':i:s Y m d');
That's just an example, you'll need to handle minute offsets as well.
date_default_timezone_set('GMT');
$curTime = strtotime("+5 hours");
$current_date = date("Y M D H:S, $curTime);
set GMT timezone first or get GMT offset。
get timestamp
timestamp to date

Understanding date processing with strtotime

I'm trying to get my head round someone else's code which they've written for handling the dates of when news stories are published. The problem has come up because they are using this line -
$date = strtotime("midnight", strtotime($dateString));
to process a date selected using a jquery calendar widget. This works fine for future dates, but when you try to use a date which is in the previous calendar year, it uses the current year instead. I think this is due to "midnight" finding the closest instance of the selected day and month.
I could remove the "midnight", but I'm not sure what the repercussions of this would be - is there a reason that the midnight could be there?
EDIT: this is the full block of code which handles the date. The date contains the time, which allows the user to publish an item at a specific time.
$array['display_date'] = '24 October, 2011 17:30';
$string = $array['display_date'];
$dateString = substr($string, 0, -5);
$timeArray = explode(':', substr($string, -5));
$hours_in_secs = 60 * 60 * $timeArray[0];
$mins_in_secs = $timeArray[1];
$date = strtotime("midnight", strtotime($dateString));
$timestamp = $date + $hours_in_secs + $mins_in_secs;
//assign timestamp to validation array
$array['display_date'] = $timestamp;
echo $array['display_date']; // Output = 1351094430 (Oct 24 2012 17:00:30)
This really depends on what $dateString contains. Assuming your jQuery widget delivered the time portion as well, your colleague likely wanted to remove the time portion. Compare the following:
echo date(DATE_ATOM, strtotime('2010-10-01 17:32:00'));
// 2010-10-01T17:32:00+02:00
echo date(DATE_ATOM, strtotime("midnight", strtotime('2010-10-01 17:32:00')));
// 2010-10-01T00:00:00+02:00
If your widget doesnt return the time portion, I dont see any reason for setting the date to midnight, because it will be midnight automatically:
echo date(DATE_ATOM, strtotime('2010-10-01'));
// 2010-10-01T00:00:00+02:00
Note that all these are dates in the past and they will result in the given year in the past, not the current year like you say. If they do in your code, the cause must be somewhere else.
Will there be repercussions when you change the code? We cannot know. This is just one line of code and we have no idea of any context. Your unit-tests should tell you when something breaks when you change code.
EDIT after update
The codeblock you show makes no sense whatsoever. Ask the guy who wrote it what it is supposed to do. Not only will it falsely return the current year for past years, but it will also give incorrect results for the minutes, e.g.
24 March, 2010 17:30 will be 2012-03-24T17:00:30+01:00
I assume this was an attempt at turning 24 March, 2010 17:30 into a valid timestamp, which is in a format strtotime does not recognize. But the approach is broken. When you are on PHP5.3 use
$dt = DateTime::createFromFormat('d F, Y H:i', '24 March, 2010 17:30');
echo $dt->format(DATE_ATOM); // 2010-03-24T17:30:00+01:00
If you are not on 5.3 yet, go through https://stackoverflow.com/search?q=createFromFormat+php for alternate solutions. There is a couple in there.

Categories