【问题标题】:Handling Timezones with PHP and SQL Server 2000使用 PHP 和 SQL Server 2000 处理时区
【发布时间】:2011-08-28 09:24:07
【问题描述】:

你们好,伙计们,这将是一个笨蛋:

所以有一段时间以来,我一直在为我的雇主开发一种产品,而且我们即将推出。但我们真的只是意识到我们把自己画在一个角落里。

在显示时区时,我们从不考虑时区。我们将时间存储在存储在本地 (EST) 时区的日期时间字段中。

假设我们在客户表中添加一个字段以确定他们所在的时区,在显示级别转换这些时间的最佳方法是什么,以及当我们接受来自用户的时间戳(例如,输入字段)时,将其转换返回 EST 进行存储。

对于我们的数据库抽象架构,我们使用表类方法。我不确定这个方法的花哨名称,但基本上我们使用的每个表都有一个类,其数据库模式为 PHP 代码,如下所示:

此代码将用于名为 L2FU_notification 的表

<?php

class L2FU_notificationbase extends dbobject
{

    protected $EmailSentTimestamp;
    protected $hasBeenEmailed;
    protected $HasBeenViewed;
    protected $notificationGeneratedDate;
    protected $NotificationID;
    protected $notificationText;
    protected $notificationTitle;
    protected $releaseDate;
    protected $xtelelinkTarget;
    protected $xtelelinkToBeNotified;

    protected $fields = array('EmailSentTimestamp',
                        'hasBeenEmailed',
                        'HasBeenViewed',
                        'notificationGeneratedDate',
                        'NotificationID'
                        );

    protected $table = "L2FU_notification";
    protected $primaryKey = "NotificationID";


    function getEmailSentTimestamp()
    {
        return $this->EmailSentTimestamp;
    }

    function setEmailSentTimestamp($EmailSentTimestamp)
    {
        $this->changedFields[] = 'EmailSentTimestamp';
        $this->EmailSentTimestamp = $EmailSentTimestamp;

        return $this;
    }


    function gethasBeenEmailed()
    {
        return $this->hasBeenEmailed;
    }

    function sethasBeenEmailed($hasBeenEmailed)
    {
        $this->changedFields[] = 'hasBeenEmailed';
        $this->hasBeenEmailed = $hasBeenEmailed;

        return $this;
    }


    function getHasBeenViewed()
    {
        return $this->HasBeenViewed;
    }

    function setHasBeenViewed($HasBeenViewed)
    {
        $this->changedFields[] = 'HasBeenViewed';
        $this->HasBeenViewed = $HasBeenViewed;

        return $this;
    }


    function getnotificationGeneratedDate()
    {
        return $this->notificationGeneratedDate;
    }

    function setnotificationGeneratedDate($notificationGeneratedDate)
    {
        $this->changedFields[] = 'notificationGeneratedDate';
        $this->notificationGeneratedDate = $notificationGeneratedDate;

        return $this;
    }


    function getNotificationID()
    {
        return $this->NotificationID;
    }

    function setNotificationID($NotificationID)
    {
        $this->changedFields[] = 'NotificationID';
        $this->NotificationID = $NotificationID;

        return $this;
    }


}
?>

这是一个由我编写的应用程序生成的类,它查看表模式并为该类构建 getter、setter 等。

dbobject 类具有保存和加载功能,可以将数据写入/从数据库中读取数据到具有相应主键的行。

作为背景信息的最后一点,这是客户类的示例实例(它扩展了客户库,扩展了 dbobject)

<?php
      $customerObj = new customer(1431); //let's say the customer ID we're working on is 1431. the constructor automatically calls load() when a primary key is passed at instantiation 
      $customerObj->setFirstName("Henry")->setLastName("Ford")->save();
?>

所以这是我正在开发的产品架构的一些基础——现在还不能改变,因为我们已经接近发布。

这是实际的问题:

我们如何处理与时区相关的时间戳的互操作性?不幸的是,SQL Server 2000 无法在显示时转换时区(然后在保存时将它们转换回本地时区),这本来是最简单的解决方案。

这是我的一个想法,并且实际上开始着手,但我想在花时间将它写到完成之前获得一些反馈,但它实际上并不能正常工作。

在所有基类中,我让生成器添加了一个新属性,该属性列出了表中所有字段类型为数组中的日期时间,如下所示:

protected $dateFields = array('EmailSentTimestamp',
                    'notificationGeneratedDate');

然后在我的 dbobject::load() 函数中,当我作为 SQL 查询的结果填充对象中的所有字段属性时,如果当前正在迭代的字段(通过 $ 上的 foreach 循环该类中的字段数组)在 $dateFields 数组中,它会将该值转换为正确的时区,然后再将其设置到属性中。

为了保持数据完整性,在 save() 函数中,当它在日期时间字段上进行迭代时,它会将其转换回我们的本地时间戳。

这是我如何将其可视化的一步一步。

表上的记录有一个时间戳为2011-05-20 13:45:00的字段

我们知道示例用户来自英国,并且时区偏移量为 +5。

当实例化对象并调用 load 时,它会自动将该时间戳转换为 2011-05-20 18:45:00,因此每当调用 getter 时,它都会返回该字符串。

用户通过网页上的日期时间选择器修改时间戳。他选择 2011-05-20 20:00:00。它作为这个值被存储回对象实例。

调用save函数时,将实例中存储的时间戳转换为2011-05-20 15:00:00,然后写入数据库。

完成。

这样,我们显示时间的所有地方都会自动“固定”,当它们需要更改某个时间戳值时,它会在保存时自动调整。

所以根本问题是

A) 这是处理此问题的最佳方式(考虑到我们紧迫的截止日期,也是最有效的方式)吗? B)如果不是,那是什么? C) 有哪些 PHP 解决方案可以帮助将时间戳从一个时区实际转换到另一个时区。

我处于严格的 CDA 之下,因此我尽量避免发布过多的代码,但如果需要更多信息,请告诉我。

澄清说明:更改时间的存储方式是不可能的/不理想的。其他产品也使用相同的数据库,包括内部员工 QA 工具。使用不同的时间系统(无论 UTC 多么吸引人)可能会导致问题。

【问题讨论】:

    标签: php datetime timezone sql-server-2000


    【解决方案1】:

    这有点离题,但如果您以当地时间存储日期,则无法 100% 准确地处理时区转换:每年 1 小时,在 DST 转换期间,您存储的时间您的数据库是模棱两可的 - 从本地时间根本无法得出 UTC(也无法在另一个没有完全相同的 DST 转换的时区得出适当的时间),因为您不知道事件是否是在那个时候的第一个实例,或者第二个。

    因此,如果它仍然可以协商并且您需要处理非 DST 等效时区或将来可能需要:切换到存储为 UTC。

    【讨论】:

    • 不幸的是,这并不容易。尽管我希望它像切换到 UTC 并以此为基础进行转换一样简单,但 SQL 服务器已经设置为使用 EST,并且我们的其他几个产品同时使用它。现在改变它会严重破坏事情。如果不是这种情况,您的答案将是正确的。
    • 顺便说一句,我认为我的公司愿意放弃每年一小时的模糊性,只是作为使用旧时间配置的妥协。我现在正在开发的产品是公司中第一个拥有如此全球范围的产品,因此原始数据库架构师的决策的后果开始影响到我和我的团队。
    • 不确定我是否在这里遗漏了什么,但我不是在谈论将服务器设置为 UTC 时间,只是存储 UTC 值。当您想以 UTC 获取当前时间时,只需使用 GetUtcDate() 而不是 GetDate()。
    • 也是一个相当不错的想法,但我认为它与我们公司的模型不完全兼容。在代码中,我们将字符串“CURRENT_TIMESTAMP”传递给“notificationGeneratedDate”等字段的设置器。现在我想我可以让 save() 函数自动将字符串 CURRENT_TIMESTAMP 替换为 GetDate() 的值; - 这是我愿意做的工作。但是,该公司的其他产品将与该特定产品创建的记录进行交互。这可能会导致不同步,因为它们不使用 UTC。就像我说的,其他人过去的决定现在正在咬我。
    • 哈,我可以同情。十年前我们的系统启动时,我们计划支持多个时区,但为了简单起见,将内容存储为东部时间(服务器时间);直到很久以后,我们才意识到事后正确地做到这一点要困难得多。我们仍然不支持其他时区。
    猜你喜欢
    • 2015-08-23
    • 2016-05-29
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 2014-12-19
    相关资源
    最近更新 更多