I'm working on a new project which has a lot of different data and it's all stored in database with same time zone timestamp, East coast, I make sure the server time zone is switched to it before anything is loaded via
date_default_timezone_set("America/New_York");
However I can't seem to wrap my head around it, I want to have a search function that allows a time zone selection, what if they select west coast time zone, should I only change the PHP part? Or should I query database differently? All the data is very time sensitive and it's proving very hard to wrap this around my head.
Any help or logic would be appreciated :P
The main problem is that MySQL's support for timezones is laughable. Most other databases let you define the timezone as part of the datetime column, but not MySQL. Instead, they force you to do a connection-wide or server-wide timezone setting.
Your best bet is making sure MySQL stores all datetimes as GMT / UTC. You can enforce this by running SET time_zone = 'UTC' on connect. Do it at the same time as you set the character set. You are setting the connection character set, right?
PHP-side, you can then use a combination of DateTime and DateTimeZone to take the datetimes from MySQL and display them in the user's timezone. For example, let's pretend that we get the date 2012-11-13 14:15:16 from a MySQL DATETIME column. From the PHP interactive prompt:
php > $from_mysql = '2012-11-13 14:15:16';
php > $utc = new DateTimeZone('UTC');
php > $ts = new DateTime($from_mysql, $utc);
php > $pdt = new DateTimeZone('America/Los_Angeles');
php > $ts_pdt = clone $ts;
php > $ts_pdt->setTimezone($pdt);
php > echo $ts_pdt->format('r'), "\n";
Tue, 13 Nov 2012 06:15:16 -0800
As demonstrated, you just need to create the DateTime by expressly telling it you're UTC, if UTC isn't the timezone you've set using date_default_timezone_set. Switching the timezone of a DateTime is as easy as giving it a new one. I've used clone here to work on a copy. DateTimes are mutable, and it's sometimes easy to find yourself accidentally clobbering it.
Reverse date math works the same way, just transform their selection into UTC and run the math on the native numbers.
tl;dr:
Store and perform calculations on datetimes in UTC (GMT) only
Display all datetimes in the user's timezone only
Related
I am using timestamp fields in my databases and my PHP software has its own time management system based on users timezone. I want to use timestamp fields for certain kind of data (created or modified when) and also be able to use te DEFAULT CURRENT_TIMESTAMP for the columns.
Is there any disadvantage setting the timezone to UTC using, SET time_zone = '+00:00', each time the session is created. I have four separate databases which the software uses and currently, I am setting the current timezone to UTC.
I don't want to use DATETIME as they are larger in size and also I won't be able to use DEFAULT CURRENT_TIMESTAMP as the timezone of the server might have an offset.
You should not use SET time_zone if your backend already uses all the logic into converting user's timezone correctly, because you're wasting resources unnecessarily. The UTC timezone should be into the metadata of the DB, where always the DB transactions will work with them.
By the way, TIMESTAMP columns always will be stored in UTC, so you don't need to setting that, unless your columns are datetime (not the case, i think).
When you insert a TIMESTAMP value, MySQL converts it from your
connection’s time zone to UTC for storage. When you query a TIMESTAMP
value, MySQL converts the UTC value back to your connection’s time
zone. Notice that this conversion does not occur for other temporal
data types such as DATETIME.
So you have two options:
Set the timezone in your transactions working with time in your sql;
Working with unix timezones into backend and only showing the correct converted time in the frontend to user.
I prefer the second one.
When dealing with date/time for entry date and/or modified date, it is better to use normal VARCHAR with the length of around 200 (or any other value that fits the full date) in order to store the full date and process your date/time in your PHP script. This gives you the flexibility to view your time based on the timezone defined in your PHP code. Click here to see available timezones in PHP.
You can also format the date/time in any possible format you want by simply using the date_format of PHP.
I have given a reference code below.
//This is the way you define your timezone in PHP code
date_default_timezone_set('Asia/Beirut');
//You can capture the date/time by using the below code. This will store "2017-05-28 23:55:34"
$date_time_registered = date('Y-m-d H:i:s');
//Retrieve the date/time and re-format it as you require. Below code will output "May", full month.
$retrieve_month_only = date_create($row['your_store_date_time']);
$retrieve_month_formatted = date_format($retrieve_month_only, 'F');
echo $retrieve_month_formatted;
You can refer to this link to find out about PHP date/time formatting.
I'm working on something where the user can select their own timezone and the software will be able to be used by others on their sites as well but I want to make sure that the timezone within the database is always set to UTC.
Now I know how you set the default timezone for PHP, such as:
date_default_timezone_set('Australia/Sydney');
...but I'm not sure how to make sure MySQL is using UTC? ...and even once you have made sure it is using UTC I guess you would have to convert your PHP dates/times into UTC before passing it to the database?
I guess I am wondering about many different date formats such as TIMESTAMP, DATETIME & even UNIX EPOCH integer timestamps which would simply be stored as a int datatype for example.
Then there is the whole retrieving dates/times from the DB and converting it to the respective timezone and lastly how does DST come into all of this?
I know there is a lot of similar questions out there, but I guess none really answered all my questions.
MySQL's data type timestamp stores the dates in UTC. For this to work properly, MySQL uses server's time zone and does the date conversion. It converts the date from servers's current time zone to UTC for storage. This implies that the database server should never change its time zone for this feature to work properly.
When you send the data to such a database, you send the UTC time as well. The easiest way to do this is to format a result of time() according to what MySQL wants (m-d-Y H:i:s).
In PHP, when you format the date for insertion to MySQL, it's the best to use DateTime class. It lets you offset the date with the time zone information, meaning that you don't have to use date_default_timezone_set function - that can lead to mistakes.
An example of DateTime in action:
$date = '1.12.2015 13:37:37'; // Format is day.month.year hour:minute:second
// We create DateTime from custom date format, for the person who resides in Australia/Sydney time zone
$dt = DateTime::createFromFormat('d.m.Y H:i:s', $date, new DateTimeZone('Australia/Sydney');
// Now we change the date's time zone into UTC, and we can insert it into MySQL
$dt->setTimeZone(new DateTimeZone('UTC'));
// This is the formatted date-string that can be safely inserted into MySQL
$date_string_for_mysql = $dt->format('m-d-Y H:i:s');
Alternatively, you can use int type in MySQL for timestamp storage and insert result of time() but this has a huge disadvantage of not being able to use date-related functions.
for current session of mysql you can try something like
SET time_zone = timezonename;
for more details you can also look into this answer https://dba.stackexchange.com/questions/20217/mysql-set-utc-time-as-default-timestamp
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.
I am trying to integrate a timezone system in my app, i've really tried hard on avoiding making timezone-aware apps upto now - but its a mandatory requirement now so got no choice. TimeZones it just goes over my head. I've read several topics on PHP.net and also other sites including but not limited to SO. But i never could get the hang of it.
So i was wondering if some one can help me out here :( What i'm looking to make is a preference option in my app to allow users to choose their own timezones from a select menu but the app should also be able to SET/Choose the DST accordingly itself for each user.
Please i'm sure this will help others who are still striving to get the hang of the timezones, so please provide as much detailed explanation as possible, even if you have to consider me a complete dumbo/noob.
Edit for bounty:
I am adding a bounty to this question because I really thing we need a good canonical question about time zones when writing PHP/MySQL apps (thus I'm also adding the MySQL tag). I have found things from many places, but it would be good to have it all together. Charles' answer is great, but I still feel it's lacking somewhat. Here are some things I thought of:
How to store the times in the database from a PHP DateTime object
Should they be stored in DATETIME or TIMESTAMP? What are the benefits or caveats for each?
Do we ever need to worry about time zones with MySQL DATE?
How to insert values using NOW(). Do these need to be converted somehow either before or after the insert?
Is it necessary to set the time zone used by MySQL? If so, how? Should it be done persistently or upon every HTTP request? Does it have to be set to UTC or can it be anything else? Or is the server's time sufficient?
How to retrieve values from MySQL and convert them to a DateTime object. Will putting it straight into DateTime::__construct() suffice or do we need to use DateTime::createFromFormat()?
When to convert to local time and why. Is there ever a time that we would want to convert it before it is echoed back to the user (e.g. to compare to another DateTime object or a static value)?
Is there ever a time we need to worry about Daylight Savings Time (DST)? Why or why not?
What should someone do if they have previously inserted data (e.g. using NOW()) without worrying about the time zone to make sure everything stays consistent?
Anything else you think of that someone should look out for
If possible, try to separate it into logical sections to make it easier for future users to find the information. Be sure to provide code examples where necessary.
This answer has been updated to accomodate the bounty. The original, unedited answer is below the line.
Almost all of the question points added by the bounty owner are in relation to how MySQL and PHP datetimes should interact, in the context of timezones.
MySQL still has pathetic timezone support, which means that the intelligence has to be PHP-side.
Set your MySQL connection timezone to UTC as documented in the link above. This will cause all datetimes handled by MySQL, including NOW(), to be handled sanely.
Always use DATETIME, never use TIMESTAMP unless you very expressly require the special behavior in a TIMESTAMP. This is less painful than it used to be.
It's ok to store the Unix epoch time as an integer if you have to, such as for legacy purposes. The epoch is UTC.
MySQL's preferred datetime format is created using the PHP date format string Y-m-d H:i:s
Convert all PHP datetimes to UTC when storing them in MySQL, which is a trivial thing as outlined below
Datetimes returned from MySQL can be handed safely to the PHP DateTime constructor. Be sure to pass in a UTC timezone as well!
Convert the PHP DateTime to the user's local timezone on echo, no sooner. Thankfully DateTime comparison and math against other DateTimes will take into account the timezone that each is in.
You're still up to the whims of the DST database provided with PHP. Keep your PHP and OS patches up to date! Keep MySQL in the blissful state of UTC to remove one potential DST annoyance.
That addresses most of the points.
The last thing is a doozy:
What should someone do if they have previously inserted data (e.g. using NOW()) without worrying about the time zone to make sure everything stays consistent?
This is a real annoyance. One of the other answers pointed out MySQL's CONVERT_TZ, though I'd personally have done it by hopping between server-native and UTC timezones during selects and updates, 'cause I'm hardcore like that.
the app should also be able to SET/Choose the DST accordingly itself for each user.
You don't need to and should not do this in the modern era.
Modern versions of PHP have the DateTimeZone class, which includes the ability to list named timezones. Named timezones allow the user to select their actual location, and have the system automatically determine their DST rules based on that location.
You can combine DateTimeZone with DateTime for some simple but powerful functionality. You can simply store and use all of your timestamps in UTC by default, and convert them to the user's timezone on display.
// UTC default
date_default_timezone_set('UTC');
// Note the lack of time zone specified with this timestamp.
$nowish = new DateTime('2011-04-23 21:44:00');
echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 21:44:00
// Let's pretend we're on the US west coast.
// This will be PDT right now, UTC-7
$la = new DateTimeZone('America/Los_Angeles');
// Update the DateTime's timezone...
$nowish->setTimeZone($la);
// and show the result
echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 14:44:00
By using this technique, the system will automatically select the correct DST settings for the user, without asking the user whether or not they're currently in DST.
You can use a similar method to render the select menu. You can continually reassign the time zone for the single DateTime object. For example, this code will list the zones and their current times, at this moment:
$dt = new DateTime('now', new DateTimeZone('UTC'));
foreach(DateTimeZone::listIdentifiers() as $tz) {
$dt->setTimeZone(new DateTimeZone($tz));
echo $tz, ': ', $dt->format('Y-m-d H:i:s'), "\n";
}
You can greatly simplify the selection process by using some client-side magic. Javascript has a spotty but functional Date class, with a standard method to get the UTC offset in minutes. You can use this to help narrow down the list of likely timezones, under the blind assumption that the user's clock is right.
Let's compare this method to doing it yourself. You'd need to actually perform date math every single time you manipulate a datetime, in addition to pushing a choice off on the user that they aren't going to really care about. This isn't just sub-optimal, it's bat-guano insane. Forcing users to signify when they want DST support is asking for trouble and confusion.
Further, if you wanted to use the modern PHP DateTime and DateTimeZone framework for this, you'd need to use deprecated Etc/GMT... timezone strings instead of named timezones. These zone names may be removed from future PHP versions, so it'd be unwise to do that. I say all of this from experience.
tl;dr: Use the modern toolset, spare yourself the horrors of date math. Present the user with a list of named time zones. Store your dates in UTC, which won't be impacted by DST in any way. Convert datetimes to the user's selected named time zone on display, not earlier.
As requested, here's a loop over the available time zones displaying their GMT offset in minutes. I selected minutes here to demonstrate an unfortunate fact: not all offsets are in whole hours! Some actually switch half an hour ahead during DST instead of a whole hour. The resulting offset in minutes should match that of Javascript's Date.getTimezoneOffset.
$utc = new DateTimeZone('UTC');
$dt = new DateTime('now', $utc);
foreach(DateTimeZone::listIdentifiers() as $tz) {
$local = new DateTimeZone($tz);
$dt->setTimeZone($local);
$offset = $local->getOffset($dt); // Yeah, really.
echo $tz, ': ',
$dt->format('Y-m-d H:i:s'),
', offset = ',
($offset / 60),
" minutes\n";
}
How to store the times in the database from a PHP DateTime object
The SQL-92 standard specified that temporal literals should be passed in SQL using a suitable data-type keyword (e.g. TIMESTAMP for date/time values) followed by a string representation of the value (containing an optional timezone offset if non-default).
Sadly, MySQL is not compliant with this part of the SQL standard. As documented under Date and Time Literals:
Standard SQL permits temporal literals to be specified using a type keyword and a string.
[ deletia ]
MySQL recognizes those constructions and also the corresponding ODBC syntax:
[ deletia ]
However, MySQL ignores the type keyword and each of the preceding constructions produces the string value 'str', with a type of VARCHAR.
The documentation goes on to describe the literal formats which MySQL supports and, notably, explicit timezone offsets are absent. There is a feature request to fix this that is now over seven years old and which does not look likely to be introduced any time soon.
Instead, one must set the session's time_zone variable prior to exchanging date/time values between server and client. Therefore, using PDO:
Connect to MySQL:
$dbh = new PDO("mysql:dbname=$dbname", $username, $password);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
Set the session time_zone to that of the DateTime object:
$qry = $dbh->prepare('SET SESSION time_zone = ?');
$qry->execute([$datetime->format('P')]);
Produce a suitable literal from the DateTime object and pass to MySQL as normal (i.e. as a parameter to a prepared statement).
As described in the documentation, there are a number of possible literal formats that one can use. However, I'd suggest using a string in 'YYYY-MM-DD hh:mm:ss.ffffff' format (note that fractional seconds will be ignored in versions of MySQL prior to 5.6), as it is the closest to the SQL standard; indeed one could prefix the literal with the TIMESTAMP keyword to ensure that one's SQL is portable:
$qry = $dbh->prepare('
UPDATE my_table
SET the_time = TIMESTAMP ?
WHERE ...
');
$qry->execute([$datetime->format('Y-m-d H:i:s.u')]);
Should they be stored in DATETIME or TIMESTAMP? What are the benefits or caveats for each?
PHP DateTime objects should always be stored in TIMESTAMP type columns.
The most fundamental difference is that TIMESTAMP stores timezone information (by storing the value in UTC and converting to/from as required by the time_zone variable above), whereas DATETIME does not. Thus TIMESTAMP is useful for representing a specific moment in time (analogous to PHP DateTime objects), whereas DATETIME is useful for representing the time that is seen on a calendar/clock (as in a photo).
As documented under The DATE, DATETIME, and TIMESTAMP Types:
The DATETIME type is used for values that contain both date and time parts. MySQL retrieves and displays DATETIME values in 'YYYY-MM-DD HH:MM:SS' format. The supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'.
The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC.
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME.) By default, the current time zone for each connection is the server's time. The time zone can be set on a per-connection basis. As long as the time zone setting remains constant, you get back the same value you store. If you store a TIMESTAMP value, and then change the time zone and retrieve the value, the retrieved value is different from the value you stored. This occurs because the same time zone was not used for conversion in both directions. The current time zone is available as the value of the time_zone system variable. For more information, see Section 10.6, “MySQL Server Time Zone Support”.
The TIMESTAMP data type offers automatic initialization and updating to the current date and time. For more information, see Section 11.3.5, “Automatic Initialization and Updating for TIMESTAMP”.
Note the final paragraph, which often catches out newcomers to MySQL.
It may also be worth adding that, as documented under Data Type Storage Requirements, DATETIME values require 8 bytes for storage whereas TIMESTAMP values only require 4 bytes (the underlying data storage format can be found in Date and Time Data Type Representation).
Do we ever need to worry about time zones with MySQL DATE?
It is only meaningful for a time to be sensitive to timezone. By definition, a date alone is universally the same irrespective of one's timezone and therefore there is no need to "worry about time zones" when using MySQL's DATE data type.
The corollary to this is that, if one has a value that is sensitive to timezone, one must also store its time e.g. in a TIMESTAMP column: using a DATE column causes irreversible loss of significant information.
How to insert values using NOW(). Do these need to be converted somehow either before or after the insert?
As documented under NOW():
Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a string or numeric context. The value is expressed in the current time zone.
Since "the value is expressed in the current time zone" and that same "current time zone" will be used in evaluating date/time values, one does not have to worry about time zone when using MySQL's NOW() function (or any of its aliases). Therefore, to insert a record:
INSERT INTO my_table (the_time) VALUES (NOW());
Note that, as mentioned above, MySQL's automatic initialisation of TIMESTAMP columns makes redundant most attempts to use NOW() during record insertion/update.
Is it necessary to set the time zone used by MySQL? If so, how? Should it be done persistently or upon every HTTP request? Does it have to be set to UTC or can it be anything else? Or is the server's time sufficient?
This is already addressed above. One can set MySQL's time_zone variable globally, if so desired and thus avoid having to set it upon every connection. See MySQL Server Time Zone Support for more information.
How to retrieve values from MySQL and convert them to a DateTime object. Will putting it straight into DateTime::__construct() suffice or do we need to use DateTime::createFromFormat()?
As documented under Compound Formats, one of the date/time formats recognised by the parser that PHP uses in DateTime::__construct() is MySQL's output format.
However, since the MySQL output format does not include the timezone, one must be sure to furnish the DateTime constructor with that information through its optional second argument:
$qry = $dbh->prepare('SET SESSION time_zone = ?');
$qry->execute([$timezone->getName()]);
$qry = $dbh->query('SELECT the_time FROM my_table');
$datetime = new DateTime($qry->fetchColumn(), $timezone);
Alternatively, one can have MySQL convert the time to a UNIX timestamp and construct the DateTime object from that:
$qry = $dbh->query('SELECT UNIX_TIMESTAMP(the_time) FROM my_table');
$datetime = new DateTime($qry->fetchColumn());
When to convert to local time and why. Is there ever a time that we would want to convert it before it is echoed back to the user (e.g. to compare to another DateTime object or a static value)?
I'm not sure what you mean by "local time" (local to whom? the RDBMS? the webserver? the webclient?), but comparisons between DateTime objects will handle timezone conversions as necessary (PHP stores the values internally in UTC and only converts for output).
Is there ever a time we need to worry about Daylight Savings Time (DST)? Why or why not?
Generally speaking, if you follow the methodology given above, the only concern for DST is when ensuring that values are rendered to the user in the timezone that they expect.
What should someone do if they have previously inserted data (e.g. using NOW()) without worrying about the time zone to make sure everything stays consistent?
As mentioned above, use of NOW() should never cause problems.
If literal values have been inserted into a TIMESTAMP column whilst the session's time_zone variable was set to an incorrect value, one will need to update those values accordingly. MySQL's CONVERT_TZ() function may prove helpful:
UPDATE my_table SET the_time = CONVERT_TZ(the_time, '+00:00', '+10:00');
How to store the times in the database from a PHP DateTime object
Should they be stored in DATETIME or TIMESTAMP? What are the benefits
or caveats for each?
* UPDATE, clarify my first paragraph*
You can also store a timestamp as an INT. The advantage is that you know in which timezone you have stored your value since timestamp is the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
see php doc: http://php.net/manual/en/function.time.php
Using a 64 bits operating system, you should not have to worries about the year 2038 issue:
http://en.wikipedia.org/wiki/Year_2038_problem
Timestamp are a lot easier to use to compare date times and more fun to use in objet and array. You could easily use them as keys for your arrays for example.
Do we ever need to worry about time zones with MySQL DATE?
In MySQL, the CURRENT_TIMESTAMP(), CURRENT_TIME(), CURRENT_DATE(), and FROM_UNIXTIME() functions return values in the connection's current time zone, which is available as the value of the time_zone system variable. In addition, UNIX_TIMESTAMP() assumes that its argument is a datetime value in the current time zone.
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html
How to insert values using NOW(). Do these need to be converted
somehow either before or after the insert?
If you use timestamp, you can rely on PHP function, it is just an integer.
If you use date time, the function curdate allows you to have the current date.
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_curdate
Is it necessary to set the time zone used by MySQL? If so, how? Should
it be done persistently or upon every HTTP request? Does it have to be
set to UTC or can it be anything else? Or is the server's time
sufficient?
see http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html
How to retrieve values from MySQL and convert them to a DateTime
object. Will putting it straight into DateTime::__construct() suffice
or do we need to use DateTime::createFromFormat()?
Again, if you use timestamp, it is easier. You know the timestamp timezone, you know the
When to convert to local time and why. DST is easy to manage with timestamp, see functions around timestamp :
http://php.net/manual/en/function.mktime.php
Is there ever a time that we would want to convert it before it is
echoed back to the user (e.g. to compare to another DateTime object or
a static value)?
I think again, timestamp let you works with your date to compare them, extract whatever you need and print what you want.
Is there ever a time we need to worry about Daylight Savings Time
(DST)? Why or why not? What should someone do if they have previously
inserted data (e.g. using NOW()) without worrying about the time zone
to make sure everything stays consistent?
Yes, you should worrie about if you have to create appointment or meeting in an application. I developed two applications, one for clinical appointment and one for workshops appointment that support more than 70000 accounts and huge amounts of record. I stick with timestamp, it is super eady to index, manipulate, compare. The print part comes only on the view.
There are advantages to use datetime in your database. If you have to analyse data from the table in sql direct, it is a lot easier to read, it is more 'human readable'.
I am not sure there will be a fixed answer for this post, since it depends on your needs. Timestamp are very easy to manipulate for the operations (a pragmatical approach). The way you store it depends on your preference, since you can store a date and still convert it to timestamp later. But the timezone is part of the timestamps definition from what I understand.
I'm using the America/New York timezone. In the Fall we "fall back" an hour -- effectively "gaining" one hour at 2am. At the transition point the following happens:
it's 01:59:00 -04:00
then 1 minute later it becomes:
01:00:00 -05:00
So if you simply say "1:30am" it's ambiguous as to whether or not you're referring to the first time 1:30 rolls around or the second. I'm trying to save scheduling data to a MySQL database and can't determine how to save the times properly.
Here's the problem:
"2009-11-01 00:30:00" is stored internally as 2009-11-01 00:30:00 -04:00
"2009-11-01 01:30:00" is stored internally as 2009-11-01 01:30:00 -05:00
This is fine and fairly expected. But how do I save anything to 01:30:00 -04:00? The documentation does not show any support for specifying the offset and, accordingly, when I've tried specifying the offset it's been duly ignored.
The only solutions I've thought of involve setting the server to a timezone that doesn't use daylight savings time and doing the necessary transformations in my scripts (I'm using PHP for this). But that doesn't seem like it should be necessary.
Many thanks for any suggestions.
I've got it figured out for my purposes. I'll summarize what I learned (sorry, these notes are verbose; they're as much for my future referral as anything else).
Contrary to what I said in one of my previous comments, DATETIME and TIMESTAMP fields do behave differently. TIMESTAMP fields (as the docs indicate) take whatever you send them in "YYYY-MM-DD hh:mm:ss" format and convert it from your current timezone to UTC time. The reverse happens transparently whenever you retrieve the data. DATETIME fields do not make this conversion. They take whatever you send them and just store it directly.
Neither the DATETIME nor the TIMESTAMP field types can accurately store data in a timezone that observes DST. If you store "2009-11-01 01:30:00" the fields have no way to distinguish which version of 1:30am you wanted -- the -04:00 or -05:00 version.
Ok, so we must store our data in a non DST timezone (such as UTC). TIMESTAMP fields are unable to handle this data accurately for reasons I'll explain: if your system is set to a DST timezone then what you put into TIMESTAMP may not be what you get back out. Even if you send it data that you've already converted to UTC, it will still assume the data's in your local timezone and do yet another conversion to UTC. This TIMESTAMP-enforced local-to-UTC-back-to-local roundtrip is lossy when your local timezone observes DST (since "2009-11-01 01:30:00" maps to 2 different possible times).
With DATETIME you can store your data in any timezone you want and be confident that you'll get back whatever you send it (you don't get forced into the lossy roundtrip conversions that TIMESTAMP fields foist on you). So the solution is to use a DATETIME field and before saving to the field convert from your system time zone into whatever non-DST zone you want to save it in (I think UTC is probably the best option). This allows you to build the conversion logic into your scripting language so that you can explicitly save the UTC equivalent of "2009-11-01 01:30:00 -04:00" or ""2009-11-01 01:30:00 -05:00".
Another important thing to note is that MySQL's date/time math functions don't work properly around DST boundaries if you store your dates in a DST TZ. So all the more reason to save in UTC.
In a nutshell I now do this:
When retrieving the data from the database:
Explicitly interpret the data from the database as UTC outside of MySQL in order to get an accurate Unix timestamp. I use PHP's strtotime() function or its DateTime class for this. It can not be reliably done inside of MySQL using MySQL's CONVERT_TZ() or UNIX_TIMESTAMP() functions because CONVERT_TZ will only output a 'YYYY-MM-DD hh:mm:ss' value which suffers from ambiguity problems, and UNIX_TIMESTAMP() assumes its input is in the system timezone, not the timezone the data was ACTUALLY stored in (UTC).
When storing the data to the database:
Convert your date to the precise UTC time that you desire outside of MySQL. For example: with PHP's DateTime class you can specify "2009-11-01 1:30:00 EST" distinctly from "2009-11-01 1:30:00 EDT", then convert it to UTC and save the correct UTC time to your DATETIME field.
Phew. Thanks so much for everyone's input and help. Hopefully this saves someone else some headaches down the road.
BTW, I am seeing this on MySQL 5.0.22 and 5.0.27
MySQL's date types are, frankly, broken and cannot store all times correctly unless your system is set to a constant offset timezone, like UTC or GMT-5. (I'm using MySQL 5.0.45)
This is because you can't store any time during the hour before Daylight Saving Time ends. No matter how you input dates, every date function will treat these times as if they are during the hour after the switch.
My system's timezone is America/New_York. Let's try storing 1257051600 (Sun, 01 Nov 2009 06:00:00 +0100).
Here's using the proprietary INTERVAL syntax:
SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3599 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3600 SECOND); # 1257055200
SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 1 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 0 SECOND); # 1257055200
Even FROM_UNIXTIME() won't return the accurate time.
SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051599)); # 1257051599
SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051600)); # 1257055200
Oddly enough, DATETIME will still store and return (in string form only!) times within the "lost" hour when DST starts (e.g. 2009-03-08 02:59:59). But using these dates in any MySQL function is risky:
SELECT UNIX_TIMESTAMP('2009-03-08 01:59:59'); # 1236495599
SELECT UNIX_TIMESTAMP('2009-03-08 02:00:00'); # 1236495600
# ...
SELECT UNIX_TIMESTAMP('2009-03-08 02:59:59'); # 1236495600
SELECT UNIX_TIMESTAMP('2009-03-08 03:00:00'); # 1236495600
The takeaway: If you need to store and retrieve every time in the year, you have a few undesirable options:
Set system timezone to GMT + some constant offset. E.g. UTC
Store dates as INTs (as Aaron discovered, TIMESTAMP isn't even reliable)
Pretend the DATETIME type has some constant offset timezone. E.g. If you're in America/New_York, convert your date to GMT-5 outside of MySQL, then store as a DATETIME (this turns out to be essential: see Aaron's answer). Then you must take great care using MySQL's date/time functions, because some assume your values are of the system timezone, others (esp. time arithmetic functions) are "timezone agnostic" (they may behave as if the times are UTC).
Aaron and I suspect that auto-generating TIMESTAMP columns are also broken. Both 2009-11-01 01:30 -0400 and 2009-11-01 01:30 -0500 will be stored as the ambiguous 2009-11-01 01:30.
I think micahwittman's link has the best practical solution to these MySQL limitations: Set the session timezone to UTC when you connect:
SET SESSION time_zone = '+0:00'
Then you just send it Unix timestamps and everything should be fine.
But how do I save anything to 01:30:00
-04:00?
You can convert to UTC like:
SELECT CONVERT_TZ('2009-11-29 01:30:00','-04:00','+00:00');
Even better, save the dates as a TIMESTAMP field. That's always stored in UTC, and UTC doesn't know about summer/winter time.
You can convert from UTC to localtime using CONVERT_TZ:
SELECT CONVERT_TZ(UTC_TIMESTAMP(),'+00:00','SYSTEM');
Where '+00:00' is UTC, the from timezone , and 'SYSTEM' is the local timezone of the OS where MySQL runs.
Mysql inherently solves this problem using time_zone_name table from mysql db.
Use CONVERT_TZ while CRUD to update the datetime without worrying about daylight savings time.
SELECT
CONVERT_TZ('2019-04-01 00:00:00','Europe/London','UTC') AS time1,
CONVERT_TZ('2019-03-01 00:00:00','Europe/London','UTC') AS time2;
This thread made me freak since we use TIMESTAMP columns with On UPDATE CURRENT_TIMESTAMP (ie: recordTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) to track changed records and ETL to a datawarehouse.
In case someone wonder, in this case, TIMESTAMP behave correctly and you can differentiate between the two similar dates by converting the TIMESTAMP to unix timestamp:
select TestFact.*, UNIX_TIMESTAMP(recordTimestamp) from TestFact;
id recordTimestamp UNIX_TIMESTAMP(recordTimestamp)
1 2012-11-04 01:00:10.0 1352005210
2 2012-11-04 01:00:10.0 1352008810
I was working on logging counts of visits of pages and displaying the counts in graph (using Flot jQuery plugin). I filled the table with test data and everything looked fine, but I noticed that at the end of the graph the points were one day off according to labels on x-axis. After examination I noticed that the view count for day 2015-10-25 was retrieved twice from the database and passed to Flot, so every day after this date was moved by one day to right.
After looking for a bug in my code for a while I realized that this date is when the DST takes place. Then I came to this SO page...
...but the suggested solutions was an overkill for what I needed or they had other disadvantages. I am not very worried about not being able to distinguish between ambiguous timestamps. I just need to count and display records per days.
First, I retrieve the date range:
SELECT
DATE(MIN(created_timestamp)) AS min_date,
DATE(MAX(created_timestamp)) AS max_date
FROM page_display_log
WHERE item_id = :item_id
Then, in a for loop, starting with min_date, ending with max_date, by step of one day (60*60*24), I'm retrieving the counts:
for( $day = $min_date_timestamp; $day <= $max_date_timestamp; $day += 60 * 60 * 24 ) {
$query = "
SELECT COUNT(*) AS count_per_day
FROM page_display_log
WHERE
item_id = :item_id AND
(
created_timestamp BETWEEN
'" . date( "Y-m-d 00:00:00", $day ) . "' AND
'" . date( "Y-m-d 23:59:59", $day ) . "'
)
";
//execute query and do stuff with the result
}
My final and quick solution to my problem was this:
$min_date_timestamp += 60 * 60 * 2; // To avoid DST problems
for( $day = $min_date_timestamp; $day <= $max_da.....
So I am not staring the loop in the beginning of the day, but two hours later. The day is still the same, and I am still retrieving correct counts, since I explicitly ask the database for records between 00:00:00 and 23:59:59 of the day, regardless of the actual time of the timestamp. And when the time jumps by one hour, I am still in the correct day.
Note: I know this is 5 year old thread, and I know this is not an answer to OPs question, but it might help people like me who encountered this page looking for solution to the problem I described.