Why do PHP and MySQL unix timestamps diverge on 1983-10-29? - php

I've been using PHP's strtotime and MySQL's UNIX_TIMESTAMP functions in my app, to convert dates into timestamps. PHP and MySQL are running on my local machine, and these functions generally return the same result, as I expect them to:
$ php
<?php echo strtotime("2011-06-02"); ?>
1307001600
mysql> SELECT UNIX_TIMESTAMP("2011-06-02") ts;
+------------+
| ts |
+------------+
| 1307001600 |
+------------+
But, sorta by chance, I happened to notice that when I entered 1983-01-01 as the date, the results were no longer equal:
$ php
<?php echo strtotime("1983-01-01"); ?>
410263200
mysql> SELECT UNIX_TIMESTAMP("1983-01-01") ts;
+-----------+
| ts |
+-----------+
| 410256000 |
+-----------+
As you can see, PHP returned 410263200, while MySQL returned 410256000 - a difference of 7200 seconds.
This got me curious, and I wanted to know on what date the timestamps were no longer equivalent, so I wrote a little program that starts with today's date (in Y-m-d format), uses PHP's strtotime and MySQL's UNIX_TIMESTAMP and compares the results. It then subtracts 1 day from each value and loops until they're no longer equal.
The result:
1983-10-29
On October 29, 1983, for some reason, strtotime and UNIX_TIMESTAMP return values that differ by 7200 seconds.
Any ideas?
Thanks for reading.

You say you're on Alaskan time. From http://www.statoids.com/tus.html:
1983-10-30 02:00: All parts of Alaska except the Aleutians and Saint Lawrence Island switched to AT. Prior to the change, Alaska east of 138°W (Juneau) had been on PT; between 138°W and 141°W (Yakutat) had been on Yukon Time, which was UTC-9 with DST; west of 162°W (Nome) had been on Bering Time, which was UTC-11 with DST. Name of Alaska-Hawaii zone changed to Hawaii-Aleutian.
So, I'd guess that this is because your timezone changed by two hours (7200 seconds = 2 hours) on 30th October 1983.
I'd guess that MySQL's UNIX_TIMESTAMP is using a different timezone from your Alaskan setting in PHP, which may be either the server default, or dependent on your connection settings, so these diverge when you hit that date.
You may be able to glean more information by asking MySQL what its timezone setting is on that connection with SELECT ##global.time_zone, ##session.time_zone;; see this answer for more info on the output and how to interpret it.

Related

php unixtime, time() and timezones [duplicate]

I just want to check if time() returns a UTC/GMT timestamp or do I need to use date_default_timezone_set()?
time returns a UNIX timestamp, which is timezone independent. Since a UNIX timestamp denotes the seconds since 1970 UTC you could say it's UTC, but it really has no timezone.
To be really clear, a UNIX timestamp is the same value all over the world at any given time. At the time of writing it's 1296096875 in Tokyo, London and New York. To convert this into a "human readable" time, you need to specify which timezone you want to display it in. 1296096875 in Tokyo is 2011-01-27 11:54:35, in London it's 2011-01-27 02:54:35 and in New York it's 2011-01-26 21:54:35.
In effect you're usually dealing with (a mix of) these concepts when handling times:
absolute points in time, which I like to refer to as points in human history
local time, which I like to refer to as wall clock time
complete timestamps in any format which express an absolute point in human history
incomplete local wall clock time
Visualise time like this:
-------+-------------------+-------+--------+----------------+------>
| | | | |
Dinosaurs died Jesus born Y2K Mars colonised ???
(not to scale)
An absolute point on this line can be expressed as:
1296096875
Jan. 27 2011 02:54:35 Europe/London
Both formats express the same absolute point in time in different notations. The former is a simple counter which started roughly here:
start of UNIX epoch
|
-------+-------------------+------++--------+----------------+------>
| | | | |
Dinosaurs died Jesus born Y2K Mars colonised ???
The latter is a much more complicated but equally valid and expressive counter which started roughly here:
start of Gregorian calendar
|
-------+-------------------+-------+--------+----------------+------>
| | | | |
Dinosaurs died Jesus born Y2K Mars colonised ???
UNIX timestamps are simple. They're a counter which started at one specific point in time and which keeps increasing by 1 every second (for the official definition of what a second is). Imagine someone in London started a stopwatch at midnight Jan 1st 1970, which is still running. That's more or less what a UNIX timestamp is. Everybody uses the same value of that one stopwatch.
Human readable wall clock time is more complicated, and it's even more complicated by the fact that it's abbreviated and parts of it omitted in daily use. 02:54:35 means almost nothing on the timeline pictured above. Jan. 27 2011 02:54:35 is already a lot more specific, but could still mean a variety of different points on this line. "When the clock struck 02:54:35 on Jan. 27 2011 in London, Europe" is now finally an unambiguous absolute point on this line, because there's only one point in time at which this was true.
So, timezones are a "modifier" of "wall clock times" which are necessary to express a unique, absolute point in time using a calendar and hour/minute/second notation. Without a timezone a timestamp in such a format is ambiguous, because the clock struck 02:54:35 on Jan. 27 2011 in every country around the globe at different times.
A UNIX timestamp inherently does not have this problem.
To convert from a UNIX timestamp to a human readable wall clock time, you need to specify which timezone you'd like the time displayed in. To convert from wall clock time to a UNIX timestamp, you need to know which timezone that wall clock time is supposed to be in. You either have to include the timezone every single time with each such conversion, or you set the default timezone to be used with date_default_timezone_set.
Since PHP 5.1.0 (when the date/time functions were rewritten), every
call to a date/time function will generate a E_NOTICE if the timezone
isn't valid, and/or a E_WARNING message if using the system settings
or the TZ environment variable.
So in order to get a UTC timestamp you should check what the current timezone is and work off of that or just use:
$utc_str = gmdate("M d Y H:i:s", time());
$utc = strtotime($utc_str);
http://us3.php.net/time
"Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)."
So I believe the answer to your question is yes.
From the documentation
Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
One of the comments claimed: "both time() and strtotime(gmdate("M d Y H:i:s", time())) return the same result"
Since I wasn't sure about that, I ran a test:
$now = strtotime(gmdate("Y-m-d H:i:s", time()));
$now2 = time();
echo ' now='.$now.' now2='.$now2.' diff='.($now - $now2);
Output was:
now=1536824036 now2=1536806036 diff=18000
Diff is 18000 seconds = 5 hours = the timezone offset for the server running the test.

Converting CURDATE() to my local date with MySQL

I'm using Godaddy's MySQL database. Since their timezone is MST UTC -7, I needed to modify my code. I figured out how to do it when using NOW() function. However Im struggling while converting result of CURDATE() to my local date. Topics in the website didnt help it. I dont have privilege to change timezone of mysql since it is shared host. The problem about CURDATE() is, since there is 10 hours difference between server and my country, dates will be different at somepoint.
What I have tried so far
First attempt
SELECT convert_tz(CURDATE(),'-07:00','+03:00')
this query returns following output in the mysql.
convert_tz(CURDATE(),'-07:00','+03:00')
2016-05-14 10:00:00
I didnt try yet since still dates are the same but this code probably done the work. But the problem is about the time comes after date.CURDATE should return only date. I think it returns the differences between two timezones which equals to 10 hours but I think it is gonna cause problem when Im comparing 2 dates.
Second attempt
SELECT convert_tz(CURDATE(),'MST','EEST');
Since server's timezone is MST and my timezone is EEST, I tried in this way but it returns NULL.
The question is what should I do to just return date without that 10:00:00 there. or is there any better way?
There are a couple of problems with your approach.
First, CURDATE() returns a date, not a datetime.
Second, you need to convert the current date and time from the server's time zone to your time zone before truncating the time portion. This means using NOW() inside, not CURDATE().
Third, you need to use the correct abbreviations for the correct time zones for both sides of the conversion, for the entire year. CONVERT_TZ() will return NULL if either time zone is unrecognized.
In this case, MST/MDT is called "MST7MDT" and EET/EEST is called "EET" in the MySQL time zone tables. It's surprising that Go Daddy doesn't set their server clocks to UTC -- that's sort of a de facto standard for server clocks, but assuming not, "MST7MDT" is probably the most correct value.
mysql> SELECT DATE(CONVERT_TZ(NOW(),'MST7MDT','EET'));
+-----------------------------------------+
| DATE(CONVERT_TZ(NOW(),'MST7MDT','EET')) |
+-----------------------------------------+
| 2016-05-14 |
+-----------------------------------------+
1 row in set (0.00 sec)
Or, you could use the more intuitive localized names for the time zones. I believe these values would also be correct, an would accommodate summer and time changes correctly:
mysql> SELECT DATE(CONVERT_TZ(NOW(),'America/Denver','Europe/Bucharest'));
+-------------------------------------------------------------+
| DATE(CONVERT_TZ(NOW(),'America/Denver','Europe/Bucharest')) |
+-------------------------------------------------------------+
| 2016-05-14 |
+-------------------------------------------------------------+
1 row in set (0.00 sec)
Try to convert you date into string using CAST, and then get a substring.
SELECT SUBSTRING_INDEX( CAST(CONVERT_TZ(CURDATE(),'-07:00','+03:00') AS char), ':', -1)
Should return
2016-05-14 10:00

MySql UNIX_TIMESTAMP() stored as UTC with Daylight Savings approaching

My DB server (running MySql 5.5) is set to UTC, and dates are stored as Unix timestamps in the database using UNSIGNED INT. The database is primarily used for storing tasks which are run at a specific time (exec_time).
I insert tasks by creating a timestamp in PHP using the timezone of the user logged in (BST in this instance). For example, I have a task set to run at 1351396800 which is for tomorrow morning at 4am GMT.
I pluck tasks out of the database with the following query:
SELECT * FROM tasks WHERE exec_time <= UNIX_TIMESTAMP();
When the clocks roll back one hour tomorrow at 2am will this setup be ok?
Update: PHP is converting the dates fine. With PHP timezone set to Europe/Dublin (Currently BST) Two events added for 12 midnight and then 4am are stored as follows:
mysql> select exec_time, FROM_UNIXTIME(exec_time) from tasks order by id desc limit 2;
+-------------+----------------------------+
| exec_time | FROM_UNIXTIME(exec_time) |
+-------------+----------------------------+
| 1351378800 | 2012-10-27 23:00:00 |
| 1351396800 | 2012-10-28 04:00:00 |
tl;dr You should be fine as long as your exec_time column has a TIMESTAMP data type.
There isn't an explicit UNIX_TIMESTAMP column datatype. There is a TIMESTAMP column data type. Values for columns of this data type are converted automatically from your client connection's time zone to UTC (a/k/a Z or Zulu time, f/k/a Greenwich Mean Time) when being converted from a date/time string and from UTC to your client connection's time zone upon conversion to a date/time string.
So, if you're storing your exec_time column as a TIMESTAMP, you should be able to use the clause you propose:
WHERE exec_time <= UNIX_TIMESTAMP()
This will work because both your exec_time values and the result of the UNIX_TIMESTAMP() function call are handled in UTC on the server side. Your exec_time values will be stored in UTC.
If you're storing exec_time as an UNSIGNED INT or a similar numeric data type, you won't have been able to take advantage of the automatic conversion to UTC before storing.
You can mess with the display conversion behavior by setting your client connection time_zone as follows:
SET time_zone='SYSTEM' /* for your system's local time */
or
SET time_zone='+0:00' /* for UTC */
or
SET time_zone'America/New_York' /* or 'Europe/Vienna' or whatever */
Once you've issued one of these SET operations, do
SELECT exec_time, FROM_UNIXTIME(exec_time)
to get a sense of how your values are stored server side and translated.
If you want to see what will happen eight days on, try this:
SELECT 3600*24*8+exec_time, FROM_UNIXTIME(3600*24*8+exec_time)
http://dev.mysql.com/doc/refman/5.5/en//time-zone-support.html
In answer to your question, it depends how critical the time fields are, and whether the server's local time will change or not. If it's UTC then it probably won't change.
The temporal types in MySQL aren't timezone aware. You'll have to implement timezones yourself, perhaps by always storing a UTC timestamp/datetime and a separate timezone column which contains an interval offset from +12 to -12 hours for how much time to add or subtract to the UTC timestamp for the timezone.
The actual handling of what value to put in the timezone field and the work needed to retrieve a timestamp adjusted for the timezone are up to you, unfortunately.
If switching to Postgres is an option then you can always use the TIMESTAMP WITH TIMEZONE type that Postgres supplies.

Weird strtotime Conversion of MySQL timestamp

I have a MySQL field that gives me timestamp in the format: YYYY-MM-DD HH:MM:SS. The default is CURRENT_TIMESTAMP. Now, when I try to convert it to Unix timestamp, I get some weird results.
Actually, I am using this function to calculate relative time. It outputs negative integers even though MySQL timestamp is only a couple of seconds older. I am at a loss as to what's wrong.
I also found some weird issues. For example, in MySQL,
mysql> select from_unixtime(1289206455);
+---------------------------+
| from_unixtime(1289206455) |
+---------------------------+
| 2010-11-08 14:24:15 |
+---------------------------+
1 row in set (0.03 sec)
In PHP CLI:
php -r "echo date('Y-m-d h:m:s', 1289206455);"
2010-11-08 09:11:15
The supposed output (in both cases) should be: 2010-11-08 02:54:15 source.
So there seems to be a difference in times in PHP and MySQL. How can I sync them? I am on Ubuntu 11.04 XAMPP.
Thanks
You need to set the timezone.
date_default_timezone_set($zone);
http://php.net/manual/en/function.date-default-timezone-set.php
MySQL: http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_time_zone (#Idg thanks)

Timezone differences between PHP and MySQL

I've run in to an issue whereby PHP and MySQL seem to disagree on which timezone they should be using when converting certain timestamps back to date and time.
The problem arises when I have a timestamp which I would like to convert back to a datetime format. PHP gives the date and time based on GMT being the timezone, but MySQL seems to think it is operating in GMT+1, and converts accordingly.
Simple reproduction
$original_date = '2009-08-31 23:59:59';
$timestamp = strtotime($original_date);
echo $timestamp; // Gives 1251763199
echo date('Y-m-d H:i:s', $timestamp); // PHP: 2009-08-31 23:59:59
echo db_field('SELECT FROM_UNIXTIME(1251763199)'); // MySQL: 2009-09-01 00:59:59
The timestamp given by PHP seems to be the correct one for the date given, assuming timezone is GMT. The result from MySQL would have been the correct one had we been running BST (timestamp given fell within GMT+1 at that time).
If I try the above with a $timestamp from today (1267640942), PHP and MySQL both seem to be happy to tell me that it is 2010-03-03 18:29:02 (both returning GMT).
What timezone is set on the servers?
I've checked the MySQL docs, which say that if my timezone is set to system than the OS will provide the timezone info. This appears to be the case at the moment:
mysql> SELECT ##global.time_zone, ##session.time_zone;
+--------------------+---------------------+
| ##global.time_zone | ##session.time_zone |
+--------------------+---------------------+
| SYSTEM | SYSTEM |
+--------------------+---------------------+
The default timezone on my web server is GMT. This is also the default system timezone on my database server (according to the date command run on the cl on each server).
foo#bar:/home/foo$ date
Wed Mar 3 18:45:02 GMT 2010
So according to the docs, my DB server should be running on GMT, which is what we want. Yet the query output given in my test scripts suggests that it's running in GMT+1.
I know there are a number of workarounds for this problem (all date arithmetic being done fully in either PHP or MySQL, etc) but I'd love to get to the bottom of what's causing this discrepancy so we can sort it out and prevent anyone else on the team from being tripped up by it.
Does anyone know if there's a very basic setting that I've over-looked here, or know what could be causing this discrepancy?
Thanks!
i use this methodology:
take care of PHP and leave mysql alone
it is recommended to set default timezone for php via date_default_timezone_set php function.
use a TIMESTAMP field type for keeping date record in mySql
then when you insert:
$sDate = date("Y-m-d H:i:s");
mysql_query("insert into `table` set `created_on` = '$sDate' ");
and when you select:
mysql_query("select `created_on` from `table` ");
$iTime = strtotime($aRow['created_on']);
you can always have access to global time using gmdate php function:
$iTime_Global = gmdate("U", $iTime);
the mysql timezone would have no effect in your application if you just take care of your PHP code. (that is made by timezone set)
I would suggest using UTC on your server and MySQL install and convert the timezone(s) to what ever you want. The conversion in MySQL and PHP are fairly simple.
http://www.w3schools.com/php/php_ref_date.asp (getTimezone and setTimezone)
http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html

Categories