我正在开发一个由使用 PHP 和 MySQL 的 Web 服务器组成的应用程序,该服务器在 Windows 上运行,用户可以通过设置为
http://localhost
的 Web 浏览器访问该应用程序。但我希望它适用于美国所有时区,包括亚利桑那州大部分不遵守夏令时的地区。要配置此脚本,我需要使 PHP 使用与 Windows 相同的时区。
我有一个 PHP 脚本,可以从 Windows 上的命令行获取时区。然后需要将结果转换为 PHP 列表中的等效时区。
不幸的是,我没有看到一个简单的方法来做到这一点。这是脚本:
$comp_timezone = exec('systeminfo | findstr /C:"Time Zone"');
// results are: "Time Zone: (UTC-07:00) Arizona"
date_default_timezone_set($comp_timezone);
我需要做的是将 $comp_timezone
的字符串结果转换为函数 date_default_timezone_set
可以使用的内容,但如上面的示例所示,亚利桑那州不作为 php 的时区存在,最接近的时区是 America/Phoenix
。
那么如何将
systeminfo
时区转换为 PHP 时区?
一些事情:
systeminfo
命令仅向您显示Windows时区的显示名称。您将需要标识符。您可以使用 tzutil.exe /g
返回此信息 - 尽管这仍然对您没有帮助。PHP 使用 IANA 时区,而不是 Windows 时区。有关两种不同类型时区的更多信息,请参阅时区标签 wiki。
对于 PHP,您可以通过 Internationalization Package 访问 CLDR 数据。通过添加(或取消注释)到您的
php.ini
文件来启用此功能:
extension=php_intl.dll
然后您可以简单地调用以下函数来返回本地时区作为 IANA 标识符。 (在内部,它依赖于ICU和CLDR来翻译。)
IntlTimeZone::createDefault()->getID()
这将在您的示例中返回
"America/Phoenix"
。然后,您可以将它与任何 PHP 的正常时区函数一起使用,包括 date_default_timezone_set
(如果您愿意)。即使您可以这样做,请仔细考虑一下您是否真的应该这样做。在大多数情况下,依赖 Web 应用程序(或任何服务器端应用程序)的系统本地时区设置是一种反模式。改变时区太容易了。这是一个全局设置,通常不需要任何管理员权限。如果您的数据不是基于 UTC,然后您尝试将其从一台服务器迁移到另一台服务器,也会出现问题。在绝大多数情况下,最好在应用程序逻辑级别考虑时区,而不是系统范围或应用程序范围,并且托管代码的服务器的时区通常应被视为不相关。
我的解决方案是循环
DateTimeZone::listIdentifiers()
并尝试找到匹配项,即:.
$comp_timezone = exec('systeminfo | findstr /C:"Time Zone"');
//Time Zone: (UTC +01:00) Lisbon
//we match the last word of $comp_timezone
preg_match_all('/(\w+)$/', $comp_timezone, $rawTz, PREG_PATTERN_ORDER);
$rawTz = $rawTz[1][0]; //Lisbon
foreach (DateTimeZone::listIdentifiers() as $timezone) {
if(preg_match("/.*?\/$rawTz/", $timezone)){
echo $timezone;
//Europe/Lisbon
}
}
备注:
America/Arizona
在 DateTimeZone
中不作为有效 PHP
存在。DateTimeZone
仅适用于 PHP 5 >= 5.2.0, PHP 7
Windows 附带 自己的时区列表(例如:
W. Europe Standard Time
)。
PHP 使用 IANA 时区数据库(也称为 tz 数据库甚至 Olson 数据库)。
Unicode CLDR 提供 XML 文件,允许映射 IANA 名称和 Windows 名称。 例如,请参阅以下 XML 元素:
<!-- ... -->
<mapZone other="W. Europe Standard Time" territory="001" type="Europe/Berlin"/>
<mapZone other="W. Europe Standard Time" territory="AD" type="Europe/Andorra"/>
<!-- ... -->
如您所见,一个 Windows 时区名称可能对应多个 IANA 时区名称。
我编写了一个脚本,它创建了代表两个常量的 PHP 代码,这两个常量允许相当轻松地解析名称,解析上面的 XML。
以下是此类常量的摘录:
const WINDOWS_TO_PHP = [
// ...
'W. Europe Standard Time' => ['Europe/Andorra', 'Europe/Berlin', /*... */],
// ...
];
const PHP_TO_WINDOWS = [
// ...
'Europe/Andorra' => 'W. Europe Standard Time',
'Europe/Berlin' => 'W. Europe Standard Time',
// ...
];
通过使用这些常量,您只需编写即可检索 PHP 时区名称
$phpTimeZoneNames = WINDOWS_TO_PHP['W. Europe Standard Time'] ?? [];
并检索从 PHP 代码名称开始的 Windows 时区名称:
$windowsTimeZoneName = PHP_TO_WINDOWS['Europe/Andorra'] ?? '';