时区类型


1.UTC 的介绍

  • 整个地球分为二十四时区,每个时区都有自己的本地时间。在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC, Universal Time Coordinated)。UTC与格林尼治平均时(GMT, Greenwich Mean Time)一样,都与英国伦敦的本地时相同

  • UTC时间 比 北京时间 小8个小时

2.时区类型

    • 在 django1.4 以后,存在两个概念

      • (naive time) -> (offset-naive) -> 不包含时区

# 不包含时区的时间格式

2020-03-15 16:23:27.241176

      • (active time) -> (offset-aware) -> 包含时区

# 包含时区的时间格式

2020-03-15 08:14:03+00:00

3.查看日期对象是否包含时区

  • 语法: 日期对象.tzinfo

import datetime
from app01.models import *

now = datetime.datetime.now()
print(now.tzinfo)  # None -> offset-naive: 不含时区

specified_date = datetime.datetime(year=2019, month=3, day=20, hour=10, minute=1, second=32, microsecond=1)
print(now.tzinfo)  # None -> offset-naive: 不含时区

time_obj = TimeTable.objects.filter(id=1).first()
print(time_obj.time.tzinfo # UTC -> offset-aware: 包含时区

4.给日期对象添加时区

import datetime
import pytz

now = datetime.datetime.now().replace(tzinfo=pytz.timezone('UTC'))
print(now.tzinfo)  # UTC -> offset-aware: 包含时区

Django 的时间配置项


1.USE_TZ

  • True: 存储到数据库中的时间永远是UTC时间(即: 带时区的UTC时间) -> 将 datetime.datetime.now() 获取到的时间存入数据库时 Django 会把这个时间当成UTC时间存储到数据库中

  • False: 存储到数据库中的时间永远是本地时间(即: 不带任何时区的当前主机的时间) -> 将 datetime.datetime.now() 获取到的时间存入数据库时 Django 会把这个时间当成本地时间存储到数据库中

# settings.py

USE_TZ = True

2.TIME_ZONE

  • TIME_ZONE 一般和 USE_TZ 搭配使用

  • 当 USE_TZ = False 可以直接忽略 TIME_ZONE 该参数(即: TIME_ZONE 无效)

  • 作用: 当 USE_TZ = True 时,此时数据库存储的都是 UTC 时间,当模板或 rest-framework 对时间进行渲染时会根据该参数所指定的时区进行时间的转换(即: 是否加8个小时进行显示)

  • 通俗理解: 当 USE_TZ = True 和 TIME_ZONE = 'Asia/Shanghai' 时,模板或 rest-framework 所渲染出的时间和本地时间没有时差

# settings.py

TIME_ZONE = 'UTC'  # 默认使用 UTC 时间进行显示

TIME_ZONE = 'Asia/Shanghai'  # 使用上海时间进行显示

3.常见问题

  • 三个时间 datetime.datetime.now()、datetime.datetime.utcnow()、django.util.timezone.now() 的区别

    • datetime.datetime.now(): 输出的永远是本地时间(naive time)与配置无任任何关系
    • datetime.datetime.utcnow(): 如果setting中配置 USE_TZ=True 则输出的是UTC时间(naive time -> 不包含时区),如果setting中配置 USE_TZ=False,则该输出时间与 datetime.datetime.now() 完全相同
    • django.util.timezone.now(): 如果setting中配置 USE_TZ=True 则输出的是UTC时间(active time -> 包含时区),如果setting中配置 USE_TZ=False,则该输出时间与 datetime.datetime.now() 完全相同

  • Django 存储到数据库的时间比本地时间小8个小时

    • 如果配置为 USE_TZ=True 与 TIME_ZONE = 'UTC',将 datetime.datetime.now() 获取到的时间存入数据库时 Django 会把这个时间当成UTC时间存储到数据库中,当模板或 rest-framework 对时间进行渲染时会按照 UTC 时间进行渲染

    • 如果配置为 USE_TZ=True 与 TIME_ZONE = 'Asia/Shanghai',用 datetime.datetime.now() 获取的时间由于不带时区,Django会把这个时间当成 Asia/Shanghai 时间(即东八区时间),然后 Django 会把这个时间转成带时区UTC时间存储到数据库中去,当模板或 rest-framework 对时间进行渲染时会按照 Asia/Shanghai 时间(即: 上海时间)进行渲染

  • 模板 或 rest-framework 显示的时间

    • 当 USE_TZ = True 和 TIME_ZONE = 'Asia/Shanghai' 时,模板或 rest-framework 所渲染出的时间和本地时间没有时差

  • django.util.timezone.now() 输出时间比本地时间小8个小时

    • 设置了 USE_TZ = True,django.util.timezone.now() 输出地永远是UTC时间,不管你设置的TIME_ZONE是什么。
    • 设置了 USE_TZ=False,django.util.timezone.now() 输出等同于datetime.datetime.now(),也不管TIME_ZONE设置的是什么

4.建议

  • 为了统一时间,在django开发时,尽量使用UTC时间,即设置 USE_TZ=True,TIME_ZONE = 'Asia/Shanghai',并且在获取时间的时候使用 django.util.timezone.now()。因为后台程序使用时间时UTC时间就能满足,也能保证证模板时间的正确显示

在 Django 开发中 日期的运算 和 比较 都需要注意时区类型


  • 不同时区的类型的运算和比较错误提醒(即: 一个日期对象有时区,一个日期对象没有时区)


Traceback (most recent call last):
  File "C:/Users/Mr. Yeung/Desktop/PyFolder/test_time/app01/tests.py", line 27, in <module>
    print(time_obj.time - now)
TypeError: can't subtract offset-naive and offset-aware datetimes

  • 解决办法 -> 统一时间类型

    • 统一为 offset-naive: 不含时区

      • 方法一 -> 将查询到的日期对象变成不包含时区的日期对象

import datetime
from app01.models import *

time_obj = TimeTable.objects.filter(id=1).first()
time_obj_time = time_obj.time.replace(tzinfo=None)  # 将查询到的日期对象变成不包含时区的日期对象
print(time_obj_time.tzinfo)  # None -> offset-naive: 不含时区

now = datetime.datetime.now()
print(now.tzinfo)  # None -> offset-naive: 不含时区

difference_time = now - time_obj_time  # 10:46:45.613783
result = now > time_obj_time  # True

      • 方法二 -> 在 settings.py 中设置 USE_TZ = False,那么直接查询到的是不带时区的日期对象

# settings.py

USE_TZ = False

import datetime
from app01.models import *

time_obj = TimeTable.objects.filter(id=1).first()
print(time_obj.time.tzinfo)  # None -> offset-naive: 不含时区

now = datetime.datetime.now()
print(now.tzinfo)  # None -> offset-naive: 不含时区

difference_time = now - time_obj.time  # 10:51:10.098679
result = now > time_obj.time  # True

    • 统一为 offset-aware: 包含时区

      • 前提: settings.py 中的设置 USE_TZ = True

# settings.py

USE_TZ = True

import datetime
import pytz
from app01.models import *

time_obj = TimeTable.objects.filter(id=1).first()
print(time_obj.time.tzinfo)  # UTC -> offset-aware: 包含时区

now = datetime.datetime.now().replace(tzinfo=pytz.timezone('UTC'))
print(now.tzinfo)  # UTC -> offset-aware: 包含时区

difference_time = now - time_obj.time  # 10:55:30.414490
result = now > time_obj.time  # True