PHP - strtotime, specify timezone - php

I have a date string, say '2008-09-11'. I want to get a timestamp out of this, but I need to specify a timezone dynamically (rather then PHP default).
So to recap, I have two strings:
$dateStr = '2008-09-11';
$timezone = 'Americas/New_York';
How do I get the timestamp for this?
EDIT: The time of day will be the midnight of that day.... $dateStr = '2008-09-11 00:00:00';

$date = new DateTime($dateStr, new DateTimeZone($timezone));
$timestamp = $date->format('U');

The accepted answer is great if you're running PHP > 5.2 (I think that's the version they added the DateTime class). If you want to support an older version, you don't want to type as much, or if you just prefer the functional approach there is another way which also does not modify global settings:
$dateStr = '2008-09-11 00:00:00';
$timezone = 'America/New_York';
$dtUtcDate = strtotime($dateStr. ' '. $timezone);

This will work if for some reason you're using <5.2 (Heaven forbid).
$reset = date_default_timezone_get();
date_default_timezone_set('America/New_York');
$stamp = strtotime($dateStr);
date_default_timezone_set($reset);
But anything 5.2 and above, I'd strongly recommend you opt for #salathe's answer.

If you're going to use Timezones, I propose you use the DateTime class, and in this case the DateTime::createFromFormat() function which will allow you to do something like this:
$start = "2015-01-14 11:59:43";
$timezone = "America/Montreal";
$tz = new DateTimeZone($timezone);
$dt = DateTime::createFromFormat('Y-m-d H:i:s', $start, $tz);
When you put $tz in the DateTime::createFromFormat function, you tell it what time zone the date you gave is in, so that when you need to convert it to another timezone, all you have to do is something like this:
$start = $dt->setTimeZone(new DateTimeZone('UTC'));

Whenever you are referring to an exact moment in time, persist the time according to a unified standard that is not affected by daylight savings. (GMT and UTC are equivalent with this regard, but it is preferred to use the term UTC. Notice that UTC is also known as Zulu or Z time.)
If instead you choose to persist a time using a local time value, include the local time offset from UTC, such that the timestamp can later be interpreted unambiguously.
In some cases, you may need to store both the UTC time and the equivalent local time. Often this is done with two separate fields, but some platforms support a datetimeoffset type that can store both in a single field.
When storing timestamps as a numeric value, use Unix time - which is the number of whole seconds since 1970-01-01T00:00:00Z (excluding leap seconds). If you require higher precision, use milliseconds instead. This value should always be based on UTC, without any time zone adjustment.
If you might later need to modify the timestamp, include the original time zone ID so you can determine if the offset may have changed from the original value recorded.
When scheduling future events, usually local time is preferred instead of UTC, as it is common for the offset to change. See answer, and blog post.
Remember that time zone offsets are not always an integer number of hours (for example, Indian Standard Time is UTC+05:30, and Nepal uses UTC+05:45).
If using Java, use java.time for Java 8, or use Joda Time for Java 7 or lower.
If using .NET, consider using Noda Time.
If using .NET without Noda Time, consider that DateTimeOffset is often a better choice than DateTime.
If using Perl, use DateTime.
If using Python, use pytz or dateutil.
If using JavaScript, use moment.js with the moment-timezone extension.
If using PHP > 5.2, use the native time zones conversions provided by DateTime, and DateTimeZone classes. Be careful when using.
DateTimeZone::listAbbreviations() - see answer. To keep PHP with up to date Olson data, install periodically the timezonedb PECL package; see answer.
If using C++, be sure to use a library that uses the properly implements the IANA timezone database. These include cctz, ICU, and Howard Hinnant's "tz" library.
Do not use Boost for time zone conversions. While its API claims to support standard IANA (aka "zoneinfo") identifiers, it crudely maps them to fixed offsets without considering the rich history of changes each zone may have had.
(Also, the file has fallen out of maintenance.)
Most business rules use civil time, rather than UTC or GMT. Therefore, plan to convert UTC timestamps to a local time zone before applying application logic.
Remember that time zones and offsets are not fixed and may change. For instance, historically US and UK used the same dates to 'spring forward' and 'fall back'.
However, in 2007 the US changed the dates that the clocks get changed on. This now means that for 48 weeks of the year the difference between London time and New York time is 5 hours and for 4 weeks (3 in the spring, 1 in the autumn) it is 4 hours. Be aware of items like this in any calculations that involve multiple zones.
Consider the type of time (actual event time, broadcast time, relative time, historical time, recurring time) what elements (timestamp, time zone offset and time zone name) you need to store for correct retrieval - see "Types of Time" in answer.
Keep your OS, database and application tzdata files in sync, between themselves and the rest of the world.
On servers, set hardware clocks and OS clocks to UTC rather than a local time zone.
Regardless of the previous bullet point, server-side code, including web sites, should never expect the local time zone of the server to be anything in particular. see answer.
Use NTP services on all servers.
If using FAT32, remember that timestamps are stored in local time, not UTC.
When dealing with recurring events (weekly TV show, for example), remember that the time changes with DST and will be different across time zones.
Always query date-time values as lower-bound inclusive, upper-bound exclusive (>=, <).

Laconic Answer (no need to change default timezone)
$dateStr = '2008-09-11';
$timezone = 'America/New_York';
$time = strtotime(
$dateStr,
// convert timezone to offset seconds
(new \DateTimeZone($timezone))->getOffset(new \DateTime) - (new \DateTimeZone(date_default_timezone_get()))->getOffset(new \DateTime) . ' seconds'
);
Loquacious Answer
Use strtotime's second option which changes the frame of reference of the function. By the way I prefer not to update the default time zone of the script:
http://php.net/manual/en/function.strtotime.php
int strtotime ( string $time [, int $now = time() ] )
The function
expects to be given a string containing an English date format and
will try to parse that format into a Unix timestamp (the number of
seconds since January 1 1970 00:00:00 UTC), relative to the timestamp
given in now, or the current time if now is not supplied.
And a Helper
/**
* Returns the timestamp of the provided time string using a specific timezone as the reference
*
* #param string $str
* #param string $timezone
* #return int number of the seconds
*/
function strtotimetz($str, $timezone)
{
return strtotime(
$str, strtotime(
// convert timezone to offset seconds
(new \DateTimeZone($timezone))->getOffset(new \DateTime) - (new \DateTimeZone(date_default_timezone_get()))->getOffset(new \DateTime) . ' seconds'
)
);
}
var_export(
date(
'Y-m-d',
strtotimetz('this monday', 'America/New_York')
)
);
Maybe not the most performant approach, but works well when you know the default timezone and the offset. For example if the default timezone is UTC and the offset is -8 hours:
var_dump(
date(
'Y-m-d',
strtotime('this tuesday', strtotime(-8 . ' hours'))
)
);

Related

How to deal with time zones between Android and PHP/MySQL?

I am building backend queue system. My app's users need to automatically fetch data from server around 08:00:00 AM, individually for each time zone.
Every user needs to be assigned to a specific time a day. He can fetch data only at this time as the app uses API that has specific calls-per-minute limits.
How do I synchronize clients with server?
NOTE
I ran into specific problems, and solved it already. I am posting the solution right away as a complete answer that combines many answers I found on SO while solving it.
Core of the solution
For clarity use time values in UTC that is supported in each Java/PHP/MySQL, because:
Although GMT and UTC share the same current time in practice, there is a basic difference between the two:
GMT is a time zone officially used in some European and African countries. The time can be displayed using both the 24-hour format (0 - 24) or the 12-hour format (1 - 12 am/pm).
UTC is not a time zone, but a time standard that is the basis for civil time and time zones worldwide. This means that no country or territory officially uses UTC as a local time.
source
It gives you simple solution as once you use UTC, you only need to convert it to server's or clients' time zone for display purposes.
Managing client's time zone
You need to send client's time zone to backend to calculate what time do you want him to call API. You want to convert 08:00:00 local time to UTC, but here's a trick, because there are incompatible time zones' strings between Java and PHP.
// Java/Android
SimpleDateFormat sdf = new SimpleDateFormat("z");
I live in Poland, and using the code above I get 2 different values depending on seasons (CET for winter time and CEST for summer time).
// PHP
$tz1 = new DateTimeZone('CET');
$tz2 = new DateTimeZone('CEST');
The problem is that when I pass it to PHP, CET works perfectly as it's supported time zone string, but CEST is not.
To unify your code, you need to use:
// Java/Android
SimpleDateFormat sdf = new SimpleDateFormat("ZZZZ");
which gives you a time zone likethis:
GMT+01:00 // for CET
GMT+02:00 // for CEST
Remember that when you send it in URL like http://api.domain.com?timezone=GTM+02:00, you need to change + into %2B as timezone converted to GTM 02:00 won't work in PHP.
Calculating queue time for users
Once you get client's time zone, in PHP you convert 08:00:00 AM local time to UTC.
$tz = new DateTimeZone('GMT+02:00');
$dt = new DateTime('2017-03-30 08:00:00', $tz);
$dt->setTimezone(new DateTimeZone('UTC'));
echo $dt->format('H:i:s');
// echoes 06:00:00
Then you store calculated value in MySQL at type TIME column. You don't need to care about time zone in the database as TIME and DATE types are time zone independent.
Setting alarm at calculated UTC time
You get 06:00:00 as a response in the app, and you set AlarmManager using Calendar object like this:
// set UTC as a time zone
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(new Date());
long timeNow = cal.getTimeInMillis();
// set 06:00:00
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.HOUR_OF_DAY, 6);
// make sure to set alarm in future
long timeAlarm = cal.getTimeInMillis();
if (timeAlarm <= timeNow) {
cal.add(Calendar.HOUR_OF_DAY, 24);
}
alarm.setRepeating(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(),
24*60*60*1000, pintent);

PHP set TimeZone without changing date?

I send UNIX timestamp from javascript vat stamp = +new Date/1000 to PHP.
Then I do
//Here $d = '2015/04/03 00:00:00'
$d = new DateTime("#{$stamp}");
$d->setTimezone( new DateTimeZone( 'Pacific/Auckland' ) );
//Here $d = '2015/04/03 00:00:00' + 7:15 hrs ( 7:15 hrs is time diff between my browser & Auckland)
I want to change the timezone but keep the date to same. So, after I setTimezone to Pacific/Auckland, the date should still be '2015/04/03 00:00:00'.
Here's one way to do it.
$_date = new \DateTime($date->format('Y-m-d H:i:s'), new \DateTimeZone('<time zone>'));
I need to point out that you're asking for something nonsensical. A UNIX timestamp represents an absolute point in time. It does not represent "2015/04/03 00:00:00", because that date format can refer to a few dozen different points in time, depending on which timezone you're interpreting this string in. A UNIX timestamp doesn't have this problem, what point in time it represents is not negotiable based on timezones.
If you take an absolute point in time and want to format it as a human readable time which depends on timezones, then this human readable value will necessarily change by applying a different timezone to it. What you're asking for is to change the point in time the timestamp refers to, at which point it's just arbitrary.

PHP: right way to manage DateTime (timestamp Y2038 Bug aware!)

In my "tool box" i'm using this function:
function dataAttuale() {
$now = new DateTime();
$dataAttuale = $now->format(DateTime::ISO8601);
$offset = $now->getOffset();
date_default_timezone_set('UTC');
$nowUTC = new DateTime();
$dataUTC = $nowUTC->format(DateTime::ISO8601);
$orario = array();
$orario['dataAttuale'] = $dataAttuale;
$orario['dataUTC'] = $dataUTC;
$orario['offset'] = $offset;
return $orario;
}
I get this array
Array
(
[dataAttuale] => 2013-10-18T11:03:52+0200
[dataUTC] => 2013-10-18T09:03:52+0000
[offset] => 7200
)
So i could save in a datetime MySql field a datetime referred to UTC.
Now, i've some trouble about this.
1) I would save also offset (in seconds). What's best Mysql field? I think max seconds can be +14hour * 60 * 60 = 50400 and -12hours*60*60 = -43200
2) Do you think is notable save also offset? I.e., for example, several API services return a date in UTC + offset...
Thank you very much!
UPDATE:
Thank you to both people. Now i'm saving in MySQL datetime in UTC format and varchar timezone. With a couple of code I'm getting what I want:
$orario = new DateTime($value['creazione'], new DateTimeZone($value['timezone']));
$orario = $orario->format(DateTime::ISO8601);
The output is (for Europe/Rome)
2013-10-19T09:27:54+0200
And for America/Montreal
2013-10-19T09:29:16-0400
And for Australia/Melbourne
2013-10-19T09:30:31+1100
(difference of minutes//seconds it the time to change in my PHP scripts the default Timezone).
Now I think that:
1) I can laugh about Y2038 bug, abandoning (sigh :( ) timestamp :(
2) I can safely travel around the world and use my own Calendar (naaaa... i'll use forever Google Calendar, of course)
It doesn't make a lot of sense to save the offset. There are two possible values you can be interested in with a timestamp:
the general global timestamp, e.g. "the point in time in this world at which it was 12:52am on Sept. 6 2013 UTC"
the specific local time of some point in time, e.g. "17:34 on Dec. 19th 2012 in Manila, Philippines"
Notice that both of these are actually the same thing, they express a point in time in the notation of wall clock time and date at a specific location or timezone. The only difference is that UTC is a specified standard "location" relative to which other timezone offsets are expressed; but there's no reason Manila in the Philippines couldn't be used for the same purpose.
So when you want to store an absolute timestamp, you either:
decide that all your times are stored in a specific timezone like UTC and simply store that timestamp
decide that you are interested in a specific local time and store the timestamp and its timezone
Either way you need the timestamp and you need to know which timezone it's in. In 1. you decide in advance that all timestamps are in the same defined timezone and don't need to store it, in 2. you explicitly save that timezone information.
An offset is not a good thing to store, because it varies throughout the year. The offset in summer may be +6 hours to UTC, but in winter may be +7. If you need to do date calculations on a localized time later on, an offset is misleading and doesn't help you much. If you know the timezone you're talking about, you can get the offset for any time of the year later on.
MySQL doesn't support a DATETIME + TIMEZONE field (Postgres for example does), so you need to store the timezone (e.g. "Europe/Berlin") in a separate text field. If you don't need to associate a timestamp with a specific location at all, then there's no need for a timezone and you just need to store the normalized timestamp, e.g. normalized to UTC.
MySQL is award of timezones (it does not store the timezone with the date, but it converts it to a normalized format), so most of the time you do not need to have an additional field with the offset.
You just need to make sure that you set the correct time_zone for your connection.
So if you have a date and you want to store it in your database you have different possibilities:
You can use SET time_zone = timezone; for your connection. Way you tell MySQL that the date you send or receive from MySQL should be in the give timezone. MySQL will internally convert it to a normalized format.
If you want to insert dates that have different timezones then set for the time_zone then you could use CONVERT_TZ(dt,from_tz,to_tz). from_tz is the timezone of your date, to_tz the one that is set for your connection.
There are for sure situations where the timezone could matter. If that is true for your case is not exactly clear out of your question.

PHP in Windows, how to obtain the current system time that ignores the timezone?

I have the next problem. I have several PHP systems running in different Windows machine. Those systems use intensivelly the current time.
Right now, i configured php.ini and assigned it to a specific timezone. Also Windows is configure to the same timezone and everything works as expected.
However, in my country, the State decided to change the daylight saving time. So, sometimes, the admin changes the windows timezone and left unchanged the php timezone, creating a discordance in the system. Other times, is windows who changes automatically the timezone.
Is there are any way, from PHP, to obtain the current system time that ignores the timezone?.
update:
function time_zone_fix($timeGiven = "H:i:s")
{
$shell = new COM("WScript.Shell") or die("Requires Windows Scripting Host");
$time_bias = -($shell->RegRead("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\ActiveTimeBias")) / 60;
$timestamp_bias = 60 * 60 * $time_bias;
return gmdate($timeGiven, strtotime($timestamp_bias . " seconds"));
}
gives the current date and time no matter what timezone/dsl is specified.
However, creating a new COM inteface is anything but efficient.
gmdate('Y-m-d H:i:s');
changing the format to whatever you need. It returns the "timezone-less date-and-time". The gm stands for Greenwich Mean, as in GMT (Greenwich Mean Time). GMT has been superseded by UTC (Universal Time, Coordinated), so the function should really be called utcdate()...
But the most portable representation of time is the Unix timestamp, i.e., the number of seconds elapsed since 1970-01-01 00:00:00 UTC, ignoring leap seconds:
time();
The value of time() is also the default value for the second parameter of gmdate().
BTW, a leap second is scheduled for 2012-06-30 23:59:60
Note: In case you are asking for a way to get the time at a certain timezone but without the daylight saving time correction, you can do this:
gmdate('Y-m-d H:i:s', time() + 3600 * $h);
where $h is the offset in hours from UTC (a negative number in America), or, in case the offset is not a full number of hours:
gmdate('Y-m-d H:i:s', time() + 60 * $m);
where $m is the offset in minutes from UTC (e.g., 330 for IST, India Standard Time). A Unix timestamp with an added timezone offset doesn't make any sense by itself, though!
Last but not least, don't forget to synchronize your systems by means of NTP, the Network Time Protocol.
PHP Manual List of Supported Timezones:
... Here you'll find the complete list of timezones supported by PHP, which are meant to be used with e.g. date_default_timezone_set(). ...
And then this one: UTC (from this sub-page)
PHP in Windows, how to obtain the current system time that ignores the timezone?
I have the next problem. I have several PHP systems running in different Windows machine. Those systems use intensivelly the current time.

Timezone and Daylight Savings Issues

I've looked through the other solutions on SO and none of them seem to address the timezone/dst issue in the following regard.
I am making calls to NOAA Tide Prediction API and NOAA National Weather Service API which require a time range to be passed for retrieving data. For each location in my database, I have the timezone as a UTC offset and whether daylight savings time is observed (either 1 or 0). I'm trying to format some dates (todays and tomorrow) to be what the LST (Local Standard Time) would be in it's own timezone so I can pass to these API's.
I'm having trouble figuring out how to know if a date, such as todays, is within the daylight savings time range or not.
Here is what I have so far:
// Get name of timezone for tide station
// NOTE: $locationdata->timezone is something like "-5"
$tz_name = timezone_name_from_abbr("", $locationdata->timezone * 3600, false);
$dtz = new DateTimeZone($tz_name);
// Create time range
$start_time = new DateTime('', $dtz);
$end_time = new DateTime('', $dtz);
$end_time = $end_time->modify('+1 day');
// Modify time to match local timezone
$start_time->setTimezone($dtz);
$end_time->setTimezone($dtz);
// Adjust for daylight savings time
if( $locationdata->dst == '1' )
{
// DST is observed in this area.
// ** HOW DO I KNOW IF TODAY IS CURRENTLY DST OR NOT? **
}
// Make call to API using modified time range
...
How can I go about doing this? Thanks.
You can use PHP's time and date functions:
$tzObj = timezone_open($tz_name);
$dateObj = date_create("07.03.2012 10:10:10", $tzObj);
$dst_active = date_format($dateObj, "I");
If DST is active on the given date, $dst_active is 1, else 0.
Instead of specifying a time in the call to date_create you can also pass "now" to receive the value for the current date and time.
However, like Jon mentioned, different countries within the same timezone offset may observe DST while others may not.
For each location in my database, I have the timezone as a UTC offset and whether daylight savings time is observed (either 1 or 0).
That's not enough information. There can be multiple time zones which all have the same standard offset, all observe DST, but perform DST transitions at different times. (Indeed, historically they may also start and stop observing daylight saving time for several years.)
Basically, your database should contain a time zone ID, not the offset/DST-true-or-false. (Assuming PHP uses the zoneinfo time zone database, a time zone ID is something like "Europe/London".)
EDIT: To find the offset of a given DateTime, you can call getOffset, which you can then compare with the standard time offset. But unless you have the definitive time zone ID, you will be risking getting the wrong zone.
Cillosis,
I hope you are not working with Java! I am and fought with time all the time. I also work with weather data. Most of the data I use is in local standard time (ignoring daylight saving time). I also need to use times from other time zones and found that Java kept reading my computer's time zone. I also kept running into deprecated classes. I came up with a solution that works. It is a bit of a kluge, so I have it heavily documented and it only exists in one function. My solution is relative time. I have set the local time to UTC. I am subtracting the GMT offset instead of adding it. I don’t really care about the actual times, all I care about is the difference between two times. It is working very well.
Good luck

Categories