Skip to content

DateTimeZone 时区处理

DateTimeZone 类封装了时区信息,是 DateTime 操作的重要组成部分。正确的时区处理对于国际化应用、日志记录和定时任务至关重要。本节将讲解 PHP 时区的创建、转换和使用。

前置知识

阅读本节前,建议先了解:DateTime 类

基础概念

PHP 时区设置

php
<?php
declare(strict_types=1);

// php.ini 设置默认时区
// date.timezone = Asia/Shanghai

// 运行时设置
date_default_timezone_set('Asia/Shanghai');

// 获取当前默认时区
echo "默认时区: " . date_default_timezone_get() . PHP_EOL;

// 获取所有支持的时区列表
$zones = DateTimeZone::listIdentifiers();
echo "支持 " . count($zones) . " 个时区" . PHP_EOL;

// 按地区获取时区
$asiaZones = DateTimeZone::listIdentifiers(DateTimeZone::ASIA);
$europeZones = DateTimeZone::listIdentifiers(DateTimeZone::EUROPE);

创建 DateTimeZone

php
<?php
declare(strict_types=1);

// 从时区标识符创建
$shanghai = new DateTimeZone('Asia/Shanghai');
$tokyo = new DateTimeZone('Asia/Tokyo');
$utc = new DateTimeZone('UTC');
$est = new DateTimeZone('America/New_York');

// 从缩写创建(不推荐,可能有歧义)
// $cst = new DateTimeZone('CST'); // 可能是 Chicago 或 Cuba 等

// 从偏移量创建
$offset = new DateTimeZone('+08:00');
$offsetNeg = new DateTimeZone('-05:00');

时区转换

php
<?php
declare(strict_types=1);

// 创建 UTC 时间
$utcTime = new DateTimeImmutable('2024-01-15 14:30:00', new DateTimeZone('UTC'));
echo "UTC: " . $utcTime->format('Y-m-d H:i:s T') . PHP_EOL;

// 转换为北京时间
$shanghai = $utcTime->setTimezone(new DateTimeZone('Asia/Shanghai'));
echo "上海: " . $shanghai->format('Y-m-d H:i:s T') . PHP_EOL;

// 转换为东京时间
$tokyo = $utcTime->setTimezone(new DateTimeZone('Asia/Tokyo'));
echo "东京: " . $tokyo->format('Y-m-d H:i:s T') . PHP_EOL;

// 转换为纽约时间
$ny = $utcTime->setTimezone(new DateTimeZone('America/New_York'));
echo "纽约: " . $ny->format('Y-m-d H:i:s T') . PHP_EOL;

// 输出多个时区的当前时间
$zones = ['UTC', 'Asia/Shanghai', 'Asia/Tokyo', 'America/New_York', 'Europe/London'];
$now = new DateTimeImmutable('now', new DateTimeZone('UTC'));

foreach ($zones as $zoneName) {
    $zoned = $now->setTimezone(new DateTimeZone($zoneName));
    echo "{$zoneName}: " . $zoned->format('Y-m-d H:i:s e') . PHP_EOL;
}

时区偏移信息

php
<?php
declare(strict_types=1);

$zone = new DateTimeZone('Asia/Shanghai');

// 获取偏移量(秒)
$offset = $zone->getOffset(new DateTime('2024-01-15'));
echo "偏移秒数: {$offset}" . PHP_EOL;        // 28800 (8小时)
echo "偏移小时: " . ($offset / 3600) . PHP_EOL;  // 8

// 获取时区名称
echo "时区名: " . $zone->getName() . PHP_EOL;

// 获取时区缩写
$transitions = $zone->getTransitions(time() - 86400, time() + 86400);
if (!empty($transitions)) {
    echo "缩写: " . $transitions[0]['abbr'] . PHP_EOL;
    echo "是否 DST: " . ($transitions[0]['isdst'] ? '是' : '否') . PHP_EOL;
    echo "偏移: " . $transitions[0]['offset'] . PHP_EOL;
}

跨时区计算

php
<?php
declare(strict_types=1);

/**
 * 获取两个时区之间的时差
 */
function getTimezoneDiff(string $tz1, string $tz2, string $date = 'now'): int
{
    $zone1 = new DateTimeZone($tz1);
    $zone2 = new DateTimeZone($tz2);

    $dt = new DateTimeImmutable($date, $zone1);
    $converted = $dt->setTimezone($zone2);

    $offset1 = $zone1->getOffset($dt);
    $offset2 = $zone2->getOffset($converted);

    return ($offset2 - $offset1) / 3600;
}

echo "上海-纽约时差: " . getTimezoneDiff('Asia/Shanghai', 'America/New_York') . " 小时" . PHP_EOL;

注意事项

  • 使用 IANA 时区标识符(如 Asia/Shanghai),不要用缩写
  • setTimezone() 不会改变时间点,只是切换显示时区
  • date_default_timezone_set() 是全局设置,影响所有日期函数

下一节

继续学习:日期格式化与解析

参考链接