关于在mysql数据库中存储PHP支持的时区的最佳实践

时间:2013-07-07 13:30:49

标签: php mysql datetime timezone

我想知道存储PHP 'supported timezones'的最佳做法是什么。我正在存储每个用户的时区,因此我可以转换到UTC或从当地时间转换。您会将它作为字符串存储在varchar类型字段中吗?在这种情况下,支持的最长时区字符串是什么?存储这些是否有更好的做法?简单地存储时区偏移量不是一个选项,因为它没有像PHP的datetime对象那样自动考虑DST。

3 个答案:

答案 0 :(得分:12)

几乎这个问题的完全重复:  Proper way to store a timezone in a database?

但我会谈到你提到的其他几点:

  • PHP时区为IANA time zones

  • 只需将名称存储在varchar中即可。关于时区名称的字段长度,有一些讨论herevarchar(32)会起作用,但为了安全起见,我会为将来的更改留出一些额外的空间。 255可能是矫枉过正,但也许varchar(50)是合理的。

  • 很多人建议在数据库中存储UTC。这有时候是一种很好的做法,但是当我推荐你应该总是做的事情时,我不喜欢它。有充分的理由使用UTC,并且有充分的理由使用当地时间。

  • 如果您使用UTC,则需要时区名称,并且必须在输入和输出时转换为本地时间。

  • 如果您使用当地时间,除了当地时间外,您应始终存储时区偏移。这是因为在DST过渡期间可能存在模糊的本地时间。有些数据库只有一个类型,例如Oracle或Postgres中的TIMESTAMP WITH TIMEZONE或SQL Sever中的DATETIMEOFFSET。不幸的是,MySql没有这种类型,所以你需要两列。

  • 如果您的数据只记录一次且从未更改过(例如记录的事件时间),则这些选项中的任何一个都是可行的。 UTC具有为数学和转换做好准备的优势。当地时间的优势在于保留了观察者的视角。请参阅DateTime vs DateTimeOffset哪个是关于.Net的,但这些概念仍然适用于此。

  • 如果您将在这些时间编辑,那么您将需要任何一种时区名称,因此您也可以存储它。您可以决定是否按记录的时间戳存储它,或者每个用户只存储一次。如果用户更改时区,您应该考虑要发生什么。它应该适用于所有地方或者只对新记录的条目?

我可以想到以UTC格式存储的一些实际原因:

  • 如果您正在运行将输出数百或数千行的报告,并且所需的输出是在记录的本地时间内,则必须在紧密循环中多次重复转换UTC 可以减慢报告的执行时间。

  • 有时法律要求时间准确存储。非技术审核员可能不允许进行任何类型的转换,并将UTC时间视为非法。如果他们查看本地时间加偏移值,他们可能不关心偏移部分,并且会对本地时间值感到满意。我知道这看起来很愚蠢,但有些行业和司法管辖区确实存在这些要求。

  • 有时您不希望实际引用单个瞬间,而是专门针对该时间的本地化表示。假设您在美国各地的餐厅都在上午6:00开放。那么,太平洋时间早上6点是与东部时间不同的时刻。将这些存储为UTC可能会导致无效的假设,特别是在DST更改时。

在大多数其他情况下,我建议以UTC格式存储。但是你必须决定哪种方法最适合你的特殊要求。

答案 1 :(得分:2)

然后将所有时间存储为UTC 创建另一个具有时区偏移的字段。

这实际上不是PHP问题,而是使用时区的数据库最佳实践。始终以UTC格式保存数据。有另一个TZ偏移字段,例如-10或作为时区,例如“欧洲/阿姆斯特丹”。对我来说,我在python中使用pytz来处理这些东西。

您将始终能够恢复夏令时。

答案 2 :(得分:2)

我总是存储日期& times作为整数字段中的unix时间戳。这样我就知道我从数据库中得到了什么。我建议使用您已经知道的this list中的字符串表示将时区存储在文本字段中。

例如,一个名为stackoverflow的表,在一个名为stackoverflow的数据库中: -

+--+----------+-------------+
|id|date_time |time_zone    |
+--+----------+-------------+
|1 |1373212914|Europe/London|
|2 |1373212914|Europe/Rome  |
+--+----------+-------------+

然后你会为这样的DateTime对象加水: -

$dsn = 'mysql:dbname=stackoverflow;host=127.0.0.1';
$user = 'stackoverflow';
$password = 'stackoverflow';

try {
    $dbh = new PDO($dsn, $user, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

$sql = 'select date_time, time_zone from stackoverflow';
$statement = $dbh->prepare($sql);
$statement->execute();
$results = $statement->fetchAll();
foreach($results as $result){
    $datetimes[] = (new \DateTime())->setTimestamp((int)$result['date_time'])->setTimezone(new \DateTimeZone($result['time_zone']));
}

var_dump($datetimes);

这给出了这个输出: -

array (size=2)
  0 => 
    object(DateTime)[3]
      public 'date' => string '2013-07-07 17:01:54' (length=19)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/London' (length=13)
  1 => 
    object(DateTime)[4]
      public 'date' => string '2013-07-07 18:01:54' (length=19)
      public 'timezone_type' => int 3
      public 'timezone' => string 'Europe/Rome' (length=11)

检查表将显示两个条目具有相同的时间戳,相当于2013-07-07 16:01:54 UTC,但在发送到客户端时在存储的时区中正确表示。