Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This answer has been updated to accomodate the bounty. The original, unedited answer is below the line.</p> <p>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. </p> <p><a href="http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html">MySQL still has <em>pathetic</em> timezone support</a>, which means that the intelligence has to be PHP-side.</p> <ul> <li>Set your MySQL <em>connection timezone</em> to UTC as documented in the link above. This will cause all datetimes handled by MySQL, including <code>NOW()</code>, to be handled sanely.</li> <li><a href="https://dev.mysql.com/doc/refman/5.0/en/datetime.html">Always use <code>DATETIME</code>, never use <code>TIMESTAMP</code></a> unless you very expressly require the special behavior in a <code>TIMESTAMP</code>. This is less painful than it used to be.<br> <ul> <li>It's <em>ok</em> to store the Unix epoch time as an integer if you <em>have</em> to, such as for legacy purposes. The epoch is UTC.</li> <li>MySQL's preferred datetime format is created using the PHP date format string <code>Y-m-d H:i:s</code></li> </ul></li> <li>Convert <em>all</em> PHP datetimes to UTC when storing them in MySQL, which is a trivial thing as outlined below</li> <li>Datetimes returned from MySQL can be handed safely to the PHP DateTime constructor. Be sure to pass in a UTC timezone as well!</li> <li>Convert the PHP DateTime to the user's local timezone <em>on echo</em>, no sooner. Thankfully DateTime comparison and math against other DateTimes will take into account the timezone that each is in.</li> <li>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.</li> </ul> <p>That addresses <em>most</em> of the points.</p> <p>The last thing is a doozy:</p> <blockquote> <ul> <li>What should someone do if they have previously inserted data (e.g. using <code>NOW()</code>) without worrying about the time zone to make sure everything stays consistent?</li> </ul> </blockquote> <p>This is a real annoyance. One of the other answers pointed out MySQL's <a href="http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_convert-tz"><code>CONVERT_TZ</code></a>, 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.</p> <hr> <blockquote> <p>the app should also be able to SET/Choose the DST accordingly itself for each user.</p> </blockquote> <p>You don't need to and <em>should not</em> do this in the modern era.</p> <p>Modern versions of PHP have the <a href="http://us2.php.net/manual/en/class.datetimezone.php">DateTimeZone</a> class, which includes the ability to <a href="http://us2.php.net/manual/en/datetimezone.listidentifiers.php">list named timezones</a>. Named timezones allow the user to select their actual location, and have the system <em>automatically</em> determine their DST rules based on that location. </p> <p>You can combine DateTimeZone with <a href="http://us2.php.net/manual/en/class.datetime.php">DateTime</a> for some simple but powerful functionality. You can simply store and use <em>all</em> of your timestamps in UTC <a href="http://us2.php.net/manual/en/function.date-default-timezone-set.php">by default</a>, and convert them to the user's timezone on display.</p> <pre><code>// 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-&gt;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-&gt;setTimeZone($la); // and show the result echo $nowish-&gt;format('Y-m-d H:i:s'); // 2011-04-23 14:44:00 </code></pre> <p>By using this technique, the system will <em>automatically</em> select the correct DST settings for the user, without asking the user whether or not they're currently in DST. </p> <p>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:</p> <pre><code>$dt = new DateTime('now', new DateTimeZone('UTC')); foreach(DateTimeZone::listIdentifiers() as $tz) { $dt-&gt;setTimeZone(new DateTimeZone($tz)); echo $tz, ': ', $dt-&gt;format('Y-m-d H:i:s'), "\n"; } </code></pre> <p>You can greatly simplify the selection process by using some client-side magic. Javascript has a <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date">spotty but functional</a> Date class, with a standard method to <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset">get the UTC offset in minutes</a>. You can use this to help narrow down the list of likely timezones, under the blind assumption that the user's clock is right.</p> <p>Let's compare this method to doing it yourself. You'd need to actually perform date math <em>every single time</em> 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.</p> <p>Further, if you wanted to use the modern PHP DateTime and DateTimeZone framework for this, you'd need to <a href="http://us2.php.net/manual/en/timezones.others.php">use deprecated <code>Etc/GMT...</code> timezone strings</a> 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. </p> <p><strong>tl;dr</strong>: Use the modern toolset, spare yourself the horrors of date math. Present the user with a list of <strong>named</strong> time zones. <strong>Store your dates in UTC</strong>, which won't be impacted by DST in any way. Convert datetimes to the user's selected named time zone <strong>on display</strong>, not earlier.</p> <hr> <p>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 <em>should</em> match that of Javascript's <code>Date.getTimezoneOffset</code>.</p> <pre><code>$utc = new DateTimeZone('UTC'); $dt = new DateTime('now', $utc); foreach(DateTimeZone::listIdentifiers() as $tz) { $local = new DateTimeZone($tz); $dt-&gt;setTimeZone($local); $offset = $local-&gt;getOffset($dt); // Yeah, really. echo $tz, ': ', $dt-&gt;format('Y-m-d H:i:s'), ', offset = ', ($offset / 60), " minutes\n"; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. CO@Charles: Thanks for the very detailed post :) i think i understand most of the stuff you're saying and also that I should avoid using the offset and dst to present the timezone-aware Date/Time to the user? and instead just use the named Timezone identifiers? if i got that right, i think i get it alittle now :) but i'm confused about something alittle the above foreach loop you posted is pretty nice and all but for my timezone it is showing the time 1 hour ahead of what it is here currently, but i know its not something wrong in your code because it does that with all the date/time in my code.
      singulars
    2. CO@Charles: sorry ran out space to write more :) do you know what might be causing that 1 extra hour? i already tried using the set timezone function at the beginning of my script and defined my local timezone, but that doesn't do any good. One other thing could you please modify your above foreach code so it prints out the offset to on the same line like: timezone - time - GMT -5.0 ?
      singulars
    3. CO@Zubair, have you made sure that your server's clock is correct, and that it's in the correct time zone? That could cause the odd one-hour-off behavior you're seeing. If the operating system was installed more than a year or two ago and hasn't been kept up to date, it could have an out of date timezone database. The DST start and end dates in many US zones have changed over the past few years. I've also updated my post with a loop demonstrating how to get the offset from GMT from a given datetime and timezone.
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload