【问题标题】:Store in UTC in SQLite and display in local timezone with PHP在 SQLite 中以 UTC 存储并使用 PHP 在本地时区显示
【发布时间】:2019-09-16 21:40:49
【问题描述】:

我有一个包含日期时间列的表:

$db = new SQLite3('test.db');
$results = $db->query('CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT,
                                                        date DATE, foo TEXT);');

然后我添加一行(以 UTC 存储添加行的日期时间)

$results = $db->query('INSERT INTO test (date, foo) VALUES(CURRENT_TIMESTAMP, "bar");');

这行得通。现在显示行时:

$results = $db->query('SELECT * FROM test ORDER BY date desc');
while ($row = $results->fetchArray()) {
    echo $row['date'];
}

日期显示如下,UTC 格式:2019-04-27 16:41:33

如何改为在当地时区显示?(包括夏令时)

我可以想象有不同的选择:

  • 使用本地时区直接存储在 SQLite 中(但我认为这不是好的做法)

  • 以 UTC 存储在 SQLite 中,并在 SELECT 期间进行 UTC->本地时区转换。怎么样?

  • 以 UTC 存储在 SQLite 中,并通过 PHP 进行 UTC->本地时区转换。

如何正确执行此操作?

【问题讨论】:

标签: php sql sqlite datetime timezone


【解决方案1】:

正如评论所建议的,this page 提到了 localtime 修饰符。这是一个解决方案:

$results = $db->query('SELECT datetime(date, "localtime") AS localdate, foo FROM test ORDER BY date desc');
while ($row = $results->fetchArray()) {
    echo $row['localdate'];
}

【讨论】:

    【解决方案2】:

    我想说的是,您需要将时间值存储为称为“Unix 时间戳”的东西,它实际上是自 UTC 1970 年 1 月 1 日 00:00 以来的秒数。您可以使用 PHP 的 time() 函数获取当前时间。然后将您的数据库“日期”列更改为整数类型。这样一来,你的数据库中存储的时间数据就完全独立于时区等,这是一件好事!

    为了正确解释这些时区,我们可以使用 PHP DateTime 对象,它会在时区和 DST 方面完成所有繁重的工作。以下代码sn -p给出了基本思路:

    // These need to be provided from the client side
    // Native JS will get you the offset, read my comment below for where to get DST from 
    $TimeOffset = $_POST['TZOffset'];
    $isDST = $_POST['isDST'];
    
    // DST needs to be an integer
    if ($isDST == 'true'){
        $isDst = 1;
    }else{
        $isDst = 0;
    }
    
    // Will give a result like Europe/London
    // In most use cases, save this into the users session :)
    $TimeZoneName = timezone_name_from_abbr('', $TimeZone * -60, $isDst);
    
    // Now to tell PHP we are working in this timezone
    date_default_timezone_set($TimeZoneName);
    
    ///// Somewhere else in your script
    
    // Fetched a unix timestamp from your DB; $timestamp
    $DT = new DateTime();
    $DT -> setTimestamp($timestamp);
    
    // Now you have control over how it is displayed
    // For instance, this gives a 2010-04-28 22:41:43 type format
    // This will be correct to the user's timezone we calculated earlier
    echo $DT -> format('Y-m-d H:i:s');
    

    客户端是否在夏令时应该从JS,have a look at the answer for this question for a simple method获取,但是很多库等也可以做到。

    那么大问题,为什么要绕远路?

    • 数据库中的时间数据完全独立于数据库/服务器配置。随着时间的流逝,这些可能会游荡,并且错误可能会发挥作用;您希望基本数据保持一致。
    • 它让 PHP 在每个地区/DST 制度中完成所有有关调整时区和 DST 的艰苦工作。如果您尝试自己解决这个问题,很快就会变得很头疼,DST 非常尴尬。
    • 时间数学更高效、更容易,尤其是在 DB 调用中。您需要做的只是对秒数进行数值比较,而不是处理 SQL 函数等。
    • 不依赖特定的数据库功能或引擎;它只是一个整数值!便于携带。

    请注意,请注意 DB 整数列中的最大值。找到最大值/最小值,将它们转换为日期时间 (this is a useful tool) 并检查您的用例。

    最后要说的是,您可以使用 DateTime 对象来解释 UTC 中的时间字符串(就像您现在使用的那样),然后在打印它们之前设置时区。它会起作用,但我觉得它更容易出错。

    希望对你有帮助:)

    【讨论】:

      【解决方案3】:

      看看这个 SQL 解决方案:

      select ts, 
             cast( round( ( julianday( ts, 'localtime' ) - julianday( ts ) ) * 24 ) as int ) as diff, 
             datetime( ts, '+' || cast( round( ( julianday( ts, 'localtime' ) - julianday( ts ) ) * 24 ) as int ) || ' hours' ) as local
      from test
      

      结果:

      ts                  dif local
      2020-03-25 14:09:31 1   2020-03-25 15:09:31
      2020-03-31 06:54:08 2   2020-03-31 08:54:08
      2020-03-31 14:08:10 2   2020-03-31 16:08:10
      2020-04-01 07:23:04 2   2020-04-01 09:23:04
      2020-04-01 09:53:19 2   2020-04-01 11:53:19
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-07
        • 2018-05-08
        • 2011-06-06
        • 2012-03-10
        • 1970-01-01
        • 2021-06-26
        • 2020-10-10
        • 2021-02-09
        相关资源
        最近更新 更多