In time-zones observing Daylight Saving Time, the clock typically:
moves forward during transition from winter to summer
is set back during transition from summer to winter
For example, in the Europe/Paris time-zone, the UTC offset changes from +02:00 to +01:00 during the transition from summer to winter, at 3:00 AM on the last Sunday of the month of October.
In other words:
At 3:00 AM (+02:00) on 2014-10-26, clocks are set back to 2:00 AM (+01:00).
Which means that creating a DateTime for 2014-10-26 at 02:30 AM in the Europe/Paris time-zone is ambiguous, as it can represent either:
2014-10-26T02:30+01:00 (timestamp 1414287000)
2014-10-26T02:30+02:00 (timestamp 1414283400)
Java's ZonedDateTime documentation explains this problem very well, and their API offers a way to choose the preferred offset if needed.
In PHP however, it seems that this ambiguity is resolved by choosing arbitrarily the winter time:
$dt = new DateTime('2014-10-26T02:30', new DateTimeZone('Europe/Paris'));
echo $dt->format(DateTime::ISO8601); // 2014-10-26T02:30:00+0100
echo $dt->getTimestamp(); // 1414287000
echo $dt->getOffset(); // 3600
echo $dt->getTimeZone->getName(); // Europe/Paris
(By arbitrarily, I mean that I could not find any documentation about it).
Is there a way to choose the preferred offset when creating a DateTime from a date and time that fall within a DST overlap for the given time-zone?
Or in other words:
How can I create a DateTime object that would exhibit the following characteristics:
echo $dt->format(DateTime::ISO8601); // 2014-10-26T02:30:00+0200
echo $dt->getTimestamp(); // 1414283400
echo $dt->getOffset(); // 7200
echo $dt->getTimeZone->getName(); // Europe/Paris
That is, an object representing this date/time in the Europe/Paris time-zone in summer time?
First, consider that there is a known bug in PHP that will affect you here. Consider:
$dt = new DateTime('2014-10-26T02:30', new DateTimeZone('Europe/Paris'));
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")\n";
$dt->setTimeStamp($dt->getTimeStamp() - 3600);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")\n";
Output:
2014-10-26T02:30:00+0100 (1414287000)
2014-10-26T02:30:00+0100 (1414287000)
Even though you adjusted the timestamp back an hour to reflect summer time, PHP erroneously advanced it to the winter time position.
You can work around this for display purposes by using UTC as an intermediary.
$tz = new DateTimeZone('Europe/Paris');
$dt = new DateTime('2014-10-26T02:30', $tz);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")\n";
$ts = $dt->getTimeStamp() - 3600;
$dt = new DateTime("#$ts", new DateTimeZone('UTC'));
$dt->setTimeZone($tz);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")\n";
Output:
2014-10-26T02:30:00+0100 (1414287000)
2014-10-26T02:30:00+0200 (1414287000)
Note that even though the wrong timestamp is returned (it should be 1414283400), it does retain the desired summer-time offset of +0200.
Now, lets tackle the problem of knowing when to apply this. We'll examine the transitions and use that to decide whether or not to subtract an hour.
// set up the original input values
$tz = new DateTimeZone('Europe/Paris');
$dt = new DateTime('2014-10-26T02:30', $tz);
echo $dt->format(DateTime::ISO8601) . "\n";
// check for a transition +/- an hour from the current time stamp
$ts = $dt->getTimestamp();
$transitions = $tz->getTransitions($ts - 3600, $ts + 3600);
if (count($transitions) > 1) {
// see if we are moving backwards, creating the ambiguity
$shift = $transitions[1]['offset'] - $transitions[0]['offset'];
if ($shift < 0)
{
// apply the difference in offsets to move back to summer time
$ts = $ts + $shift;
$dt = new DateTime("#$ts", new DateTimeZone('UTC'));
$dt->setTimeZone($tz);
}
}
echo $dt->format(DateTime::ISO8601) . "\n";
Output:
2014-10-26T02:30:00+0100
2014-10-26T02:30:00+0200
You may also wish to read this related question and answer.
Related
I'm using the Bittrex REST API to find trades that occurred less than ten minutes ago:
https://bittrex.com/api/v1.1/public/getmarkethistory?market=BTC-ETH
Here are some of the trade timestamps that are returned from the endpoint:
2018-09-23T04:47:07.237
2018-09-23T04:47:02.797
Although today is actually 2018-09-22 where I live, it's showing these trades occurring in the future. Is the timezone different or something for these timestamps?
$now = date('m/d/y g:i a');
$now = strtotime($now);
$ten_minutes_ago = strtotime('-10 minutes');
foreach ($buy_sell_bittrex_orders['result'] as $buy_sell_bittrex_order) {
$buy_sell_bittrex_order_timestamp = $buy_sell_bittrex_order['TimeStamp'];
echo "Ten Minutes ago: " . $ten_minutes_ago . "<br>";
echo "Buy sell timestamp: " . $buy_sell_bittrex_order_timestamp . "<br>";
echo "Now: " . $now . "<br><br>";
if ( strtotime($buy_sell_bittrex_order_timestamp) >= $ten_minutes_ago && strtotime($buy_sell_bittrex_order_timestamp) <= $now ) {
// Do something
}
}
When I use the code above, none of the trades are found within 10 minutes ago, although there certainly must be. There can't have been trades that occurred in the future, so is there a timestamp issue here?
Here's an example of what the code above outputs:
Ten Minutes ago Bittrex: 1537679309
Buy sell timestamp: 1537704500
Now: 1537679940
In the example above, "Buy sell timestamp" is a higher value than "now" which represents the current day/time.
How can I convert the trade timestamps to match my current timezone? That seems to be the issue, though I may be wrong. Thanks!
You should provide the original Timezone and convert it to your Timezone.
See this Stackoverflow question and answer.
Convert time and date from one time zone to another in PHP
Good luck.
I'm having trouble calculating the number of hours worked.
We start with a time which starts as a string in this case ($time).
Then we change the time to 00:00:00 and store the result as a new variable ($newtime).
Then we need to calculate the difference between $time and $newtime but there is a formatting issue which I do not fully understand. Would anyone help?
$time = "2017-09-01 11:00:00"; //must start as a string like this
$newtime = new DateTime($time);
$newtime->setTime(00, 00,00); //change time to 00:00:00
$worktime = round((strtotime($time) - $newtime)/3600, 2);
echo "Hours Worked: " . $worktime . "<br>";
You're subtracting a timestamp with a DateTime object, so it tries to convert the DateTime object to an int, which it can't. You need to get the timestamp for the DateTime object, to subtract two ints:
<?php
$time = "2017-09-01 11:00:00"; //must start as a string like this
$newtime = new DateTime($time);
$newtime->setTime(00, 00,00); //change time to 00:00:00
$worktime = round((strtotime($time) - $newtime->getTimestamp())/3600, 2); // notice the $newtime->getTimestamp() call
echo "Hours Worked: " . $worktime . "<br>";
Demo
DateTime::getTimestamp() reference
You are mixing types (trying to cast object to int)... And maybe you didn't realize about the error you are making because you have disabled errors.
Please use, the method that Datetime class brings to you:
http://php.net/manual/es/datetime.gettimestamp.php
You can do it in both ways:
$newtime->getTimestamp()
or by using this:
date_timestamp_get($newtime)
As this:
$time = "2017-09-01 11:00:00"; //must start as a string like this
$newtime = new DateTime($time);
$newtime->setTime(00, 00,00); //change time to 00:00:00
$worktime = round((strtotime($time) - date_timestamp_get($newtime))/3600, 2);
echo "Hours Worked: " . $worktime . "<br>";
Please, be free of using this: http://sandbox.onlinephpfunctions.com/code/f78c993f709a67ac2770d78bb809e68e3a679707
I need to detect when a timestamp is in daylight saving or not.
I'm using this code to test the functionality:
<?php
date_default_timezone_set('Europe/Berlin');
$timestamp = $baseTimestamp = 1509234900; // Sunday, 29 October of 2017 1:55:00 GMT+02:00 DST
$date = (new DateTime)->setTimestamp($baseTimestamp);
echo "DateTime\t\t| Is in summer \t| Minutes passed\n";
for($i = 0; $i < 70; $i++) {
$date = (new DateTime)->setTimestamp($timestamp);
echo $date->format("Y-m-d H:i:s \t|I") . "\t\t| " . ($timestamp - $baseTimestamp)/60 . "\n";
$timestamp = $timestamp + 60;
}
https://3v4l.org/dqNlK
Working with Europe/Berlin, I've seen that in March, when at 2.00 we pass from winter to summer, php solves it right, but then in October, when it is supposed to come back from summer to winter at 3.00, it doesn't work as expected.
In this case, we have two timestamps corresponding to the same hour (at 3 is 2 again), but the timestamp is unique, so for 2.00 there must be one timestamp in the summer time and another one in the winter time.
Using hhvm, it shows the right value, but normal php interpreters show that is not in summer for both 2.00 (the first one, which is 2am and the second one which is when at 3am is 2am again. This is the one that should say is not in summer anymore)
Yes, there are several long-lived, unresolved, DST-related bugs in PHP.
You appear to have hit on this one:
https://bugs.php.net/bug.php?id=68549
function isSummer($timestamp) {
$date = (new DateTime)->setTimestamp($timestamp);
if ($date->format('I') == 1) {
return true;
}
return ($date->getTimestamp() > $timestamp);
}
I think this is going to do the trick
I have an application that needs to send a UTC timestamp in order for it to work correctly. In my application a user can have any number of timezones. So if they pick 3pm and their timezone is America/New_York, it is a different 3pm than if it was America/Chicago.
I need to figure out a way to change the date into the right UTC timestamp. I know I can use date_default_timezone_set("UTC")...but I don't think will work correctly.
I think I need to calculate a difference between UTC and regular timezone, but I am not sure. Any advice is welcomes.
date_default_timezone_set("UTC");
echo strtotime('5/13/2014 3:00 PM');
1399993200
date_default_timezone_set("America/New_York");
echo strtotime('5/13/2014 3:00 PM');
1400007600
As you can tell these 2 values are different.
EDIT: Here is what my code looks like. It doesn't seem to work correctly as the application doesn't show the event in the right time.
$previous_timezone = date_default_timezone_get();
date_default_timezone_set("UTC");
$aceroute_schedule = $this->sale_lib->get_send_to_aceroute_schedule();
if (($start_time = strtotime($aceroute_schedule['aceroute_schedule_date']. ' '.$aceroute_schedule['aceroute_schedule_time_start'])) !== FALSE)
{
//Append 000 as as string for 32 bit systems
$start_epoch = $start_time.'000';
$end_epoch = strtotime('+ '.$aceroute_schedule['aceroute_duration'].' minutes', $start_time).'000';
}
else //Default to current time + 1 hour
{
//Append 000 as as string for 32 bit systems
$start_epoch = time().'000';
$end_epoch = strtotime('+1 hour', time()).'000';
}
$event->start_epoch = $start_epoch;
$event->end_epoch = $end_epoch;
Update:
This will now create a DateTime object in the user's DateTimeZone ('America/New_York'). And then it will set that object's timezone to UTC. To get the timestamp (or other string representations of date), use ::format().
# Create NY date
$NY = new DateTimeZone("America/New_York");
$NYdate = new DateTime('5/13/2014 3:00 PM', $NY);
# Set timezone to UTC
$UTC = new DateTimeZone("UTC");
$UTCdate = $NYdate->setTimezone($UTC);
# Get timestamp (PHP 5.2 compatible)
$timezone = $UTCdate->format('U');
var_dump($timezone); // a string containing UNIX timestamp
First I create 2 DateTime objects based off of their respective DateTimeZone objects. Then we can either use OOP ::diff() to get another object containing information about the time difference. Or we can use simple integers representing the difference in seconds from ::getTimestamp.
$date = '5/13/2014 3:00 PM';
# Create NY date
$NY = new DateTimeZone("America/New_York");
$NYdate = new DateTime($date, $NY);
# Create UTC date
$UTC = new DateTimeZone("UTC");
$UTCdate = new DateTime($date, $UTC);
# Find difference object
$diff = $NYdate->diff($UTCdate);
var_dump($diff); // a DateInterval object containing time difference info
# Find difference in seconds
$diff = $NYdate->getTimestamp() - $UTCdate->getTimestamp();
var_dump($diff); // an integer containing time difference in seconds
Links:
DateTimeZone
DateTime
DateInterval
Example in http://www.php.net/manual/en/datetime.settimezone.php
$date = new DateTime('2000-01-01', new DateTimeZone('Pacific/Nauru'));
echo $date->format('Y-m-d H:i:sP') . "\n";
$date->setTimezone(new DateTimeZone('Pacific/Chatham'));
echo $date->format('Y-m-d H:i:sP') . "\n";
The first line creates a DateTIme object, using the timezone Pacific/Nauru.
You can then change the timezone using setTimezone as shown in line 4, and the output will be modified accordingly.
note: the default timezone (if you don't specify it in the 2nd parameter in line 1) is the one set in your php.ini file, which you can modify (at runtime) with date_default_timezone_set("America/New_York")
note2: the 1st parameter in line 1, is equivalent to the 1st parameter of the strtotime function.
note3: the format method takes the same format parameter as date (http://www.php.net/manual/en/function.date.php)
I'm writing a php script that iterates over the Monday of each week.
However the script seemed to get out of sync after 22nd of October.
<?php
$october_8th = strtotime("2012-10-08");
$one_week = 7 * 24 * 60 * 60;
$october_15th = $october_8th + $one_week;
$october_22nd = $october_15th + $one_week;
$october_29th = $october_22nd + $one_week;
$november_5th = $october_29th + $one_week;
echo date("Y-m-d -> l", $october_8th) . '<br />';
echo date("Y-m-d -> l", $october_15th) . '<br />';
echo date("Y-m-d -> l", $october_22nd) . '<br />';
echo date("Y-m-d -> l", $october_29th) . '<br />';
echo date("Y-m-d -> l", $november_5th) . '<br />';
This would output:
2012-10-08 -> Monday
2012-10-15 -> Monday
2012-10-22 -> Monday
2012-10-28 -> Sunday
2012-11-04 -> Sunday
I would expect it to say the 29th of October but it gets stuck at the 28th.
How should I get around this problem?
A preferred choice would be to use PHP's date-related classes to get the dates.
These classes importantly handle the daylight-savings boundaries for you, in a way that manually adding a given number of seconds to a Unix timestamp (the number from strtotime() that you used) cannot.
The following example takes your start dates and loops four times, each time adding a week to the date.
$start_date = new DateTime('2012-10-08');
$interval = new DateInterval('P1W');
$recurrences = 4;
foreach (new DatePeriod($start_date, $interval, $recurrences) as $date) {
echo $date->format('Y-m-d -> l') . '<br/>';
}
PHP Manual links:
The DatePeriod class
The DateInterval class
The DateTime class
While writing this question I discovered that day light saving time ends at the 28th of October.
Because the date at initialization doesn't contain a specific time automatically midnight is assigned. This however yields a problem when summertime ends. Suddenly the time isn't midnight anymore but one hour before that AND thus a day earlier then you would expect.
An easy fix would be to initialize the time to be midday instead of midnight:
$october_8th = strtotime("2012-10-08 12:00");
Perhaps there might be more elegant solution (you're welcome to leave one), but this will do for this purpose.