【工作杂谈_20260731】数据平台与工程治理: MySQL 时区转换:UTC、北京时间与 PST/PDT 的对齐

记录在 Grafana、MySQL 和 Azure 跨时区场景中排查 create_time 展示差异的过程,并梳理 UTC、北京时间、PST/PDT 之间的转换关系与实践建议。

全文共计3330字 次阅读

1、问题引入

先介绍一下上游数据:L1事件明细,记录了Web 侧事件级遥测数据,包含用户行为及部分系统/测试/机器人事件。此文件每一个小时记录会生成一个,每个大小为5-30TB左右。

假设当前时间为curr_time,我们需要得到curr_time-2h, curr_time-1h, curr_time, curr_time+1h共计四个文件才能开始当前时间为curr_time的计算任务。每两个小时开始一个这样的计算任务,也就是说一天需要完成12个并生成对应12个文件,每个计算任务完成都会在数据库插入一条记录。

目前的保障措施:因为上游数据数据达到的时间不确定,所以如果想保证计算任务顺利执行,在设计ETL流程时会根据当前时间预判可能的到达时间,并wait10h,在此期间如果需要的所有上游文件都到达就会触发计算任务。同时增加了对上游数据的监控,在规定时间范围内未到达就会触发通知。

但这样做还是有一个遗漏之处,就是是上游文件在规定时间范围内到达,但是由于计算任务时间过长或者计算量太大,就会导致计算任务无法准时完成。而目前监控计算任务是否完成只能通过肉眼去查文件是否生成。

因此引入我的任务:在grafana中新建一个dashboard来可视化计算任务的结果,用来判断计算任务是否完成 实现过程也很简单:去查对应时间的数据库内是否有计算任务完成的记录,构造本应在一天中生成的12个文件行,将其与数据库进行Join,对的上的标记为存在,对不上的标记为不存在。

在实现的过程中我发现自己踩了时区转化的坑,导致dashboard中展示的计算结果的create_time和实际create_time不同,具体踩坑点如下:

  • grafana的dashboard中有时间范围筛选,其默认时区为北京时间

  • Mysql数据库的时区为UTC时间

  • 计算任务生成的结果存储在Azure云上,其时区为PDT时间

这样就导致了同一条记录在不同的地方查询显示/操作的create_time都是不同的,所以此文章介绍一下时区转换

2、时间解析

2.1 什么是 UTC 时间

2.1.1 UTC 是全球统一的时间基准

UTC 全称:

Coordinated Universal Time,协调世界时

它不是某个国家或城市的地方时间,而是全球系统进行时间同步时使用的基准。

常见表示方式:2026-07-31T06:30:00Z

  • 2026-07-31:日期
  • T:日期和时间的分隔符
  • 06:30:00:时、分、秒
  • Z:表示 UTC,也叫 Zulu Time

2.1.2 UTC 和 GMT 的关系

日常业务中,UTC+0GMT+0两者通常可以视为相同偏移:

但概念上并不完全相同:

  • UTC:Coordinated Universal Time,现代时间标准,基于原子钟等时间体系。
  • GMT: Greenwich Mean Time,格林尼治平均时,最初基于天文观测,也常被用作时区名称。

对于日志、数据库、API、Pipeline 和告警系统,建议统一使用 UTC,而不是 GMT。


2.2 什么是北京时间CST

北京时间CST,通常指:

China Standard Time(CST) ,常表示为 UTC+08:00

换算关系:

UTC: 2026-07-31 16:30,对应北京时间: 2026-08-01 00:30(注意,减去或加上 8 小时后,日期也可能变化

2.2.1 北京时间没有夏令时切换

目前中国标准时间全年固定为:UTC+8

因此,在当前规则下:

  • 夏天是 UTC+8
  • 冬天也是 UTC+8
  • 不存在北京时间突然跳过一小时或重复一小时的问题

不过,跨时区项目中不要把它简单写成模糊的 CST。因为 CST 可能被解释为:

  • China Standard Time
  • Central Standard Time
  • Cuba Standard Time

更明确的写法是:UTC+08:00Asia/Shanghai


2.3 什么是 PST 和 PDT

PST 和 PDT 都与北美太平洋时区有关,他们是北美太平洋时区在不同月份范围的细分时区,但是可以统称为Pacific Time(缩写为PT),即PT = PST 或 PDT

按美国现行的一般规则,夏令时从每年 3 月第二个星期日开始,到 11 月第一个星期日结束。这个时间范围内采用PDT时区,剩余时间范围采用PST时区。

2.3.1 PST:Pacific Standard Time

太平洋标准时间 Pacific Standard Time (缩写PST),主要对应不使用夏令时(DST)的时期

  • UTC 偏移:UTC-8(比 UTC 慢 8 小时)

  • 使用地区:美国西海岸(加州、华盛顿州、俄勒冈州、内华达州等)、加拿大不列颠哥伦比亚省,主要城市包括洛杉矶、旧金山、西雅图、波特兰、温哥华

  • 启用时段:每年 11 月第一个星期日到次年 3 月第二个星期日(即冬令时)

  • 夏令时切换:3 月第二个星期日起切换为 PDT(UTC-7),11 月第一个星期日回切到 PST

2.3.2 PDT:Pacific Daylight Time

太平洋夏令时间 Pacific Daylight Time(PDT) 在实行夏令时期间使用

  • UTC 偏移:PDT = UTC-7(比 UTC 晚 7 小时)

  • 使用地区:美国西海岸(加州、华盛顿州、内华达州、俄勒冈州等)、加拿大不列颠哥伦比亚省,主要城市包括洛杉矶、旧金山、西雅图、波特兰、温哥华等

  • 启用时段:每年 3 月第二个星期日11 月第一个星期日

  • 冬令时切换:夏令时结束后切换回 PST(Pacific Standard Time,太平洋标准时间,UTC-8)

💡 注意:PDT 只在夏令时期间有效(3 月中~11 月初)。其余月份美国西海岸用的是 PST(UTC-8)。如果你的业务时间跨度覆盖全年,不能简单地固定减 7 小时,应该用命名时区让 MySQL 自动处理切换:

1
CONVERT_TZ(utc_time, '+00:00', 'America/Los_Angeles')

2.3.3 PST 与 PDT 的关系

两者指的是同一个地理区域的同一条时间线,只是根据季节在"标准时间"和"夏令时"之间切换:

时段缩写全称UTC 偏移
冬季(11 月~3 月)PSTPacific Standard​ TimeUTC-8
夏季(3 月~11 月)PDTPacific Daylight​ TimeUTC-7

也就是说,PDT = PST + 1 小时。夏令时开始时时钟拨快 1 小时(PST → PDT),结束时拨慢 1 小时(PDT → PST)。

💡 所以严格来说,“Pacific Time(PT)“是一个统称,它底下分 PST 和 PDT 两种状态,随季节切换。 


2.4 PST/PDT 与北京时间相差多少

已知:

PDT = UTC-7,PST = UTC-8,北京时间 = UTC+8

所以可以得出:

太平洋当地时间UTC 偏移与北京时间的时差
PSTUTC-8CST=PST+16H
PDTUTC-7CST=PST+15H

3、跨时区项目一般怎么处理

原则一:后端和数据层统一使用 UTC

建议以下内容全部保存为 UTC:

  • 数据库时间字段
  • Pipeline 的运行时间
  • 文件到达时间
  • 日志时间
  • API 请求时间
  • 事件发生时间
  • 告警检测时间
  • 消息队列中的时间
  • 分布式系统 Watermark
  • 审计记录

例如:2026-07-31T06:30:00Z

而不是只保存:2026-07-31 14:30:00

后者没有说明时区,无法判断它是北京时间、UTC 还是美国当地时间。

原则二:展示时才转成用户当地时间

典型流程是:

1
2
3
4
5
6
7
8
9
事件发生
转换为 UTC
数据库按 UTC 保存
API 返回 UTC
前端根据用户时区显示

4、我的项目怎么处理

因为想的是替换掉手动去云存储那里一个个查文件的过程,所以dashboard这里显示的时间要和云存储的create_time一致

1
2
MySQL create_time = UTC
云存储 create_time = 洛杉矶当地时间

针对目标查询表加一层view视图,将转为洛杉矶当地时间,如下

1
CONVERT_TZ(create_time, '+00:00', 'America/Los_Angeles')

如果转换完得到的时间为NULL,是因为MySQL 必须查询内部时区表,以确定:

  • America/Los_Angeles 是否存在
  • 2026 年 7 月 31 日是否处于夏令时
  • 此时应该使用 UTC-7 还是 UTC-8
  • 夏令时切换边界如何处理

当前 MySQL 查不到对应的时区规则,因此返回 NULL。解决办法详见下面的4.1、4.2、4.3

4.1 查看MySQL Server 和 Session 当前使用的时区

1
2
3
4
SELECT
    @@system_time_zone AS system_time_zone,
    @@global.time_zone AS global_time_zone,
    @@session.time_zone AS session_time_zone;

返回

system_time_zoneglobal_time_zonesession_time_zone
China Standard TimeSYSTEMSYSTEM

4.2 查看命名时区表是否已经加载

1
2
3
4
SELECT
    COUNT(*) AS timezone_count
FROM mysql.time_zone_name
WHERE Name = 'America/Los_Angeles';

返回

timezone_count
0

4.3 开启时区数据库

要在 MySQL 中使用命名时区(如 'America/Los_Angeles''Asia/Shanghai'),必须先把系统的 IANA 时区数据库(zoneinfo)加载进 MySQL 自带的 mysql.time_zone_* 系统表里。否则 CONVERT_TZ 用命名时区时会返回 NULL

4.3.1 windows系统

windows系统则首先下载 MySQL 时区包:MySQL :: Time zone description tables,选择timezone_2026c_posix_sql.zip,解压到对应位置(我的位置是:C:\Users\v-liuyuhang\AppData\Roaming\DBeaverData\workspace6\General\timezone_posix.sql) 打开cmd,进入mysql.exe所在目录,我的是 C:\Program Files\MySQL\MySQL Server 8.4\bin

执行命令:

1
mysql -u root -p mysql < C:\Users\v-liuyuhang\AppData\Roaming\DBeaverData\workspace6\General\timezone_posix.sql

4.3.2 Linux系统

大多数 Linux 发行版和 macOS 在 /usr/share/zoneinfo 下都有 IANA 数据库,直接用一行管道加载

1
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql

4.3.4 云数据库

1
mysql --host="xxx.com" --port=3306 --user="xxx" --password --ssl-mode=REQUIRED mysql <  C:\Users\v-liuyuhang\AppData\Roaming\DBeaverData\workspace6\General\timezone_posix.sql

然后弹出 Enter password: 输入密码

4.3.3 验证

1
2
3
4
SELECT
    NOW()                                              AS mysql_original_time,
    CONVERT_TZ( NOW() , '+00:00', 'America/Los_Angeles') AS los_angeles_time,
    CONVERT_TZ( NOW() , '+00:00', '+08:00')              AS beijing_time;

返回

mysql_original_timelos_angeles_timebeijing_time
2026-07-31 17:36:022026-07-31 10:36:022026-08-01 01:36:02
使用 Hugo 构建
主题 StackJimmy 设计
无法复制,本站文章内容受保护