我正在尝试将时区系统集成到我的应用程序中,直到现在为止,我一直在努力避免制作时区感知的应用程序- 但是它是一项强制性要求,因此别无选择。TimeZones只是在我头上。我已经在PHP.net和其他站点(包括但不限于SO)上阅读了多个主题。但是我永远也无法掌握。
因此,我想知道是否有人可以在这里帮助我:(我想要做的是在我的应用程序中使用偏好选项,以允许用户从选择菜单中选择自己的时区,但是该应用程序也应该能够设置/为每个用户相应地选择DST。
请确保这将对仍在努力争取时区的其他人有所帮助,所以即使您认为我是一个完整的小飞象/傻瓜,也请提供尽可能详细的解释。
编辑赏金:
我为这个问题添加了赏金,因为我真的很需要编写PHP / MySQL应用程序时需要一个有关时区的规范性问题(因此我还要添加MySQL标记)。我已经从很多地方找到了东西,但是最好将它们放在一起。查尔斯的回答很好,但我仍然觉得它缺少一些。这是我想到的一些事情:
DateTime
DATETIME
TIMESTAMP
DATE
NOW()
DateTime::__construct()
DateTime::createFromFormat()
如果可能,请尝试将其分成逻辑部分,以使将来的用户更容易找到信息。请确保在必要时提供代码示例。
此答案已更新,以适应赏金。原始的未编辑答案在该行下方。
赏金所有者添加的几乎所有问题都与在时区范围内MySQL和PHP日期时间应如何交互有关。
MySQL仍然具有 可怜的 时区支持,这意味着智能必须在PHP端提供。
始终使用DATETIME,TIMESTAMP除非明确要求特殊行为,否则不要使用TIMESTAMP。这比以前减轻了痛苦。
Y-m-d H:i:s
这解决了 大多数 问题。
最后一件事是笨拙的:
如果某人先前已插入数据(例如使用NOW()),而不必担心时区以确保一切保持一致,该怎么办?
这真是个烦人。另一个答案指出了MySQL的答案CONVERT_TZ,尽管我个人通过在选择和更新过程中在服务器本地时区和UTC时区之间切换来做到这一点,因为我很顽固。
CONVERT_TZ
该应用程序还应该能够为每个用户自行设置/选择DST。
在现代,您不需要也不 应这样 做。
现代版本的PHP具有DateTimeZone类,该类具有列出命名的timezones的功能。命名时区允许用户选择其实际位置,并使系统根据该位置 自动 确定其DST规则。
您可以将DateTimeZone与DateTime结合使用,以获得一些简单但功能强大的功能。默认情况下,您可以简单地在UTC中存储和使用 所有 时间戳,并将它们转换为显示的用户时区。
// 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
通过使用此技术,系统将 自动 为用户选择正确的DST设置,而无需询问用户当前是否在DST中。
您可以使用类似的方法来呈现选择菜单。您可以连续地为单个DateTime对象重新分配时区。例如,此代码此刻将列出区域及其当前时间:
$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"; }
通过使用一些客户端魔术,可以大大简化选择过程。Javascript有一个参差不齐但实用的 Date类,它具有一种标准方法,可以在分钟内获取UTC偏移量。在用户时钟正确的盲目假设下,您可以使用它来帮助缩小可能的时区列表。
让我们将这种方法与自己进行比较。除了推翻他们并不真正在乎的用户之外,您还需要在 每次 操作日期 时间时 实际执行一次日期数学运算。这不仅是次优的,而且是蝙蝠鸟粪的疯狂。强迫用户在需要DST支持时注明需要麻烦和困惑。
此外,如果您想为此使用现代的PHP DateTime和DateTimeZone框架,则需要使用不建议使用的Etc/GMT...时区字符串而不是命名时区。这些区域名称可能会从将来的PHP版本中删除,因此这样做是不明智的。我说的都是经验。
Etc/GMT...
tl; dr :使用现代工具集,免除日期数学的恐惧。向用户显示 命名 时区的列表。 将您的日期存储在UTC中 ,这不会受到DST的任何影响。将datetimes转换为 display上 用户选择的命名时区,而不是更早。
根据要求,这是一个可用时区的循环,以分钟为单位显示其GMT偏移量。我在这里选择分钟来演示一个不幸的事实:并非所有的补偿都在整个小时内!实际上,有些人在夏令时期间提前了半个小时而不是整整一个小时。以分钟为单位的最终偏移量 应 与Javascript的偏移量匹配Date.getTimezoneOffset。
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"; }