Django: Time Zone

2nd December 2021 at 3:07pm

Django 对时区处理的核心逻辑是:

  • 写入 DB 时,如果传的是 aware datetime object,转成 UTC 进行存储
  • 从 DB 读取时,把 DATETIME 转成终端用户的时区

即使网站用户只有一个地区,Django 也 建议 用 UTC 存储时间,来避免有些地区使用的夏令时(DST)引起一个时间重复出现多次的问题。

USE_TZ 设置:

  • 如果打开,Django 会在内部使用 aware 对象
  • 如果关闭,Django 会在内部使用 TIME_ZONE 定义的的本地时区的 naive 对象(有一些不太重要的例外)

TIME_ZONE 设置的意义是一个默认的时区。它的逻辑是:

  • 存储 DATETIME 对象时,如果:
    • USE_TZFalse 时,Django 会把 DATETIME 对象转换到这个时区再去存储;你在 DB 中的值会是这个时区的时间(即使 DB 本身不存储时区,如 MySQL)
    • USE_TZTrue 时,Django 会把 DATETIME 对象转换到 UTC 再去存储;你在 DB 中的值会是 UTC 时间
  • 读取 DATETIME 对象时,如果:
    • DB 本身不存储时区信息(比如 MySQL),那读取出来的 DATETIME 值在不同情况下,会被认为是:
      • USE_TZFalse 时,逻辑上被认为是 TIME_ZONE 设定的时区(但 Django 内部仍然使用 naive object);在展示到 template 时,不会被转换时区
      • USE_TZTrue 时,被认为是 UTC 时间,Django 内部会使用 aware object;在展示到 template 时,会被转换为应用代码中指定的用户时区;如果代码没有指定,会被转换为 TIME_ZONE 设置的时区

这个逻辑看起来很复杂。但是从演变的角度看就容易理解

  1. TIME_ZONE 设置早于 USE_TZ 设置。在 Web 还没有蓬勃发展的年代,一个网站往往仅服务一个国家 / 地区的用户。因此一个 TIME_ZONE 设置就足够了
  2. Django 1.4 才引入 USE_TZ 设置。当 USE_TZ 被打开时,TIME_ZONE 的含义 从网站的唯一时区,变成了 当没有给用户指定一个时区时 的默认时区

DATABASE 的设置中也有一个 TIME_ZONE 选项。绝大多数时候你都不应该设置它。这个设置应该会影响 MySQL session 的 time_zone 变量值。这里 描述了 MySQL 的时区支持,以及为什么不应该关心 time_zone 变量的设置。

参考