您可以使用系统范围的时区数据库并对其进行自定义更新。
IANA 的官方 TZ 数据库存储库
它可以作为源文件提供,您可以在需要时对其进行修改并重新编译成可供时间函数使用的二进制文件,glibc
因此所有使用这些时间函数的程序也可以使用。如果您的发行版执行此类更新,您可能需要找到一种方法来禁用对该数据库的任何自动更新,否则您将丢失自定义更改。
我不确定 PHP 是否真的遵循系统范围的 TZ 数据库。PECLtimezonedb
包似乎表明它是 IANA 数据库的单独重新编译版本,因此它可能不会观察到系统范围数据库的变化。
如果 PHP 忽略系统范围数据库中的更改,您可以通过调用任何使用glibc
时区感知时间函数并支持返回时区偏移量的外部程序来检索时区偏移量,例如:
putenv('TZ=America/New_York');
$offset = exec('date +%z');
$offset
将是 format ±HHMM
,因此需要进行一些额外的解析。
您还可以找到与当前时间不同的特定时间戳的正确偏移量:
$unix_timestamp = 1350757594;
$offset = exec('date +%z --date=@'.$unix_timestamp);
当然,escapeshellarg
如果时间戳来自不受信任的来源,则需要进行适当的过滤。
更新:这是单个 DST 规则集的解决方案;不使用外部资源
function calculate_offset($ts) {
static $standard_offset = 2; // = +2:00
static $dst_offset = 3; // = +3:00
static $dst_start = 'last Sunday of March %d 1:00';
static $dst_end = 'last Sunday of October %d 1:00';
date_default_timezone_set('UTC');
$y = idate('Y', $ts);
$start = strtotime(sprintf($dst_start, $y));
$end = strtotime(sprintf($dst_end, $y));
if ($start < $end) {
// Northern hemisphere, DST starts earlier in the year than it ends
$offset = ($ts >= $start && $ts < $end)? $dst_offset: $standard_offset;
} else {
// Southern hemisphere, DST ends earlier in the year than it starts
$offset = ($ts >= $end && $ts < $start)? $standard_offset: $dst_offset;
}
$utc_time = strftime('%Y/%m/%d %H:%M:%S', $ts);
$local_time = strftime('%Y/%m/%d %H:%M:%S', $ts + $offset * 60 * 60);
printf("TS: %d; Offset: +%02d:00; UTC: %s; Local: %s\n", $ts, $offset, $utc_time, $local_time);
}
示例代码使用标准欧洲 DST 规则和东欧时间偏移。您将需要找到一种方法来以以下格式表示您的 DST 开始和结束规则strtotime
:相对格式。您的 DST 开始/结束规则中的A%d
将被您的时间戳所在的年份所取代,因此每月工作日规则将找到特定年份的正确日期。所有计算均以 UTC 时间完成,因此您的规则应参考 UTC 中的 DST 开始/结束时间。