我正在尝试在我的应用程序中集成一个时区系统,我真的很努力避免现在开始制作时区感知应用程序 - 但现在它的强制性要求却别无选择。时区只是我的头脑。我在PHP.net和其他网站上阅读了几个主题,包括但不限于SO。但我永远无法掌握它。
所以我想知道是否有人可以在这里帮助我:(我想要的是我的应用程序中的偏好选项,允许用户从选择菜单中选择自己的时区,但应用程序也应该能够为每个用户自行设置/选择DST。
请相信这会帮助那些仍在努力掌握时区的人,所以请提供尽可能详细的解释,即使你必须考虑我一个完整的小型号/小号。
编辑赏金:
我正在为这个问题添加一个赏金,因为我真的需要在编写PHP / MySQL应用程序时需要一个关于时区的好的规范问题(因此我也添加了MySQL标记)。我从很多地方找到了东西,但把它们放在一起会很好。查尔斯的答案很棒,但我仍然觉得它有些缺乏。以下是我想到的一些事情:
DateTime
对象DATETIME
还是TIMESTAMP
?对每个人有什么好处或警告?DATE
的时区?NOW()
插入值。这些是否需要在插入之前或之后以某种方式进行转换?DateTime
对象。将它直接放入DateTime::__construct()
就足够了,还是我们需要使用DateTime::createFromFormat()
?NOW()
)而不担心时区以确保所有内容保持一致,应该怎么办?如果可能,请尝试将其分为逻辑部分,以便将来的用户更容易找到信息。请务必在必要时提供代码示例。
答案 0 :(得分:28)
此答案已更新,以容纳赏金。原始的,未经编辑的答案在线下。
在时区的背景下,赏金所有者添加的几乎所有问题都与MySQL和PHP日期时间应该如何交互有关。
MySQL still has pathetic timezone support,这意味着情报必须是PHP端。
NOW()
)得到妥善处理。DATETIME
, never use TIMESTAMP
除非您明确要求TIMESTAMP
中的特殊行为。这比过去痛苦少
Y-m-d H:i:s
这解决了大多数点。
最后一件事是doozy:
- 如果他们之前插入过数据(例如使用
NOW()
)而不担心时区以确保所有内容保持一致,应该怎么办?
这是一个真正的烦恼。其中一个答案指出MySQL的CONVERT_TZ
,虽然我个人已经通过在选择和更新期间在服务器本地和UTC时区之间跳跃来做到这一点,因为我是那样的硬核。
应用程序还应该能够为每个用户自行设置/选择DST。
你不需要和不应该在现代时代这样做。
PHP的现代版本具有DateTimeZone类,其中包括list named timezones的功能。命名时区允许用户选择其实际位置,并让系统自动根据该位置确定其DST规则。
您可以将DateTimeZone与DateTime结合使用,以实现一些简单但功能强大的功能。您只需以UTC by default存储和使用所有时间戳,并将其转换为显示的用户时区。
// 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有spotty but functional日期类,标准方法为get the UTC offset in minutes。您可以使用它来帮助缩小可能时区列表,盲目假设用户的时钟是正确的。
让我们将此方法与自己进行比较。除了向用户推断他们不会真正关心的选择之外,您还需要实际执行日期数学每次操作日期时间。这不仅仅是次优的,它是蝙蝠 - 鸟粪疯狂。强迫用户表示他们何时需要DST支持是一件麻烦和困惑。
此外,如果您想为此使用现代PHP DateTime和DateTimeZone框架,则需要use deprecated Etc/GMT...
timezone strings而不是命名时区。这些区域名称可能会从未来的PHP版本中删除,因此这样做是不明智的。我从经验中说出这一切。
tl; dr :使用现代工具集,免除日期数学的恐怖。向用户显示命名时区列表。 以UTC格式存储您的日期,这不会受到DST的任何影响。将日期时间转换为用户选择的指定时区显示,而不是更早。
根据要求,这是一个循环显示可用时区,以分钟为单位显示GMT偏移量。我在这里选择了几分钟来证明一个不幸的事实:不是所有的补偿都在整个小时内!有些人实际上在DST期间提前半小时而不是整整一小时。以分钟为单位的结果偏移应与Javascript的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";
}
答案 1 :(得分:4)
如何从PHP
中存储数据库中的时间DateTime
对象
SQL-92 standard指定应使用合适的数据类型关键字(例如TIMESTAMP
表示日期/时间值)在SQL中传递时态文字,后跟值的字符串表示(包含可选的)时区偏移,如果非默认)。
标准SQL允许使用type关键字和字符串指定时态文字。
[ deletia ]MySQL识别这些构造以及相应的ODBC语法:
[ deletia ]但是,MySQL忽略了type关键字,并且前面的每个结构都生成字符串值
'str'
,类型为VARCHAR
。
文档继续描述MySQL支持的文字格式,特别是没有明确的时区偏移。有一个feature request来解决这个问题,现在已经超过七年了,而且很快就会推出。
相反,必须在交换服务器和客户端之间的日期/时间值之前设置会话的time_zone
变量。因此,使用PDO:
连接到MySQL:
$dbh = new PDO("mysql:dbname=$dbname", $username, $password);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
将会话time_zone
设置为DateTime
对象的会话:
$qry = $dbh->prepare('SET SESSION time_zone = ?');
$qry->execute([$datetime->format('P')]);
从DateTime
对象生成一个合适的文字并正常传递给MySQL(即作为预备语句的参数)。
如文档中所述,可以使用许多可能的文字格式。但是,我建议使用'YYYY-MM-DD hh:mm:ss.ffffff'
格式的字符串(请注意,在5.6之前的MySQL版本中将忽略小数秒),因为它最接近SQL标准;事实上,可以在文字前面加上TIMESTAMP
关键字,以确保一个人的SQL是可移植的:
$qry = $dbh->prepare('
UPDATE my_table
SET the_time = TIMESTAMP ?
WHERE ...
');
$qry->execute([$datetime->format('Y-m-d H:i:s.u')]);
它们应该存储在
DATETIME
还是TIMESTAMP
?对每个人有什么好处或警告?
PHP DateTime
对象应始终存储在TIMESTAMP
类型列中。
最根本的区别是TIMESTAMP
存储时区信息(通过将值存储在UTC中并按照上面time_zone
变量的要求转换为/来自),而DATETIME
不存在。因此TIMESTAMP
对于表示特定时刻(类似于PHP DateTime
对象)非常有用,而DATETIME
对于表示在日历/时钟上看到的时间非常有用(如照片)。
正如The DATE
, DATETIME
, and TIMESTAMP
Types所述:
DATETIME
类型用于包含日期和时间部分的值。 MySQL以DATETIME
格式检索并显示'YYYY-MM-DD HH:MM:SS'
个值。支持的范围是'1000-01-01 00:00:00'
到'9999-12-31 23:59:59'
。
TIMESTAMP
数据类型用于包含日期和时间部分的值。TIMESTAMP
的UTC范围为'1970-01-01 00:00:01'
到'2038-01-19 03:14:07'
UTC。MySQL将
TIMESTAMP
值从当前时区转换为UTC进行存储,然后从UTC返回到当前时区进行检索。 (对于DATETIME
等其他类型,不会出现这种情况。)默认情况下,每个连接的当前时区是服务器的时间。可以基于每个连接设置时区。只要时区设置保持不变,您就会获得存储的相同值。如果存储TIMESTAMP
值,然后更改时区并检索值,则检索的值与您存储的值不同。发生这种情况是因为在两个方向上都没有使用相同的时区进行转换。当前时区可用作time_zone
系统变量的值。有关详细信息,请参阅Section 10.6, “MySQL Server Time Zone Support”。
TIMESTAMP
数据类型提供自动初始化和更新到当前日期和时间。有关详细信息,请参阅Section 11.3.5, “Automatic Initialization and Updating forTIMESTAMP
”。
注意最后一段,它经常会抓住MySQL的新手。
值得补充的是,正如Data Type Storage Requirements中所述,DATETIME
值需要8个字节用于存储,而TIMESTAMP
值只需要4个字节(基础数据存储格式可以是在Date and Time Data Type Representation)中找到。
我们是否需要担心MySQL
DATE
的时区?
时间对时区敏感才有意义。根据定义,一个日期单独无论时区如何都是普遍相同的,因此不需要担心时区"使用MySQL的DATE
数据类型时。
这样做的必然结果是,如果一个人拥有对时区敏感的值,那么还必须存储时间,例如:在TIMESTAMP
列中:使用DATE
列会导致不可逆转的重要信息丢失。
如何使用
NOW()
插入值。这些是否需要在插入之前或之后以某种方式进行转换?
正如NOW()
所述:
以
'YYYY-MM-DD HH:MM:SS'
或YYYYMMDDHHMMSS.uuuuuu
格式返回当前日期和时间,具体取决于函数是在字符串还是数字上下文中使用。该值以当前时区表示。
由于" 该值以当前时区表示"和那个" 当前时区"将用于评估日期/时间值,使用MySQL的NOW()
函数(或其任何别名)时,不必担心时区。因此,要插入记录:
INSERT INTO my_table (the_time) VALUES (NOW());
请注意,如上所述,MySQL TIMESTAMP
列的自动初始化使得在记录插入/更新期间大多数尝试使用NOW()
。
是否需要设置MySQL使用的时区?如果是这样,怎么样?应该是持久地还是每次HTTP请求?它必须设置为UTC还是其他任何东西?或者服务器的时间是否充足?
这已在上面解决。如果需要,可以全局设置MySQL的time_zone
变量,从而避免在每次连接时设置它。有关详细信息,请参阅MySQL Server Time Zone Support。
如何从MySQL检索值并将其转换为
DateTime
对象。将它直接放入DateTime::__construct()
就足够了,还是我们需要使用DateTime::createFromFormat()
?
如Compound Formats所述,PHP在DateTime::__construct()
中使用的解析器识别的日期/时间格式之一是MySQL的输出格式。
但是,由于MySQL输出格式不包含时区,因此必须确保通过其可选的第二个参数为DateTime
构造函数提供该信息:
$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);
或者,可以让MySQL将时间转换为UNIX时间戳并从中构造DateTime
对象:
$qry = $dbh->query('SELECT UNIX_TIMESTAMP(the_time) FROM my_table');
$datetime = new DateTime($qry->fetchColumn());
何时转换为当地时间以及原因。是否曾经有一段时间我们希望在之前将其转换回,它会回显给用户(例如,与另一个DateTime对象或静态值进行比较)?
我不确定你的意思是什么时候"当地时间" (本地对谁?RDBMS?网络服务器?webclient?),但DateTime
对象之间的比较将根据需要处理时区转换(PHP在内部以UTC格式存储值,仅转换为输出)。
我们是否有时间担心夏令时(DST)?为什么或为什么不呢?
一般来说,如果您遵循上面给出的方法,DST唯一需要关注的是确保以期望的时区向用户呈现价值。
如果以前插入数据(例如使用
NOW()
)而不担心时区以确保所有内容保持一致,应该怎么做?
如上所述,使用NOW()
不应该导致问题。
如果在会话的TIMESTAMP
变量设置为不正确的值时,已将文字值插入time_zone
列,则需要相应地更新这些值。 MySQL的CONVERT_TZ()
功能可能会有所帮助:
UPDATE my_table SET the_time = CONVERT_TZ(the_time, '+00:00', '+10:00');
答案 2 :(得分:0)
如何从PHP DateTime对象存储数据库中的时间 它们应该存储在DATETIME还是TIMESTAMP中?有什么好处 或每个警告?
*更新,澄清我的第一段* 您还可以将时间戳存储为INT。优点是您知道在哪个时区存储了您的值,因为时间戳是自Unix Epoch(1970年1月1日00:00:00 GMT)以来的秒数测量的当前时间。 看到php doc:http://php.net/manual/en/function.time.php 使用64位操作系统,您不必担心2038年的问题: http://en.wikipedia.org/wiki/Year_2038_problem
使用时间戳更容易比较日期时间,在objet和数组中使用更有趣。例如,您可以轻松地将它们用作阵列的键。
我们是否需要担心MySQL DATE的时区?
在MySQL中,CURRENT_TIMESTAMP(),CURRENT_TIME(),CURRENT_DATE()和FROM_UNIXTIME()函数返回连接当前时区的值,该值可用作time_zone系统变量的值。此外,UNIX_TIMESTAMP()假定其参数是当前时区中的日期时间值。 http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html
如何使用NOW()插入值。这些是否需要转换 不知何故在插入之前或之后?
如果使用时间戳,则可以依赖PHP函数,它只是一个整数。
如果您使用日期时间,则curdate函数允许您拥有当前日期。 http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html#function_curdate
是否需要设置MySQL使用的时区?如果是这样,怎么样?应该 它是持久地还是在每个HTTP请求上完成的?是否必须如此 设置为UTC还是其他任何东西?或者是服务器的时间 足够?
请参阅http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html
如何从MySQL检索值并将其转换为DateTime 宾语。将它直接放入DateTime :: __ construct()就足够了 还是我们需要使用DateTime :: createFromFormat()?
同样,如果您使用时间戳,则更容易。你知道时间戳时区,你知道吗 什么时候转换为当地时间。使用时间戳可以轻松管理DST,请参阅时间戳周围的函数: http://php.net/manual/en/function.mktime.php
有没有时间我们想要在它之前转换它 回复给用户(例如,与另一个DateTime对象进行比较或 静态值??
我再想一想,时间戳可让您使用日期来比较它们,提取您需要的任何内容并打印您想要的内容。
有没有时间我们需要担心夏令时 (DST)?为什么或者为什么不?如果他们以前有人应该做什么 插入数据(例如使用NOW())而不用担心时区 确保一切都保持一致?
是的,如果您必须在申请中创建约会或会议,您应该担心。我开发了两个应用程序,一个用于临床预约,一个用于研讨会预约,支持超过70000个帐户和大量记录。我坚持使用时间戳,它非常适合索引,操作,比较。打印部件仅在视图上显示。
在数据库中使用datetime是有好处的。如果你必须在sql direct中分析表中的数据,那么它更容易阅读,它更“人性化”。
我不确定这篇文章会有一个固定的答案,因为这取决于你的需求。时间戳很容易操作(一种实用的方法)。存储它的方式取决于您的偏好,因为您可以存储日期并仍然将其转换为时间戳。但根据我的理解,时区是时间戳定义的一部分。