引言
相信使用Django的各位开发者在存储时间的时候经常会遇到这样子的错误:
RuntimeWarning: DateTimeField received a naive datetime while time zone support is active.
这个错误到底是什么意思呢?什么是naive datetime object?什么又是aware datetime object?
在Django配置中如果将settings.TIME_ZONE设置为中国时区(Asia/Shanghai),为什么以下时间函数会得到时间相差较大的结果?
# settings.py
TIME_ZONE = 'Asia/Shanghai'
# python manage.py shell
>>> from datetime import datetime
>>> datetime.now()
datetime.datetime(2016, 12, 7, 12, 41, 22, 729326)
>>> from django.utils import timezone
>>> timezone.now()
datetime.datetime(2016, 12, 7, 4, 41, 36, 685921, tzinfo=<UTC>)
接下来笔者将详细揭秘在Django中关于时区的种种内幕,如有不对,敬请指教。
准备
UTC与DST
UTC可以视为一个世界统一的时间,以原子时为基础,其他时区的时间都是在这个基础上增加或减少的,比如中国的时区就为UTC+8。
DST(夏时制)则是为了充分利用夏天日照长的特点,充分利用光照节约能源而人为调整时间的一种机制。通过在夏天将时间向前加一小时,使人们早睡早起节约能源。虽然很多西方国家都采用了DST,但是中国不采用DST。(资料来源:DST 百度百科)
naive datetime object vs aware datetime object
当使用datetime.now()得到一个datetime对象的时候,此时该datetime对象没有任何关于时区的信息,即datetime对象的tzinfo属性为None(tzinfo属性被用于存储datetime object关于时区的信息),该datetime对象就被称为naive datetime object。
>>> import datetime
>>> naive = datetime.datetime.now()
>>> naive.tzinfo
>>>
既然naive datetime object没有关于时区的信息存储,相对的aware datetime object就是指存储了时区信息的datetime object。
在使用now函数的时候,可以指定时区,但该时区参数必须是datetime.tzinfo的子类。(tzinfo是一个抽象类,必须有一个具体的子类才能使用,笔者在这里使用了pytz.utc,在Django中的timezone源码中也实现了一个UTC类以防没有pytz库的时候timezone功能能正常使用)
>>> import datetime
>>> import pytz
>>> aware = datetime.datetime.now(pytz.utc)
>>> aware
datetime.datetime(2016, 12, 7, 8, 32, 7, 864077, tzinfo=<UTC>)
>>> aware.tzinfo
<UTC>
在Django中提供了几个简单的函数如is_aware, is_naive, make_aware和make_naive用于辨别和转换naive datetime object和aware datetime object。
datetime.now简析
在调用datetime.now()的时候时间是如何返回的呢?在官方文档里只是简单地说明了now函数返回当前的具体时间,以及可以�指定时区参数,并没有具体的说明now函数的实现。
classmethod datetime.now(tz=None)
Return the current local date and time. If optional argument tz is None or not specified, this is like today(), but, if possible, supplies more precision than can be gotten from going through a time.time() timestamp (for example, this may be possible on platforms supplying the C gettimeofday() function).If tz is not None, it must be an instance of a tzinfo subclass, and the current date and time are converted to tz’s time zone. In this case the result is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). See also today(), ut