Adjusting time zone in PHP with DateTime / DateTimeZone - php

There's a lot of info on doing time zone adjustments in PHP, but I haven't found an answer for specifically what I want to do due to all the noise.
Given a time in one timezone, I want to convert it to the time in another timezone.
This is essentially what I want to do, but I need to be able to do it using only the built-in PHP libs, not PEAR Date.
This is what I've been doing, but it seems to always give me the offset relative to GMT:
$los_angeles_time_zone = new DateTimeZone('America/Los_Angeles');
$hawaii_time_zone = new DateTimeZone('Pacific/Honolulu');
$date_time_los_angeles = new DateTime('2009-09-18 05:00:00', $los_angeles_time_zone);
printf("LA Time: %s<br/>", $date_time_los_angeles->format(DATE_ATOM));
$time_offset = $hawaii_time_zone->getOffset($date_time_los_angeles);
printf("Offset: %s<br/>", $time_offset);
This is the output:
LA Time: 2009-09-18T05:00:00-07:00
Offset: -36000
I was expecting 3 hours (10800 seconds). but the '-7:00' thing tells me it's keeping everything relative to GMT, which maybe explains why it's giving me the "absolute" offset.
How do I just get the offset between the two timezones without all of this GMT hoohah?
Thanks.
UPDATE:
I occured to me that I could do this and get what I want:
$date_time_los_angeles = new DateTime('2009-09-18 05:00:00', $los_angeles_time_zone);
printf("LA Time: %s<br/>", $date_time_los_angeles->format(DATE_ATOM));
$date_time_hawaii = new DateTime('2009-09-18 05:00:00', $hawaii_time_zone);
printf("Hawaii Time: %s<br/>", $date_time_hawaii->format(DATE_ATOM));
$time_offset = $los_angeles_time_zone->getOffset($date_time_los_angeles) - $hawaii_time_zone->getOffset($date_time_los_angeles);
printf("Offset: %s<br/>", $time_offset);
But it feels awkward to me. Anyone know a cleaner way to do it?

Here are a couple of functions using the DateTime classes. The first one will return the difference in seconds between two timezones. The second returns a "translation" of the time from one timezone to another.
function timezone_diff($tz_from, $tz_to, $time_str = 'now')
{
$dt = new DateTime($time_str, new DateTimeZone($tz_from));
$offset_from = $dt->getOffset();
$timestamp = $dt->getTimestamp();
$offset_to = $dt->setTimezone(new DateTimezone($tz_to))->setTimestamp($timestamp)->getOffset();
return $offset_to - $offset_from;
}
function time_translate($tz_from, $tz_to, $time_str = 'now', $format = 'Y-m-d H:i:s')
{
$dt = new DateTime($time_str, new DateTimezone($tz_from));
$timestamp = $dt->getTimestamp();
return $dt->setTimezone(new DateTimezone($tz_to))->setTimestamp($timestamp)->format($format);
}
Demo:
$los_angeles_time = '2009-09-18 05:00:00';
$los_angeles_tz = 'America/Los_Angeles';
$hawaii_tz = 'Pacific/Honolulu';
$los_angeles_hawaii_diff = timezone_diff($los_angeles_tz, $hawaii_tz, $los_angeles_time);
echo $los_angeles_hawaii_diff . '<br />';
$hawaii_time = time_translate($los_angeles_tz, $hawaii_tz, $los_angeles_time);
echo $hawaii_time . '<br />';

As GZipp commented, his code is really only for PHP >= 5.3.0. That is fine, but - here's a version that will work in PHP >= 5.2.0. (Incidentally, it also works in 5.3+, and with 2 less function calls)
<?php
function time_translate($tz_from, $tz_to, $time_str = 'now', $format = 'Y-m-d H:i:s')
{
$dt = new DateTime($time_str, new DateTimezone($tz_from));
$dt->setTimezone(new DateTimezone($tz_to));
return $dt->format($format);
}
$time_diffs = array('now', '-1 hour', '-1 day', '-1 week', '-1 month', '+1 hour', '+1 week', '+1 month');
foreach ($time_diffs as $diff)
{
echo "{$diff}:"
. "\n\t"
. "Current: " . date("Y-m-d H:i:s", strtotime($diff))
. "\n\t"
. "UTC: " . time_translate("US/Eastern", "UTC", $diff)
. "\n\n";
}

Simply minus the first time from the second.
That you'll be able to get the time difference in terms of seconds.

Related

How to find range diff 'datetime' and 'time'?

I want to find a range combining both data, that data has datetime and time data types, but datetime must ignore the time.
<?php
function test_duration($start_date, $end_date, $start_time, $end_time) {
$timeInterval = '-';
if(!empty($start_time) && !empty($end_time)) {
$timeStart = new DateTime($start_date->format('Y-m-d').' '.$start_time->format('H:i:s'));
$timeEnd = new DateTime($end_date->format('Y-m-d').' '.$end_time->format('H:i:s'));
$timeInterval = $timeStart->diff($timeEnd)->format('%H:%I:%s');
}
return $timeInterval;
}
$start_date = '2022-09-15 01:01:01';
$end_date = '2022-09-15 02:02:02';
$start_time = '14:48:40';
$end_time = '14:48:45';
echo test_duration($start_date, $end_date, $start_time, $end_time);
?>
so the formula is like this:
range start ==> $start_date (just date) + $start_time
range end ==> $end_date (just date) + $end_time
range start - range end
From the code above it should produce a duration of 5 seconds.
Do you have any solution to fix my code above?
The time can easily be removed from the date with strstr. Then the pure date can be combined with the new time. strtotime is well suited when only seconds are to be determined.
$start_date = '2022-09-15 01:01:01';
$end_date = '2022-09-15 02:02:02';
$start_time = '14:48:40';
$end_time = '14:48:45';
$strStart = strstr($start_date, ' ', true).' '.$start_time;
$strEnd = strstr($end_date, ' ', true).' '.$end_time;
$seconds = strtotime($strEnd) - strtotime($strStart); // int(5)
Time is time, date is date, you shouldn't mix them, so let's say
$start_date = '2022-09-15';
$start_time = '13:00:00';
$end_date = '2022-09-15';
$end_time = '14:00:00';
print strtotime($end_date) + strtotime($end_time) - strtotime($start_date) - strtotime($start_time);
You'll get 3600 seconds
If you know the date is in a fixed format can't you just explode the string on the central space like this?
<?php
function test_duration($start_date, $end_date, $start_time, $end_time) {
$timeInterval = '-';
if(!empty($start_time) && !empty($end_time)) {
$startDateOnly=explode(' ',$start_date)[0];
$endDateOnly=explode(' ', $end_date)[0];
$timeStart = date_create_from_format('Y-m-d H:i:s', $startDateOnly." ".$start_time);
$timeEnd = date_create_from_format('Y-m-d H:i:s', $endDateOnly." ".$end_time);
$timeInterval = $timeStart->diff($timeEnd)->format('%h:%i:%s');
}
return $timeInterval;
}
$start_date = '2022-09-15 01:01:01';
$end_date = '2022-09-15 02:02:02';
$start_time = '14:48:40';
$end_time = '14:48:45';
echo test_duration($start_date, $end_date, $start_time, $end_time);
?>
Your start is quite good, the use of DateTime class is one of the ways to solve your issue. The idea here can be illustrated as follows:
create a DateTime object from the starting date and then alter its time (hours, minutes and seconds) based on the starting time you supply.
do the same thing as the first step but for the ending date so we'll create a DateTime object from the ending date and then alter its time based on the ending time.
return the difference between the two dates in seconds:
to do so we will get the timestamps from both dates
make a simple subtraction of th two timestamps
return the result. We may return the absolute value here to always get a positive number for the case when the starting date is greater than the ending date (not required but that can be seen as an improvement).
Here's a live demo too
$startDate = '2022-09-15 01:01:01';
$endDate = '2022-09-15 02:02:02';
$startTime = '14:48:40';
$endTime = '14:48:45';
function diffInSeconds($startDate, $endDate, $startTime, $endTime)
{
// create a DateTime object based on $startingDate and then alter the time to use the $startingTime instead
$startDate = (new DateTime($startDate))->setTime(
($startTime = explode(':', $startTime))[0],
$startTime[1],
$startTime[2]
);
// create a DateTime object based on $endingDate and then alter the time to use the $endingTime instead
$endDate = (new DateTime($endDate))->setTime(
($endTime = explode(':', $endTime))[0],
$endTime[1],
$endTime[2]
);
// return the difference in seconds which will always be positive thanks to the "abs" function
return abs($endDate->getTimestamp() - $startDate->getTimestamp());
}
// run...
echo diffInSeconds($startDate, $endDate, $startTime, $endTime); // prints: 5
the above is code somehow primitive, it doesn't have any checks on whether the date/times are correct or not also it expects the times to be in the following format "HH:MM:SS".
Anyway, i strongly recommend looking at more modern utilities, especially the Carbon library which makes working with dates and times in PHP a piece of cake.
Learn more about DateTime objects on php.net.

NTUSER.dat - No of 100 ns intervals since 1.1.1601 UTC+0 using PHP

I am parsing UserAssist files from Windows registry NTUSER.dat. From the registry I get a field named "Last Executed" which has the explanation "Number of 100 ns intervals since 1.1.1601 UTC+0".
My decimal number is 131955686641390000, and I have to make this to a human readable format. I want to do this by using PHP.
I have figured out that there is a function called add(new DateInterval('XXXX')); that I think I can use, but I need some help to figure out what should be the input to the function. The manual is here; https://www.php.net/manual/en/class.dateinterval.php
This is my code:
$nanoseconds = "131955686641390000";
$date = new DateTime('1601-01-01');
for($i=0;$i<100;$i++) {
$date->add(new DateInterval('P1s'));
}
$date_print = $date->format('Y-m-d H:i:s');
echo"
<p><b>Date:</b> $date_print</p>
";
I tried this but it failed:
$nanoseconds = "131955686641390000";
$seconds = $nanoseconds/1000000000;
$add = "PT" . $seconds . "S";
$date = new DateTime('1601-01-01');
for($i=0;$i<100;$i++) {
$date->add(new DateInterval($add));
}
$date_print = $date->format('Y-m-d H:i:s');
echo"
<p><b>Date:</b> $date_print</p>
";
Fatal error: Uncaught Exception: DateInterval::__construct(): Unknown
or bad format (PT131955686.64139S) in
C:\Users\user\wamp64\www\x.php:257
DateInterval->__construct('PT131955686.641...') #1 {main} thrown in
Unix epoch time is the number of seconds since Jan 1st, 1970. It consists of ten digits.
Your number is in hundreds of nano seconds, so that would work out as 11 digits.
So you can strip off everything after the first 11 digits, and pass that into a DateTime starting in 1601, like so:
<?php
$nano = '131955686641390000';
$seconds = substr($nano, 0, 11);
$date = new DateTime('1601-01-01');
$date->modify('+' . $seconds . ' seconds');
echo $date->format('Y-m-d H:i:s');
Which gives 2019-02-25 11:44:24.
You can see this here https://3v4l.org/fYpSJ
Alternatively, you can subtract the number of seconds being the difference from Jan 1st 1970, to get the same result.
<?php
$nano = '131955686641390000';
$seconds = ((int) substr($nano, 0, 11)) -11644474772;
$date = new DateTime('#' . $seconds);
echo $date->format('Y-m-d H:i:s');
That's a little shorter and cleaner in my opinion. Looks like -11644474772 is a useful number to remember!

Simulate "do not disturb" functionality in PHP

I want to check between two user-specified times everyday and not run some function call (i.e. "Do Not Disturb").
For example, a user set a "Do Not Disturb" time block between 10:00pm to 6:00am (next day).
FYI, no days/dates are being specified by the end-user, ONLY times. This will run consistently everyday, 7 days a week.
So between 10pm-6am (next day), any function call is ignored. This is what I've written up so far:
$now = time(); // or $now = strtotime('11:00pm'); to simulate time to test
$start = strtotime('10:00pm');
$end = strtotime('6:00am +1 day');
// alternative time block
//$start = strtotime('10:00am');
//$end = strtotime('11:00am');
//debug
//echo date('r', $now) . '<br>' . date('r', $start) . '<br>' . date('r', $end) . '<br><br>';
if($start > $now || $now > $end) {
echo 'disturb';
} else {
echo 'do not disturb';
}
But this doesn't seem to work, because once you reach midnight, it's a new day, but the $end variable is already a day ahead.
I tried putting it a day behind, but then the issue is that the value of $end ends up being lower than the value of $start, which isn't correct.
I also tried adding a day to the $now variable whenever the time reaches midnight, but the issue w/ that is, what if the $start and $end times are within the same day?
What am I missing here?
Apparently you're trying to build some kind of calendar functionality here.
If you use strtotime('10:00pm'); this will change to the timestamp of the next day after midnight.
So you need to give the variable a date
$start = strtotime('2015-02-26 10:00pm');
$end = strtotime('2015-02-27 6:00am');
Not sure how you store these time blocks, but ideally they would be stored in a database table.
If it's every day the same you could do:
$now = time(); // or $now = strtotime('11:00pm'); to simulate time to test
$start = strtotime('10:00pm');
$end = strtotime('6:00am'); // without the +1 day
if($start > $end) {
if($start > $now && $now > $end) {
echo 'disturb';
} else {
echo 'do not disturb';
}
}else{
if($now < $start || $now > $end) {
echo 'disturb';
} else {
echo 'do not disturb';
}
}
That's a nice question actually,
You can use the the relatively new object oriented way of dealing with times.
I'll link you some info as I don't have time to write an entire example
http://php.net/manual/en/datetime.diff.php
http://php.net/manual/en/class.datetime.php
http://php.net/manual/en/class.dateinterval.php
specifically from the docs :
<?php
$datetime1 = new DateTime('2009-10-11');
$datetime2 = new DateTime('2009-10-13');
$interval = $datetime1->diff($datetime2);
echo $interval->format('%R%a days');
?>
Hope it helps
I would convert to DateTime() objects instead. Then you won't get any issues with days ending.
// obviously you'll need to feed in the date as well so
// that might involve some refactoring
$now = new DateTime();
$start = new DateTime('2015-02-26 10:00');
$end = new DateTime('2015-02-27 06:00');
Now you can compare as before.
If you don't know the date and your users are only specifying time, you might need to add the date dynamically. These are just for example.
Edit: to cope with unknown days, you could dynamically generate after grabbing today:
$today = new DateTime();
$start = new DateTime($today->format('Y-m-d') . ' 10:00');
$end = new DateTime($today->format('Y-m-d') . ' 06:00');
$end->add(new DateInterval('P1D'));

Timestamp wrong time

This is my problem
My default timezone is date_default_timezone_set('Europe/Madrid')
actual time 2012-12-06 17:00:38
start time 2012-12-10 16:30:00
unix timestamp actual time 1354809638
unix timestamp start time 1350052200
Start time is greater then actual time. But "unix timestamp actual time" is greater then "unix timestamp start time".
I wonder why?
RELEVANT CODE:
$to_unix_startTime = new DateTime($postField['startTime']);
$UnixStartTime = $to_unix_startTime->format('U');
$date = new DateTime();
$actualTime = $date->getTimestamp();
$start = new DateTime($postFields['startAuct']);
$start->format('Y-m-d H:i:s').PHP_EOL;
$start_tmstmp = date_timestamp_get($start);
$date = new DateTime();
$timestamp = $date->getTimestamp();
if ($timestamp > $start_tmstmp) {
echo $auctStatus = 1;
} elseif ($timestamp < $start) {
echo $auctStatus = 4;
}
$postField['startTime'] is in the wrong format. How do I know this? The timestamp you got from the start date, 1350052200, represents date 2012-10-12 (October 12th 2012) in stead of 2012-12-10 (December 10th 2012).
$postField['startTime'] is probably in the format 10/12/2012 (which will be parsed by DateTime as an American style date), when it should be formatted as 10-12-2012 (which will be parsed by DateTime as a European style date).
So, concluding, check the format of $postField['startTime'] and make sure it is a European style representation of a date.
[edit] Compare:
$european = new DateTime( '10-12-2012' );
$american = new DateTime( '10/12/2012' );
echo $european->format( 'Y-m-d' ) . ' (' . $european->getTimestamp() . ')' . PHP_EOL;
echo $american->format( 'Y-m-d' ) . ' (' . $american->getTimestamp() . ')' . PHP_EOL;
There is no need to format the datetime before getting the timestamp.
Check my code here: https://compilr.com/shadyyx/datetime/index.php
Should there be any problem with the link, here is the code without output:
<?php
date_default_timezone_set('Europe/Madrid');
$start = new DateTime('2012-12-10 16:30:00');
$actual = new DateTime('2012-12-06 17:00:38');
echo $start->format('d.m.Y H:i:s').PHP_EOL;
echo $actual->format('d.m.Y H:i:s').PHP_EOL;
echo PHP_EOL;
$start_tmstmp = $start->getTimestamp();
$act_tmstmp = $actual->getTimestamp();
echo $start_tmstmp.PHP_EOL;
echo $act_tmstmp.PHP_EOL;
echo PHP_EOL;
var_dump($start_tmstmp > $act_tmstmp);

Format date whose timestamp exceeds the int limit

So, we have the following code:
date("Y-m-d",time()+60*365*24*60*60);
The ideea is that I have to make a prognosis and I have the result in number of days which I have to add to the current date. The prognosis is for the year 2060 or past it...in an 64bit environment that works, but on 32bit not so much :)
any ideeas?
10x.
LE:
Ok so I've tried :
$date = new DateTime();
// for PHP 5.3
$date->add(new DateInterval('P20000D'));
// for PHP 5.2
$date->modify('+20000day');
echo $date->format('Y-m-d') . "\n";
and it works
this is working on my 32bit system:
$date = new DateTime("2071-05-26");
echo $date->format('Y-m-d H:i:s');
//i saw this in this question
See this:
http://www.infernodevelopment.com/forum/Thread-Solution-2038-PHP-Date-Bug-Y2-038K-UNIX-TIMESTAMP-BUG
<?php
// Specified date/time in your computer's time zone.
$date = new DateTime('9999-04-05');
echo $date->format('Y-M-j') ."";
// Specified date/time in the specified time zone.
$date = new DateTime('2040-09-08', new DateTimeZone('America/New_York'));
echo $date->format('n / j / Y') . "";
// INPUT UNIX TIMESTAMP as float or bigint from database
// Notice the result is in the UTC time zone.
$r = mysql_query("SELECT date FROM test_table");
$obj = mysql_fetch_object($r);
$date = new DateTime('#'.$obj->date); // a bigint(8) or FLOAT
echo $date->format('Y-m-d H:i: sP') ."";
// OR a constant greater than 2038:
$date = new DateTime('#2894354000'); // 2061-09-19
echo $date->format('Y-m-d H:i: sP') ."";
?>
If you are adding full years/days/months, I suppose you could use simple arithmetic on them individually and then use checkdate() (it claims to work up to year 32767) to validate the result
$date = array('Y' => date('Y'), 'm' => date('n'), 'd' => date('j'));
// +60 years
$date['Y'] += 60;
if (checkdate($date['m'], $date['d'], $date['Y'])) {
$fulldate = implode('-', $date);
}
<?php
// A dirty hack
function bigdate_to_string($t_64bit)
{
$t_base = strtotime('2038-01-01 00:00:00 +0000');
$t_32bit = $t_64bit - $t_base;
return date("Y", $t_32bit) + 68 . date("-m-d H:i:s", $t_32bit);
}
$t_64bit = 130 * 365 * 86400; // November 30, 2099 UTC
echo bigdate_to_string($t_64bit) . "\n";
?>
Output:
susam#swift:~$ php datehack.php
2099-11-30 05:30:00
I am subtracting 68 years from the time and adding it back again while printing the formatted output.

Categories