2

任务是什么?
根据项目规范,我需要计算特定时区偏移的 DST 规则。规则不能通过标准工具应用,date_default_timezone_set()因为我们不能依赖软件配置,如果某些 DST 规则将被更改,也无法更新它。

是)我有的?
在 DST 切换日期在上周且上周的切换日在下个月的情况下,我需要减少上周的数字。现在我只有一个丑陋的解决方案:

 $week = 5;
 if (
     (1 + 7 * ($week - 1)) 
     + $start['day_of_week'] 
     - date('w', mktime(0, 0, 0, $start['month'], 1, date('Y', $start['timestamp']))) 
     > 
     date('t', $start['month'])) 
 {
     $week--;
 }

我在找什么?
我正在寻找最简单的解决方案的任何想法,或链接=)

4

1 回答 1

1

您可以使用系统范围的时区数据库并对其进行自定义更新。

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 开始/结束时间。

于 2012-11-01T08:17:07.640 回答