时区处理完全指南:Java、JavaScript、数据库最佳实践
时区处理是开发中最容易出错的环节之一。本文介绍各平台的时区处理方法和最佳实践。
时区基础
UTC(协调世界时)
UTC 是全球统一的时间标准,所有时区都相对于 UTC。
时区表示
UTC+8: 东八区(中国标准时间)
UTC-5: 西五区(美国东部时间)
IANA 时区数据库
Asia/Shanghai — 中国
America/New_York — 美国东部
Europe/London — 英国
Java 时区处理
Java 8+ API
import java.time.*;
// 获取当前时间(带时区)
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 时区转换
ZonedDateTime tokyoTime = now.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// 格式化
String formatted = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z"));
常用操作
// 获取系统默认时区
ZoneId defaultZone = ZoneId.systemDefault();
// 获取所有可用时区
Set<String> zones = ZoneId.getAvailableZoneIds();
// 时间戳转带时区时间
Instant instant = Instant.ofEpochSecond(1704067200);
ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Shanghai"));
JavaScript 时区处理
原生 API
// 获取当前时间
const now = new Date();
// 获取时区偏移(分钟)
const offset = now.getTimezoneOffset(); // -480(UTC+8)
// 转为 UTC 字符串
now.toISOString(); // '2024-01-01T00:00:00.000Z'
// 转为本地字符串
now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
使用 Intl API
// 指定时区显示
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}).format(new Date());
使用 Moment.js / Day.js
// Day.js(推荐,轻量)
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs().tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
数据库时区处理
MySQL
-- 查看当前时区
SELECT @@global.time_zone, @@session.time_zone;
-- 设置时区
SET GLOBAL time_zone = '+08:00';
SET SESSION time_zone = '+08:00';
-- TIMESTAMP 类型:存储时转换为 UTC
-- DATETIME 类型:存储原始值,不转换
PostgreSQL
-- 查看当前时区
SHOW timezone;
-- 设置时区
SET timezone = 'Asia/Shanghai';
-- TIMESTAMP WITH TIME ZONE:存储时转换为 UTC
-- TIMESTAMP WITHOUT TIME ZONE:存储原始值
最佳实践
1. 存储使用 UTC
// 数据库存储 UTC 时间
ZonedDateTime utcTime = ZonedDateTime.now(ZoneOffset.UTC);
2. 传输使用 ISO 8601
// API 响应
String iso8601 = now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
// "2024-01-01T00:00:00+08:00[Asia/Shanghai]"
3. 显示转换时区
// 前端根据用户时区显示
const userTime = utcTime.toLocaleString('zh-CN', {
timeZone: getUserTimezone()
});
4. 使用 IANA 时区标识
// ✅ 推荐
ZoneId.of("Asia/Shanghai")
// ❌ 不推荐
ZoneId.of("GMT+8")
常见问题
1. 时间显示不一致
原因: 前后端时区不一致
解决: 统一使用 UTC 存储和传输
2. 夏令时问题
原因: 某些地区有夏令时
解决: 使用 IANA 时区数据库,自动处理夏令时
3. 数据库时间错误
原因: 数据库时区配置错误
解决: 统一数据库时区为 UTC
总结
时区处理的最佳实践:存储 UTC,传输 ISO 8601,显示转换本地时区。使用 IANA 时区标识,避免手动计算时区偏移。