MySQL和Django时间问题总结
MySQL和Django时间问题总结
最近做了一个项目的重构,遇到一些关于Django时区问题和MySQL时间问题,在网上查了一些资料,很多博客都分享了相关信息。但是发现一些博客的相关回答不是那么准确,我重新总结下。
Python时间问题
在Python中关于时间有两种:Naive and aware datetime objects。offset-naive不带时区信息,offset-aware带时区信息。
Python的datetime.datetime对象有一个tzinfo属性,用于存储时区信息。也可以使用tzinfo判断datetime对象是否带有时区信息。
>>> import datetime
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2020, 6, 19, 16, 37, 12, 257502)
>>> now.tzinfo==None
True
也可以使用django.utils.timezone中的is_aware()和is_naive()来判断。
上面代码中的now就是一个offset-naive,offset-naive和offset-aware也可以相互转换。
>>> import pytz
>>> now=now.replace(tzinfo=pytz.timezone('UTC'))
>>> now
datetime.datetime(2020, 6, 19, 16, 37, 12, 257502, tzinfo=<UTC>)
>>> now=now.replace(tzinfo=None)
>>> now
datetime.datetime(2020, 6, 19, 16, 37, 12, 257502)
Django时区问题
Django配置文件的TIME_ZONE属性可以设置时区
在Django1.4之前,没有时区的概念,在Django1.4后可以通过配置文件的TIME_ZONE设置时区。
TIME_ZONE = 'UTC' # 世界标准时间
TIME_ZONE = 'Asia/Shanghai' # 中国时间
在中国,如果使用TIME_ZONE = 'UTC',获取offset-aware时间,会导致获取的时间与本地时间相差8小时。
Django配置文件的USE_TZ属性可以开开启关闭UTC时区
设置UTC时区关闭(USE_TZ=False 默认为False),Django会获取TIME_ZONE属性配置的时区,与datetime.datetime.now()完全相同,比如:
# TIME_ZONE = 'Asia/Shanghai' # 使用中国时间
# USE_TZ = False # 关闭UTC时间
>>> from django.utils import timezone
>>> timezone.now()
datetime.datetime(2020, 6, 22, 9, 54, 26, 199944)
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2020, 6, 22, 9, 54, 45, 585132)
启用UTC时区后(USE_TZ=True),Django的timezone.now()输出地永远是UTC时间,不管你设置的TIME_ZONE是什么。比如:
TIME_ZONE = 'Asia/Shanghai' # 使用中国时间
# USE_TZ = True # 开启UTC时间
>>> from django.utils import timezone
>>> timezone.now()
datetime.datetime(2020, 6, 22, 1, 58, 29, 189227, tzinfo=<UTC>)
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2020, 6, 22, 9, 58, 44, 109917)
所以如果想使用非UTC时区时间,不要设置USE_TZ=True,默认为False
总结下
- datetime.datetime.now()输出的是TIME_ZONE时区的naive time。
TIME_ZONE = 'UTC'
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2020, 6, 22, 2, 3, 37, 319363)
- django.util.timezone.now()如果setting中配置USE_TZ=True则输出的是UTC时间(offset-aware),如果配置USE_TZ=False,则与datetime.datetime.now()完全相同。
- USE_TZ=True时,在模型类里设置DateTimeField,这个类型的时间会存储带时区的时间。如果存储datetime.datetime.now(),就会报下列错误。
DateTimeField role_cost_history.cost_time received a naive datetime (2015-05-12 19:59:01.259517) while time zone support is active
- 如果确实需要获取中国时区的offset-aware,可以使用开头介绍的pytz.timezone('Asia/Shanghai')进行转换。
Mysql时间类型
mysql中时间字段用什么类型(基于innodb存储引擎)?
-
(1)varchar,如果用varchar类型来存时间,优点在于显示直观。但是插入的数据没有校验,格式可能不规范。比如一条数据为2020111的数据,无法辨别这是代表2020年1月11日,还是2020年11月1日。
其次,做时间比较运算,需要用STR_TO_DATE等函数将其转化为时间类型,你会发现这么写是无法命中索引的。数据量一大,是个坑! -
(2)timestamp,该类型是四个字节的整数,它能表示的时间范围为1970-01-01 08:00:01到2038-01-19 11:14:07。2038年以后的时间,是无法用timestamp类型存储的。
但是它有一个优势,timestamp类型是带有时区信息的。一旦你系统中的时区发生改变,例如你修改了时区
SET TIME_ZONE = "america/new_york";
你会发现,项目中的该字段的值自己会发生变更。这个特性用来做一些国际化大项目,跨时区的应用时,特别注意!
-
(3)datetime,datetime储存占用8个字节,它存储的时间范围为1000-01-01 00:00:00 ~ 9999-12-31 23:59:59。显然,存储时间范围更大。但是它存储的是时间绝对值,不带有时区信息。如果你改变数据库的时区,该项的值不会自己发生变更!
-
(4)bigint,也是8个字节,自己维护一个时间戳,表示范围比timestamp大多了,就是要自己维护,不大方便。
Django设置时区为TIME_ZONE = 'Asia/Shanghai' USE_TZ = True后,存入mysql中的时间只能是UTC时间。(参考自梓沂)
因为Mysql存储的时间不能灵活设置时区,不像datetime对象有一项参数专门指定时区,所以为了统一全球的时间,必须使用国际标准时间UTC,否则就会乱套。所有时间在存如数据库前,必须转换成UTC时间。比如北京时间8点,存入mysql变成0点(UTC)。
综上所述,如果没有跨时区的需求,可以将mysql的时间字段可以设置为datetime类型,Django中可以设置USE_TZ = False,TIME_ZONE = 'Asia/Shanghai'。

浙公网安备 33010602011771号