Django之 Timezone 详解

Django 时区问题:

最近在做项目时发现存储的时间比本地时间慢了八小时,查了很多文章,感觉都没有讲的很明白,本文根据自己的理解和如何解决的详细的记录下来,但也参考了一些文章和官网介绍。

datetime.now()

datetime.now() 永远获取的是你本地的时间,和你配置无任何关系,并且是不带时区属性的。

from datetime import datetime
>>> datetime.now()
datetime.datetime(2021, 11, 11, 15, 20, 50, 742399)

datetime.utcnow()

datetime.utcnow() 获取的标准时间,即 UTC 时间。

from datetime import datetime
>>> datetime.utcnow()
datetime.datetime(2021, 11, 11, 7, 58, 32, 919602)

timezone.now()

timezone.now() 如果 settings.py 中的 USE_TZ 为 True 则获取的时间是 UTC 时间,False 就是本地时间和 datetime.now() 完全相同。

from django.utils import timezone
>>> USE_TZ=True
>>> timezone.now()
datetime.datetime(2021, 11, 11, 8, 2, 14, 825261, tzinfo=<UTC>)
USE_TZ=Fasle
>>> timezone.now()
datetime.datetime(2021, 11, 11, 16, 19, 20, 426752)

django 储存到数据库的时间比本地慢八小时

如果 settings.py 中的 USE_TZ 为 True 则存储的永远是 UTC 时间,这也是合理的,在 django 1.4 之前对时区是不做处理的,通常都是本地时间,都是 naive time,即不带时区的时间,但是这样会带来一个问题,如果用户居住在多个时区时,时间就会很乱,所以在django 1.4 之后启用了时区支持,全部统一使用 UTC存储日期信息,在内部使用一些感知对象去转换时间。这样即便用户在多个时区也不会有时间问题。

Naive 日期对象与 Aware 日期对象

上述提到 django 内部使用一些方法去感知用户时区对象,在 datetime 对象中有一个 tzinfo 属性,它用来存储时区信息,表示为 datetime.tzinfo 子类的实例,当 USE_TZ 为 True 时日期对象就是 Aware 的,否则就是 Naive 的。

from django.utils import timezone
>>> USE_TZ=True
>>> timezone.now()
datetime.datetime(2021, 11, 11, 8, 2, 14, 825261, tzinfo=<UTC>)  // 带时区:Aware 
USE_TZ=Fasle
>>> timezone.now()
datetime.datetime(2021, 11, 11, 16, 19, 20, 426752)    // 不带时区:Naive 

is_aware() 和 is_naive()

怎么知道当前时间是否带时区和不带时区,你可以使用 is_aware() 和 is_naive() 来决定日期是 aware 还是 naive 的。

from django.utils import timezone
>>> timezone.now()
datetime.datetime(2021, 11, 11, 8, 35, 11, 360384, tzinfo=<UTC>)
>>> now = timezone.now()
>>> timezone.is_aware(now)
True
>>> timezone.is_naive(now)
False

make_aware() 和 make_naive()

make_aware():返回一个本地化的时间,根据 TIME_ZONE 参数。
make_naive():返回一个不带时区的时间,根据 TIME_ZONE 的值作为时间,否则就是当前时区。

>>> from django.utils import timezone
>>> now = timezone.now()
>>> timezone.is_aware(now)  // 判断是否带时区
True
>>> now
datetime.datetime(2021, 11, 11, 8, 51, 26, 327845, tzinfo=<UTC>)
>>> timezone.make_naive(now)  // 去除时区,根据 tzinfo 中的时区属性使用 pytz 进行时区定义 
datetime.datetime(2021, 11, 11, 16, 51, 26, 327845)
>>> sh = timezone.make_naive(now)
>>> sh
datetime.datetime(2021, 11, 11, 16, 51, 26, 327845)
>>> t = timezone.make_aware(sh)  // 增加时区,转换成设置的 TIME_ZONE 时区的时间
>>> t
datetime.datetime(2021, 11, 11, 16, 51, 26, 327845, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_TZ = True

总结

  1. django 默认全部储存为 utc 时间,是为了兼容各国时区问题,内部使用 is_aware() 和 is_naive() 去感知时间对象。
  2. 只要设置了USE_TZ=True 输出的永远是UTC时间,反之就是 datetime.now(),也不管 timezone 设置的是什么。
  3. 在 django 开发时,尽量使用 utc 时间,即设置USE_TZ=True,TIME_ZONE = ‘Asia/Shanghai’,并且在获取时间的时候使用django.util.timezone 中提供的方法去感知时间,从而来确定是否转换当前时间还是继续使用 utc 时间。

参考文献

pytz: http://pytz.sourceforge.net/
is_aware、is_naive、make_aware、make_naive: https://docs.djangoproject.com/en/3.2/ref/utils/#django.utils.timezone.is_aware
django 时区官网:https://docs.djangoproject.com/en/3.2/topics/i18n/timezones/

你可能感兴趣的:(python,orm,django,django,python,后端,orm)