【问题标题】:Using time zones in a PHP web application在 PHP Web 应用程序中使用时区
【发布时间】:2026-02-14 13:50:01
【问题描述】:

我已经寻找了几个小时来寻找在 PHP/MySQL Web 应用程序中使用时区的最佳方式,但很难找到明确的答案。根据我到目前为止所学到的知识,最好将每个人的资料以 UTC 格式存储在数据库中(如果我错了,请纠正我)。

当用户注册时,我会询问他们那里的时区,然后针对那里的用户存储该时区。这将是这种格式的下拉菜单:

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

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

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

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

谢谢

【问题讨论】:

标签: php mysql html timezone utc


【解决方案1】:

在 MySQL 中,你需要做的是:

  1. 将每个用户选择的时区存储在某个位置,当您代表该用户进行数据库查询时可以检索它。您可以将其存储为字符串。
  2. 在您代表特定用户工作(例如,存储或检索约会时间和日期)之前,请执行SET time_zone = (stored time zone setting)

这将导致时区适当地转换为每个人的当地时间。

编辑:

这是因为

  1. MySQL 尝试使用 UTC(通用时间,以前称为格林威治标准时间)将 DATETIME 和 TIMESTAMP 数据项存储在表中。
  2. 只有知道应用程序提供的每个数据项的正确本地时区,它才能正确执行此操作。
  3. 在不关心不同时区的应用程序中,它以 MySQL 服务器范围的方式执行此操作。见https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html。大多数运行多国和多时区应用程序的人将他们的服务器时区设置为 UTC,而不是本地时间,因为它更加更容易整理。
  4. 除非您还知道日期和时区,否则尝试将时间从 UTC 转换为本地时间几乎没有意义,因为本地时间在一年中的不同时间打开和关闭。尝试为以色列、亚利桑那州和纽约的所有三个地区都做到这一点,我敢说!以色列在逾越节和犹太新年在白天和标准时间之间切换;亚利桑那州没有改变,而纽约则随美国联邦立法机构的心血来潮而改变。
  5. 有一个会话范围的 time_zone 设置 (SET time_zone = something)。如果您不设置它,它将使用上面第 3 项中的时区表示。如果您设置它,服务器将使用它作为时区来将其内部表示转换为它在查询中发送回的表示。
  6. 您可以通过发出SELECT Name from mysql.time_zone_name 获取MySQL 服务器中可用时区的名称列表。这可以填充下拉菜单,您的用户可以从中选择她的时区。如果此查询未返回任何项目,请查看 https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html 的底部。

因此,这意味着您可以将会话 time_zone 设置设置为特定用户的时区,然后返回该用户时区的所有时间。此外,任何DATETIMETIMESTAMP 项目都将在您的表中从该用户的时区转换为MySQL 的内部表示。

注意:在具有持久 MySQL 连接的 Web 应用程序中,新用户请求的工作将继承连接的 time_zone 设置。不要忘记为新用户重置它。

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

或者,你可以使用MySQL函数

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

每个项目或类似。

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

【讨论】:

  • 我认为网站上显示的任何时间都显示在用户时区时间?不需要任何魔法?
  • 如果您执行的查询从具有不同时区的 2 个用户那里获取时间 - 您会怎么做?
  • 我更喜欢根据用户配置文件时区参数在 PHP 中处理时区。 PHP 更加精细,因为每个 DateTime 对象都可以有自己的时区。 MySQL 通过设置和重置环境连接变量,强制您对每个 MySQL 值使用相同的时区。此外,在大多数大型 Web 应用程序中,MySQL 计算昂贵且稀缺,但 PHP 处理丰富且便宜。我在回答这个问题时提供了另一种方式。
【解决方案2】:

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

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

MySQL 和 PHP 都在配置文件中设置了默认时区。 MySQL 对每个数据库连接使用datetime set in the config file,除非用户在使用命令SET time_zone = [timezone]; 启动连接后指定不同的时区。 PHP 还使用服务器配置文件中设置的时区为每个脚本设置了一个时区环境变量,并且可以在脚本启动后使用 PHP 函数date_default_timezone_set() 覆盖该环境变量。

PHP DateTime 类有一个名为timezone 的属性,即PHP DateTimeZone object。 DateTimeZone 对象是使用字符串指定的确切时区。 list of timezones 是全面的,在全球拥有数百个单独的时区。 PHP 时区将自动考虑夏令时。

当用户在 Web 应用程序中生成日期时间时,在用户个人资料的时区中构造一个 PHP 日期时间对象。然后使用setTimezone 方法将DateTime 对象修改为UTC 时区。现在您有了 UTC 格式的用户日期时间,您可以将值存储在数据库中。使用 DateTimeformat 方法将数据表示为 MySQL 接受的格式的字符串。

所以用户生成了一个日期时间,你在用户指定的时区创建一个 PHP 日期时间对象:

// 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 构造,然后转换为用户的时区。

$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 日期类型来存储日期时间值,而不是时间戳数据类型。时间戳数据类型在手册here 中有介绍。

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

【讨论】:

  • 这会自动更改日期吗?假设 8 月 10 日凌晨 1:00 在英国举行会议,如果我邀请来自美国的某人,是否会在休假后自动在正确的时区和 8 月 9 日显示该会议?
  • 是的,在这种情况下,日期将从 8 月 10 日更改为 8 月 9 日。我建议做一些测试。在英国做一个 PHP 日期时间,var_dump 值,更改对象的时区,然后再次 var_dump。日期时间既是日期又是时间,它们是 PHP 对象不可分割的属性。
  • 太棒了!日期和时间可以单独保存还是必须一栏保存到数据库中?
  • 我建议在 MySQL DATETIME 数据类型中保存任何同时具有日期和时间的值。当只需要这些部分时,MySQL 还具有 DATE 数据类型和 TIME 数据类型。 The manual explains here。我可以投票吗?
  • 我刚试过这些,插入数据库时​​效果很好。但是,当我使用您的第二个查询从数据库中读取数据时,它似乎不起作用。这是我尝试过的代码:$startdatetimeobject=new DateTime($row['StartDateAndTime'], new DateTimeZone('UTC'); $startdatetimeobject-&gt;setTimezone($user_timezone); $start = $startdatetimeobject-&gt;format('m/d/Y g:i a');