Prisma、Drizzle 等时间存储与时区问题

Prisma、Drizzle 的时间类型都会存储为 UTC 时间,JS 时间对象转换 SQL 也会基于 UTC 。应在数据库存 UTC,展示层转本地时区。

为什么时间要存储为 UTC

数据库应保持 UTC 时区存储,优点:

  • 数据库存 UTC,避免时区混乱,展示层再转本地时区
  • 多服务器部署时,每台机器可能时区不同,这样有利于始终保持一致

一律使用 ORM 框架处理时间

正常来说,把数据库的时区设置为 UTC ,就可以使数据库的时间处理跟 ORM 一致。

但有些数据库环境不方便配置时区,这时候如果把数据库本身的 CURRENT_TIMESTAMP 和 ORM 混用,就会出现时间混乱的情况。

一律使用 ORM 框架处理时间即可,比如 Drizzle 定义 schema 时使用 $defaultFn 这种不会实际影响数据库迁移的语法:

create_time: datetime("create_time").notNull().$defaultFn(() => new Date()),
update_time: datetime("update_time").notNull().$defaultFn(() => new Date()).$onUpdateFn(() => new Date()),

显式接收前端传入的时间字段时(如可供前端设置的活动开始时间、文章发布时间等),也要转换为标准的 UTC 时间,供 ORM 框架处理,转换为 JS 的 Date 对象,并最终以 UTC 时区存入。

历史数据处理

对之前存储 +8 时区的时间的数据,可以写脚本统一调整一下。

使用 MySQL 的内置函数 CONVERT_TZ() 将时间从东八区转换为 UTC 时间。以下是 SQL 语句示例:

UPDATE collection
SET create_time = CONVERT_TZ(create_time, '+08:00', '+00:00'),
    update_time = CONVERT_TZ(update_time, '+08:00', '+00:00'),
    publish_time = CONVERT_TZ(publish_time, '+08:00', '+00:00');

UPDATE diary
SET create_time = CONVERT_TZ(create_time, '+08:00', '+00:00'),
    update_time = CONVERT_TZ(update_time, '+08:00', '+00:00'),
    publish_time = CONVERT_TZ(publish_time, '+08:00', '+00:00');
本文收录于专栏
收集一些好用的前端开源库,主要是 npm 包