python中什么是naive时间和aware时间
naive时间表示幼稚时间,不知道自己所在的时区
aware时间表示清醒时间,知道自己所在时区
aware时间的作用
aware时间知道自己的时区,在国际化时可以方便的转换成其它时区。开发中一般都用aware时间。
举个例子
使用比较多的datetime.now()返回的其实是naive时间,比如naive时间转utc时区,会抛异常
from datetime import datetime
import pytz
naive_date = datetime.now()
target_timezone = pytz.timezone("UTC")
utc_date = naive_date.astimezone(target_timezone) #astimezone()作用是转换时区
#抛异常
>>>astimezone() cannot be applied to a naive datetime
naive转aware后再改时区
#naive时间转aware时间
from datetime import datetime
import pytz
naive_date = datetime.now()
print(naive_date)
target_timezone = pytz.timezone("Asia/Shanghai")
aware_date = naive_date.replace(tzinfo=target_timezone)#replace()作用是修改时间属性,可以修改年月日时分秒、时区
print(aware_date)
utc_timezone = pytz.timezone("UTC")
utc_date = aware_date.astimezone(utc_timezone)
print(utc_date)
>>>2020-06-10 18:22:59.108800
>>>2020-06-10 18:22:59.108800+08:06
>>>2020-06-10 10:16:59.108800+00:00
可以看到打印结果,区别就是有没有带上时区
django中怎么生成aware时间
直接生成aware时间:
from django.utils import timezone
print(timezone.now())
>>>2020-06-10 10:32:06.143800+00:00
打印结果是一个utc时区的aware时间,看下源码timezone.now()这个函数做了什么:
def now():
"""
Returns an aware or naive datetime.datetime, depending on settings.USE_TZ.
"""
if settings.USE_TZ:
# timeit shows that datetime.now(tz=utc) is 24% slower
return datetime.utcnow().replace(tzinfo=utc)
else:
return datetime.now()
如果settings.py文件中USE_TZ = True,将datetime.utcnow()返回的utc naive时间修改成utc aware时间。如果是False返回datetime.now(),是基于当前时区的naive时间。
naive时间转aware时间:
from django.utils.timezone import make_aware
naive_time = datetime(year=2020,month=5,day=31,hour=8)
print(naive_time)
aware_time = make_aware(naive_time)
print(aware_time)
>>>2020-05-31 08:00:00
>>>2020-05-31 08:00:00+08:00
可以自行查看下make_aware的实现代码,如果没有传timezone参数,使用的是settings文件的TIME_ZONE属性,这里aware时间时区显示+8是因为TIME_ZONE = 'Asia/Shanghai'
ORM中常用的时间查询
有了上面的时间概念,来看下orm中的时间查询示例
订单表如下:
+----+-----------+----------------------------+
| id | order_num | create_time |
+----+-----------+----------------------------+
| 1 | 002 | 2020-05-31 08:29:41 |
| 2 | 001 | 2020-05-31 08:59:35 |
+----+-----------+----------------------------+
range
#USE_TZ = True
start_date = datetime(year=2020,month=5,day=31,hour=8)
end_date = datetime(year=2020,month=5,day=31,hour=9)
orders = Order.objects.filter(create_time__range=(start_date,end_date))
print(orders.query)
print(orders)
>>>RuntimeWarning: DateTimeField Order.create_time received a naive datetime (2020-05-31 08:00:00) while time zone support is active.
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE `goods_order`.`create_time` BETWEEN 2020-05-31 00:00:00 AND 2020-05-31 01:00:00
>>><QuerySet []>
#USE_TZ = False
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE `goods_order`.`create_time` BETWEEN 2020-05-31 08:00:00 AND 2020-05-31 09:00:00
>>><QuerySet [<Order: Order object>, <Order: Order object>]>
如果USE_TZ = True,我理解的sql翻译的规则是:ORM会在逻辑上将settings中的TIME_ZONE 对应的时区直接附加到查询时间上,再计算出对应的utc时间,比如2020-5-31 9:00,假设TIME_ZONE = Asia/Shanghai,逻辑上看做2020-5-31 9:00+08:00,计算出的utc时间就是2020-5-31 1:00+00:00
。
通过警告可以看出是一个2020-05-31 08:00:00的naive时间,要想得到正确结果并且取消警告,操作如下:
start_date = make_aware(datetime(year=2020,month=5,day=31,hour=16))
end_date = make_aware(datetime(year=2020,month=5,day=31,hour=17))
...
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE `goods_order`.`create_time` BETWEEN 2020-05-31 08:00:00 AND 2020-05-31 09:00:00
>>><QuerySet [<Order: Order object>, <Order: Order object>]>
date,根据年月日查找
#USE_TZ = True
search_date = datetime(year=2020,month=5,day=31)
orders = Order.objects.filter(create_time__date=search_date)
print(orders.query)
print(orders)
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE DATE(CONVERT_TZ(`goods_order`.`create_time`, 'UTC', Asia/Shanghai)) = 2020-05-31
>>><QuerySet []>
#USE_TZ = False
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE DATE(`goods_order`.`create_time`) = 2020-05-31
>>><QuerySet [<Order: Order object>, <Order: Order object>]>
如果USE_TZ = True,查询结果为空,DATE(CONVERT_TZ(goods_order.create_time, UTC, Asia/Shanghai))
使用了时区mysql表中没有存储时区信息,windows相同可以在http://dev.mysql.com/downloads/timezones.html下载文件解压后复制到mysql5.7.24\data\mysql
下,再次查询即可查到正确的数据。
week_day,根据星期查找
1-7表示从周日到周六
#USE_TZ = True
orders = Order.objects.filter(create_time__week_day=1)
print(orders.query)
print(orders)
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE DAYOFWEEK(CONVERT_TZ(`goods_order`.`create_time`, 'UTC', Asia/Shanghai)) = 1
>>><QuerySet [<Order: Order object>, <Order: Order object>]>
#USE_TZ = False
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE DAYOFWEEK(`goods_order`.`create_time`) = 1
>>><QuerySet [<Order: Order object>, <Order: Order object>]>
time,根据时分秒查找
#USE_TZ = True
from datetime import time
search_time = time(hour=16,minute=59,second=35)
orders = Order.objects.filter(create_time__time=search_time)
print(orders.query)
print(orders)
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE TIME(CONVERT_TZ(`goods_order`.`create_time`, 'UTC', Asia/Shanghai)) = 16:59:35
>>><QuerySet [<Order: Order object>]>
#USE_TZ = False
search_time = time(hour=8,minute=59,second=35)
>>>SELECT `goods_order`.`id`, `goods_order`.`order_num`, `goods_order`.`create_time` FROM `goods_order` WHERE TIME(`goods_order`.`create_time`) = 08:59:35
>>><QuerySet [<Order: Order object>]>
精确到秒,一般使用time进行范围查询:
#USE_TZ = True
start_time = time(hour=16,minute=59,second=35)
start_end = time(hour=16,minute=59,second=36)
Order.objects.filter(create_time__time__range=(start_time,end_time))
网友评论