关于mysql:在PHP Web应用程序中使用时区

Using time zones in a PHP web application

我已经找了几个小时了,看看在php/mysql web应用程序中使用时区的最佳方法是什么,很难找到一个明确的答案。根据我迄今为止所学,最好将每个人的资料以UTC格式存储在数据库中(如果我错了,请纠正我)。

当一个用户注册时,我会向他们询问那里的时区,然后将其存储在那里。此格式为下拉菜单:

1
<option value="Europe/London">(GMT) Greenwich Mean Time : London</option>

我正在构建的应用程序将允许用户在未来设置人员安排(会议),就像日历一样。很明显,在一年的时间里,不同的时区有不同的夏令时,你知道我该如何应付吗?

假设一个来自英国的用户在2013年1月24日下午3:00安排了一个会议,并邀请了一个住在加利福尼亚的人参加这个会议,我如何才能让美国人在他/她的时区看到这个会议,而英国用户在他/她的时区看到这个会议?(请注意,两个用户都已注册并设置了时区)。

有没有人对此有明确的解释,也许还有一些例子?或者可以指给我在哪里能找到它?

谢谢


我在一年多前为一个私人喷气机操作员编写的PHP/MySQL应用程序中广泛地处理了这种情况。在这两个平台中,处理时区有不同的策略,但我将解释我是如何做到的。我将mysql服务器设置为utc,并在用户在用户配置文件注册过程中指定的时区中运行每个php脚本。

mysql和php(php 5.2及更高版本)都具有本机日期时间数据类型。mysql的datetime是一种原始数据类型,而php 5.2及更高版本提供了内置的datetime类。mysql datetime数据类型不包括时区的元数据,但php datetime对象始终包括时区。如果php datetime构造函数没有在第二个参数中指定可选时区,则php datetime构造函数使用php环境变量。

MySQL和PHP在配置文件中都设置了默认时区。MySQL使用配置文件中为每个DB连接设置的日期时间,除非用户在使用命令SET time_zone = [timezone];启动连接后指定了不同的时区。php还使用服务器配置文件中设置的时区为每个脚本设置一个时区环境变量,在脚本启动后,可以使用php函数date_default_timezone_set()覆盖此环境变量。

php datetime类有一个名为timezone的属性,它是php datetimezone对象。DateTimeZone对象是使用精确时区的字符串指定的。时区列表是全面的,在世界各地有数百个单独的时区。PHP时区将自动计入夏令时。

当用户在Web应用程序中生成日期时间时,在用户配置文件的时区中构造一个php-datetime对象。然后使用setTimezone方法将datetime对象修改为UTC时区。现在您有了以UTC表示的用户日期时间,并且可以将该值存储在数据库中。使用datetime format方法将数据表示为MySQL接受的格式的字符串。

因此,用户生成一个datetime,并在用户指定的时区中生成一个php datetime对象:

1
2
3
4
5
6
7
8
// set using an include file for user profile
$user_timezone = new DateTimeZone('America/New_York');

// 1st arg in format accepted by PHP strtotime
$date_object1 = new DateTime('8/9/2012 5:19 PM', $user_timezone);
$date_object1->setTimezone(new DateTimeZone('UTC'));
$formated_string = $date_object1->format('Y-m-d H:i:s');
$query_string="INSERT INTO `t_table1` (`datetime1`) VALUES('$formated_string')";

从数据库中检索值时,请使用UTC构造,然后转换为用户的时区。

1
2
3
4
$query_string="SELECT `datetime1` FROM `t_table1`";
$date_object1=new DateTime($datetime_string_from_mysql, new DateTimeZone('UTC');
$date_object1->setTimezone($user_timezone);
$string_for_display_in_application = $date_object1->format('m/d/Y g:i a');

使用此方法,您的日期时间值总是存储在数据库中的UTC中,并且用户总是体验到他/她的配置文件时区中的值。如果每个时区有必要,PHP将更正夏令时。

一言为定:这个解释不包括mysql时间戳数据类型。我建议使用mysql datetime datetime来存储datetime值,而不是timestamp数据类型。时间戳数据类型在这里的手册中介绍。

编辑:可以使用listIdentifiers生成一个数组,其中包含每个php时区字符串,这是datetimezone类的静态方法。


在MySQL中,您需要做的是:

  • 将每个用户选择的时区存储到您可以在代表该用户进行数据库查询时检索到的位置。您可以将其存储为字符串。
  • 在代表特定用户工作(例如,存储或检索预约时间和日期)之前,先执行SET time_zone = (stored time zone setting)
  • 这将导致时区适当地转换为每个人的本地时间。

    编辑:

    这是因为

  • MySQL尝试使用UTC(通用时间,以前称为格林威治标准时间)在表中存储日期时间和时间戳数据项。
  • 只有当它知道应用程序给定的每个数据项的正确本地时区时,它才能正确地执行此操作。
  • 在不关心不同时区的应用程序中,它以一种mysql服务器范围的方式来实现这一点。请参阅https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html。大多数运行多国家和多时区应用程序的人将其服务器时区设置为UTC,而不是本地时间,因为这样可以更容易地将问题解决。
  • 除非您还知道日期和时区,否则尝试将时间从UTC转换为本地时间是没有意义的,因为本地时间在一年中的不同时间打开和关闭。试着让以色列、亚利桑那和纽约的三个国家都明白这一点,我敢说!以色列在逾越节和Rosh Hashanah的白天和标准时间之间切换;亚利桑那州不切换,纽约根据美国联邦立法机构的意愿切换。
  • 有一个会话范围的时区设置(SET time_zone = something)。如果不进行设置,则使用上面第3项中的时区表示。如果设置了它,服务器将使用它作为时区,将其内部表示转换为它在查询中发送回的表示。
  • 您可以通过发出SELECT Name from mysql.time_zone_name来获取mysql服务器中可用时区的名称列表。这可以填充下拉菜单,用户可以从中选择时区。如果此查询未返回任何项,请查看https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html的底部。
  • 所以,这意味着您可以将会话时区设置设置为特定用户的时区,然后在该用户的时区中返回所有时间。此外,您的INSERTUPDATE中的任何DATETIMETIMESTAMP项都将从该用户的时区转换为mysql的内部表示,因为它们放在您的表中。

    注意:在具有持久性MySQL连接的Web应用程序中,新用户请求的工作将继承连接的时区设置。别忘了为新用户重置它。

    如果您正在运行一个为多个用户返回本地时间数据的查询,而这些用户恰好位于不同的时区,则不能利用此MySQL每会话功能集。您可以通过为不同的用户运行不同的查询并更改他们之间的时区设置来解决这个问题。

    或者,您可以使用mysql函数

    1
    CONVERT_TZ(datetime,'UTC','user_time_zone')

    或在每件物品上相似。

    另外,Java和dotnet有自己的高质量时区操作系统。因此,您可以选择使用UTC时区设置运行MySQL服务器,并在应用程序中进行所有时区转换。