如何在时区之间转换时间
时区转换以两种方式出错:偏移量算错,或者忽略夏令时(DST)。纽约冬季是 UTC−5,夏季是 UTC−4。2 月的"下午 5 点 EST"和 7 月的"下午 5 点 EDT"用的是不同的 UTC 偏移量。硬编码偏移量的工具和代码都会出错。
在浏览器中
在时区转换器中输入时间,选择两个时区,即可看到对应时间。转换器使用 IANA 时区数据,夏令时切换自动处理——无需手动调整偏移量。
常见时区的 UTC 偏移量
| 城市 | IANA 时区 | 标准时间偏移 | 夏令时偏移 |
|---|---|---|---|
| 纽约 | America/New_York | UTC−5 | UTC−4 |
| 伦敦 | Europe/London | UTC+0 | UTC+1 |
| 巴黎 | Europe/Paris | UTC+1 | UTC+2 |
| 迪拜 | Asia/Dubai | UTC+4 | — (无夏令时) |
| 孟买 | Asia/Kolkata | UTC+5:30 | — (无夏令时) |
| 新加坡 | Asia/Singapore | UTC+8 | — (无夏令时) |
| 东京 | Asia/Tokyo | UTC+9 | — (无夏令时) |
| 悉尼 | Australia/Sydney | UTC+10 | UTC+11 |
| 北京/上海 | Asia/Shanghai | UTC+8 | — (无夏令时) |
使用 IANA 时区名称(America/New_York),而不是缩写(EST)。EST 有歧义——在北美和澳大利亚指不同的时区。
Python
Python 3.9+ 在标准库中内置了 zoneinfo,无需第三方包。
from datetime import datetime
from zoneinfo import ZoneInfo
# 创建带时区信息的 datetime
ny_time = datetime(2026, 3, 15, 14, 30, tzinfo=ZoneInfo('America/New_York'))
# 转换到其他时区
london = ny_time.astimezone(ZoneInfo('Europe/London'))
tokyo = ny_time.astimezone(ZoneInfo('Asia/Tokyo'))
print(f'纽约: {ny_time:%Y-%m-%d %H:%M %Z}')
print(f'伦敦: {london:%Y-%m-%d %H:%M %Z}')
print(f'东京: {tokyo:%Y-%m-%d %H:%M %Z}')
# 将当前 UTC 时间转换到多个时区
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
now_utc = datetime.now(timezone.utc)
zones = ['America/New_York', 'Europe/London', 'Asia/Singapore', 'Asia/Shanghai']
for tz in zones:
local = now_utc.astimezone(ZoneInfo(tz))
print(f'{tz:<25} {local:%Y-%m-%d %H:%M %Z}')
JavaScript
// 将日期转换到指定时区
function convertToTimezone(date, timezone) {
return new Intl.DateTimeFormat('zh-CN', {
timeZone: timezone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false,
}).format(date);
}
const now = new Date();
console.log(convertToTimezone(now, 'America/New_York'));
console.log(convertToTimezone(now, 'Asia/Shanghai'));
console.log(convertToTimezone(now, 'Asia/Tokyo'));
// 获取任意时区当前的 UTC 偏移量(分钟)
function getUtcOffsetMinutes(timezone, date = new Date()) {
const utc = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const tz = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
return (tz - utc) / 60000;
}
console.log(getUtcOffsetMinutes('America/New_York')); // -300 或 -240,取决于是否夏令时
console.log(getUtcOffsetMinutes('Asia/Shanghai')); // 始终是 480
naive datetime 陷阱
没有时区信息的 Python datetime 是 naive 的——它不了解夏令时。在夏令时切换时对 naive datetime 做计算会得出错误结果。
from datetime import datetime, timedelta
# 错误——美国夏令时切换日的 naive datetime(凌晨 2 点时钟拨快 1 小时)
dt = datetime(2026, 3, 8, 1, 0)
print(dt + timedelta(hours=2)) # 显示 3:00 AM,但本地时间在 2:00 AM 就跳到了 3 AM
# 正确——带时区信息的 datetime
from zoneinfo import ZoneInfo
dt = datetime(2026, 3, 8, 1, 0, tzinfo=ZoneInfo('America/New_York'))
print(dt + timedelta(hours=2)) # 正确处理夏令时的空缺
创建需要做计算的 datetime 时,务必附加时区信息。
存储和传输时间
| 方式 | 问题 |
|---|---|
存储为 EST / PST |
缩写有歧义,未编码夏令时 |
存储为 UTC 偏移(-05:00) |
无法表示未来的夏令时行为 |
| 存储为 UTC + IANA 时区名称 | 正确——无歧义,夏令时自动处理 |
数据库做法:存储 UTC 时间戳,如需展示本地时间则单独存储 IANA 时区名称,展示时再重建本地时间。
要点总结
- 使用 IANA 时区名称(
America/New_York)——EST等缩写在不同大陆有歧义。 - Python 3.9+ 的
zoneinfo自动处理夏令时;现代项目无需pytz。 - Naive datetime 在夏令时边界产生错误结果——务必附加时区信息。
- 迪拜、孟买、新加坡和东京没有夏令时——偏移量全年不变。
- 数据库存 UTC 时间戳;展示时用 IANA 时区名称重建本地时间。