PHP timezone_offset_get()函数

timezone_offset_get() 函数DateTimeZone 类的方法,用于返回相对于 GMT 的时区偏移量。这个函数需要传入一个 DateTime 对象作为参数,以便计算特定时间点的偏移量(考虑夏令时等因素)。

注意:这不是一个独立的函数,而是 DateTimeZone 对象的实例方法,需要通过 DateTimeZone 对象调用。

语法

DateTimeZone::getOffset(DateTimeInterface $datetime) : int

参数说明

参数 描述
datetime

必需。 一个 DateTimeInterface 对象(通常是 DateTime 对象),表示要计算偏移量的时间点。

由于时区偏移量可能因夏令时而变化,所以需要指定具体的时间。

返回值

返回相对于 GMT 的偏移量(以秒为单位)。正值表示东时区,负值表示西时区。

示例

示例 1:基本用法

<?php
// 创建 DateTimeZone 对象
$timezone = new DateTimeZone('America/New_York');

// 创建当前时间的 DateTime 对象
$datetime = new DateTime('now');

// 获取时区偏移量
$offset = $timezone->getOffset($datetime);

echo "时区: " . $timezone->getName() . "<br>";
echo "当前时间: " . $datetime->format('Y-m-d H:i:s') . "<br>";
echo "偏移量: " . $offset . " 秒<br>";
echo "偏移量: " . ($offset / 3600) . " 小时<br>";

// 输出类似:
// 时区: America/New_York
// 当前时间: 2023-08-15 10:30:45
// 偏移量: -14400 秒
// 偏移量: -4 小时
?>

示例 2:比较不同时区的偏移量

<?php
// 比较不同时区在同一时间的偏移量
$datetime = new DateTime('2023-08-15 10:30:45');

$timezones = [
    'America/New_York',
    'Asia/Shanghai',
    'Europe/London',
    'Australia/Sydney',
    'UTC',
    'Pacific/Honolulu'
];

echo "<h4>比较不同时区在 " . $datetime->format('Y-m-d H:i:s') . " 的偏移量:</h4>";
echo "<table border='1'>";
echo "<tr><th>时区</th><th>偏移量(秒)</th><th>偏移量(小时)</th><th>GMT偏移</th></tr>";

foreach ($timezones as $tz) {
    $timezone = new DateTimeZone($tz);
    $offset = $timezone->getOffset($datetime);
    $hours = $offset / 3600;

    echo "<tr>";
    echo "<td>" . $tz . "</td>";
    echo "<td>" . $offset . "</td>";
    echo "<td>" . $hours . "</td>";
    echo "<td>GMT" . ($hours >= 0 ? '+' : '') . $hours . "</td>";
    echo "</tr>";
}

echo "</table>";

// 输出类似:
// 比较不同时区在 2023-08-15 10:30:45 的偏移量:
// 时区            偏移量(秒)  偏移量(小时)  GMT偏移
// America/New_York  -14400      -4          GMT-4
// Asia/Shanghai     28800       8           GMT+8
// Europe/London     3600        1           GMT+1
// Australia/Sydney  36000       10          GMT+10
// UTC               0           0           GMT+0
// Pacific/Honolulu  -36000      -10         GMT-10
?>

示例 3:夏令时对偏移量的影响

<?php
// 演示夏令时对偏移量的影响
$timezone = new DateTimeZone('America/New_York');

// 测试不同时间的偏移量
$dates = [
    '2023-01-15 10:30:45',  // 冬季,非夏令时
    '2023-07-15 10:30:45',  // 夏季,夏令时
    '2023-03-12 01:30:45',  // 夏令时开始前后
    '2023-11-05 01:30:45'   // 夏令时结束前后
];

echo "<h4>纽约时区的夏令时影响:</h4>";
foreach ($dates as $date_str) {
    $datetime = new DateTime($date_str, $timezone);
    $offset = $timezone->getOffset($datetime);
    $hours = $offset / 3600;

    echo "时间: " . $datetime->format('Y-m-d H:i:s') . "<br>";
    echo "偏移量: " . $offset . " 秒 (" . $hours . " 小时)<br>";
    echo "是否为夏令时: " . ($datetime->format('I') ? '是' : '否') . "<br>";
    echo "时区缩写: " . $datetime->format('T') . "<br>";
    echo "<hr>";
}

// 计算夏令时切换的具体时间
$year = 2023;
$transitions = $timezone->getTransitions(
    strtotime("$year-01-01"),
    strtotime(($year+1) . "-01-01")
);

echo "<h4>2023年夏令时转换:</h4>";
foreach ($transitions as $transition) {
    if (isset($transition['isdst'])) {
        $time = date('Y-m-d H:i:s', $transition['ts']);
        $isdst = $transition['isdst'] ? '是' : '否';
        $abbr = $transition['abbr'];
        $offset = $transition['offset'];
        echo "时间: $time, 夏令时: $isdst, 缩写: $abbr, 偏移: " . ($offset/3600) . "小时<br>";
    }
}

// 输出类似:
// 纽约时区的夏令时影响:
// 时间: 2023-01-15 10:30:45
// 偏移量: -18000 秒 (-5 小时)
// 是否为夏令时: 否
// 时区缩写: EST
//
// 时间: 2023-07-15 10:30:45
// 偏移量: -14400 秒 (-4 小时)
// 是否为夏令时: 是
// 时区缩写: EDT
//
// 2023年夏令时转换:
// 时间: 2023-03-12 07:00:00, 夏令时: 是, 缩写: EDT, 偏移: -4小时
// 时间: 2023-11-05 06:00:00, 夏令时: 否, 缩写: EST, 偏移: -5小时
?>

示例 4:计算两个时区之间的时间差

<?php
// 计算两个时区之间的时间差
function getTimezoneDifference($timezone1, $timezone2, $datetime = null) {
    if ($datetime === null) {
        $datetime = new DateTime('now');
    }

    $tz1 = new DateTimeZone($timezone1);
    $tz2 = new DateTimeZone($timezone2);

    $offset1 = $tz1->getOffset($datetime);
    $offset2 = $tz2->getOffset($datetime);

    $difference = $offset2 - $offset1; // 秒
    $difference_hours = $difference / 3600;

    return [
        'timezone1' => $timezone1,
        'timezone2' => $timezone2,
        'offset1' => $offset1,
        'offset2' => $offset2,
        'difference' => $difference,
        'difference_hours' => $difference_hours
    ];
}

// 测试
$datetime = new DateTime('2023-08-15 10:30:45');
$comparisons = [
    ['America/New_York', 'Asia/Shanghai'],
    ['Europe/London', 'Australia/Sydney'],
    ['America/Los_Angeles', 'America/New_York'],
    ['UTC', 'Asia/Tokyo']
];

echo "<h4>时区时间差比较:</h4>";
foreach ($comparisons as $pair) {
    $result = getTimezoneDifference($pair[0], $pair[1], $datetime);
    echo "<strong>" . $result['timezone1'] . " 与 " . $result['timezone2'] . ":</strong><br>";
    echo "偏移量: " . ($result['offset1']/3600) . "h 与 " . ($result['offset2']/3600) . "h<br>";
    echo "时差: " . $result['difference_hours'] . " 小时<br>";

    if ($result['difference_hours'] > 0) {
        echo $result['timezone2'] . " 比 " . $result['timezone1'] . " 快 " . abs($result['difference_hours']) . " 小时<br>";
    } else {
        echo $result['timezone2'] . " 比 " . $result['timezone1'] . " 慢 " . abs($result['difference_hours']) . " 小时<br>";
    }
    echo "<hr>";
}

// 输出类似:
// 时区时间差比较:
// America/New_York 与 Asia/Shanghai:
// 偏移量: -4h 与 8h
// 时差: 12 小时
// Asia/Shanghai 比 America/New_York 快 12 小时
//
// Europe/London 与 Australia/Sydney:
// 偏移量: 1h 与 10h
// 时差: 9 小时
// Australia/Sydney 比 Europe/London 快 9 小时
?>

示例 5:将时间转换为不同时区

<?php
// 将时间转换为不同时区
function convertTimeBetweenTimezones($time, $from_timezone, $to_timezone) {
    $datetime = new DateTime($time, new DateTimeZone($from_timezone));
    $datetime->setTimezone(new DateTimeZone($to_timezone));

    return [
        'original' => $time,
        'from_timezone' => $from_timezone,
        'to_timezone' => $to_timezone,
        'converted' => $datetime->format('Y-m-d H:i:s'),
        'offset_from' => (new DateTimeZone($from_timezone))->getOffset($datetime) / 3600,
        'offset_to' => (new DateTimeZone($to_timezone))->getOffset($datetime) / 3600
    ];
}

// 测试
$time = '2023-08-15 10:30:45';
$conversions = [
    ['America/New_York', 'Asia/Shanghai'],
    ['Europe/London', 'America/Los_Angeles'],
    ['UTC', 'Australia/Sydney']
];

echo "<h4>时区时间转换:</h4>";
foreach ($conversions as $conversion) {
    $result = convertTimeBetweenTimezones($time, $conversion[0], $conversion[1]);
    echo "<strong>原始时间:</strong> " . $result['original'] . " (" . $result['from_timezone'] . ", UTC" .
         ($result['offset_from'] >= 0 ? '+' : '') . $result['offset_from'] . ")<br>";
    echo "<strong>转换后:</strong> " . $result['converted'] . " (" . $result['to_timezone'] . ", UTC" .
         ($result['offset_to'] >= 0 ? '+' : '') . $result['offset_to'] . ")<br>";
    echo "<hr>";
}

// 输出类似:
// 时区时间转换:
// 原始时间: 2023-08-15 10:30:45 (America/New_York, UTC-4)
// 转换后: 2023-08-15 22:30:45 (Asia/Shanghai, UTC+8)
//
// 原始时间: 2023-08-15 10:30:45 (Europe/London, UTC+1)
// 转换后: 2023-08-15 02:30:45 (America/Los_Angeles, UTC-7)
?>

示例 6:处理历史偏移量变化

<?php
// 查看时区偏移量的历史变化
function getTimezoneOffsetHistory($timezone, $start_year, $end_year) {
    $tz = new DateTimeZone($timezone);

    $history = [];
    for ($year = $start_year; $year <= $end_year; $year++) {
        // 检查每年的几个关键时间点
        $key_dates = [
            "$year-01-01 00:00:00",  // 年初
            "$year-07-01 00:00:00",  // 年中
        ];

        foreach ($key_dates as $date) {
            $datetime = new DateTime($date, $tz);
            $offset = $tz->getOffset($datetime);
            $hours = $offset / 3600;

            $history[] = [
                'date' => $date,
                'offset' => $offset,
                'hours' => $hours,
                'isdst' => $datetime->format('I'),
                'abbr' => $datetime->format('T')
            ];
        }
    }

    return $history;
}

// 获取纽约时区的历史偏移量
$history = getTimezoneOffsetHistory('America/New_York', 2018, 2023);

echo "<h4>纽约时区历史偏移量 (2018-2023):</h4>";
echo "<table border='1'>";
echo "<tr><th>日期</th><th>偏移量(小时)</th><th>夏令时</th><th>缩写</th></tr>";

foreach ($history as $record) {
    echo "<tr>";
    echo "<td>" . $record['date'] . "</td>";
    echo "<td>" . $record['hours'] . "</td>";
    echo "<td>" . ($record['isdst'] ? '是' : '否') . "</td>";
    echo "<td>" . $record['abbr'] . "</td>";
    echo "</tr>";
}
echo "</table>";

// 输出类似:
// 纽约时区历史偏移量 (2018-2023):
// 日期                 偏移量(小时)  夏令时  缩写
// 2018-01-01 00:00:00  -5           否       EST
// 2018-07-01 00:00:00  -4           是       EDT
// 2019-01-01 00:00:00  -5           否       EST
// 2019-07-01 00:00:00  -4           是       EDT
// ... 更多
?>

示例 7:错误处理

<?php
// 错误处理示例
function safeGetOffset($timezone_name, $datetime_str = 'now') {
    try {
        $timezone = new DateTimeZone($timezone_name);
        $datetime = new DateTime($datetime_str, $timezone);

        $offset = $timezone->getOffset($datetime);
        $hours = $offset / 3600;

        return [
            'success' => true,
            'timezone' => $timezone_name,
            'datetime' => $datetime->format('Y-m-d H:i:s'),
            'offset' => $offset,
            'hours' => $hours
        ];
    } catch (Exception $e) {
        return [
            'success' => false,
            'error' => $e->getMessage()
        ];
    }
}

// 测试
$test_cases = [
    ['timezone' => 'America/New_York', 'datetime' => '2023-08-15 10:30:45'],
    ['timezone' => 'Invalid/Timezone', 'datetime' => '2023-08-15 10:30:45'],
    ['timezone' => 'Asia/Shanghai', 'datetime' => 'invalid datetime'],
    ['timezone' => 'UTC', 'datetime' => 'now']
];

echo "<h4>错误处理测试:</h4>";
foreach ($test_cases as $case) {
    $result = safeGetOffset($case['timezone'], $case['datetime']);

    echo "<strong>测试:</strong> " . $case['timezone'] . " at " . $case['datetime'] . "<br>";
    if ($result['success']) {
        echo "<span style='color: green;'>✓ 成功: 偏移量 = " . $result['hours'] . " 小时</span>";
    } else {
        echo "<span style='color: red;'>✗ 错误: " . $result['error'] . "</span>";
    }
    echo "<br><br>";
}

// 输出类似:
// 错误处理测试:
// 测试: America/New_York at 2023-08-15 10:30:45
// ✓ 成功: 偏移量 = -4 小时
//
// 测试: Invalid/Timezone at 2023-08-15 10:30:45
// ✗ 错误: DateTimeZone::__construct(): Unknown or bad timezone (Invalid/Timezone)
//
// 测试: Asia/Shanghai at invalid datetime
// ✗ 错误: DateTime::__construct(): Failed to parse time string (invalid datetime) at position 0 (i): The timezone could not be found in the database
//
// 测试: UTC at now
// ✓ 成功: 偏移量 = 0 小时
?>

常见时区偏移量参考

时区 标准时间偏移 (GMT) 夏令时偏移 (GMT) 使用夏令时 主要地区
America/New_York -5 -4 纽约、华盛顿、迈阿密
America/Chicago -6 -5 芝加哥、达拉斯、休斯顿
America/Denver -7 -6 丹佛、凤凰城、盐湖城
America/Los_Angeles -8 -7 洛杉矶、旧金山、西雅图
Europe/London 0 +1 伦敦、都柏林、爱丁堡
Europe/Paris +1 +2 巴黎、柏林、罗马、马德里
Asia/Shanghai +8 +8 北京、上海、广州、深圳
Asia/Tokyo +9 +9 东京、大阪、名古屋
Australia/Sydney +10 +11 悉尼、墨尔本、布里斯班
UTC 0 0 全球标准

注意事项

  • getOffset() 返回的是相对于 GMT 的偏移量,不是相对于 UTC(两者在实践中通常相同)
  • 偏移量以秒为单位,正值表示东时区,负值表示西时区
  • 由于夏令时,同一时区在不同时间的偏移量可能不同
  • 某些时区(如 Asia/Shanghai)不实行夏令时,偏移量全年不变
  • 时区偏移量可能因历史政策变化而改变
  • 处理时区转换时,要考虑边界情况(如夏令时切换的时刻)

最佳实践

场景 建议
计算时区偏移 总是提供具体的 DateTime 对象,以考虑夏令时
存储时间戳 使用 UTC 时间戳,在显示时转换为本地时区
比较不同时区时间 先转换为同一时区(通常是 UTC)再比较
处理用户输入 记录用户时区,在显示时间时进行转换
计算时间差 考虑时区偏移和夏令时影响
安排跨时区事件 明确指定时区,避免歧义

相关函数和类