Django 时区处理全指南:UTC 配置、Naive/Aware 对象与实战技巧
本文详解 Django 中的时区处理机制,涵盖 USE_TZ 配置、Naive 与 Aware 对象的区别,以及 Python 标准库 time 和 datetime 模块的使用。通过实战案例展示如何利用 timedelta 对象实现 Redis 缓存时间与当前时间的差值计算,解决前端倒计时需求,并提供常用格式化代码参考。
Django 中的时区
在实际应用场景中,用户分布在全球不同的时区,且许多国家实行夏令时制度。若网站面向多时区用户,仅依赖本地时间进行开发会导致时间计算出现偏差。
解决此类问题的最佳实践是在代码和数据库层面统一使用 UTC 时间,仅在最终与用户交互时转换为本地时间。
Django 默认关闭时区支持。如需开启,需在 settings 配置文件中设置 USE_TZ = True。建议同时安装 pytz 模块(pip install pytz)以增强兼容性。
Naive 和 Aware 类型的 datetime 对象
Python 的 datetime.datetime 对象包含 tzinfo 属性,该属性是 datetime.tzinfo 子类的实例,用于存储时区信息。当 datetime 对象的 tzinfo 属性被设置并指定了时间偏移量时,该对象被称为 aware(已知)类型;否则称为 naive(原生)类型。
可以使用 is_aware() 和 is_naive() 函数来判断某个 datetime 对象属于 aware 类型还是 naive 类型。
当关闭时区支持时,Django 使用 naive 的 datetime 对象保存本地时间:
import datetime now = datetime.datetime.now()
当开启时区支持时,Django 使用 aware(已知)的 datetime 对象存储本地时间:
from django.utils import timezone now = timezone.now()
Django 和 Python 中的 Timezone 处理
Django 中的 timezone
from django.utils import timezone >>> now = timezone.now() >>> now datetime.datetime(2018, 2, 22, 3, 13, 2, 383795, tzinfo=<UTC>) >>>new = timezone.localtime(now) >>>new datetime.datetime(2018, 2, 22, 11, 13, 2, 383795, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
Python 中的 datetime
>>>import datetime,pytz
>>>t = datetime.datetime.now()
>>>t
datetime.datetime(2018, 2, 22, 11, 18, 15, 623160)
>>>new_t = t.replace(tzinfo=(pytz.timezone('Asia/Shanghai')))
>>>new_t
datetime.datetime(2018, 2, 22, 11, 18, 15, 623160, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)
Python 中的 time/datetime
time 模块
time 模块提供了多种操作时间的函数。
通常有两种表示时间的方式:
第一种是时间戳方式(相对于 1970.1.1 00:00:00 以秒计算的偏移量),时间戳具有唯一性。
>>> import time >>> time.time() 1519270378.989196
第二种是以数组形式表示,即 (struct_time),共包含九个元素,分别表示不同的时间属性。同一时间戳下的 struct_time 会因时区不同而有所差异。
>>> time.localtime() time.struct_time(tm_year=2018, tm_mon=2, tm_mday=22, tm_hour=11, tm_min=38, tm_sec=45, tm_wday=3, tm_yday=53, tm_isdst=0)
gmtime() 和 mktime() 函数可实现两种时间表示方法之间的自由转换。
>>> time.gmtime(time.time()) time.struct_time(tm_year=2018, tm_mon=2, tm_mday=22, tm_hour=11, tm_min=38, tm_sec=45, tm_wday=3, tm_yday=53, tm_isdst=0) >>> time.mktime(time.localtime()) 1519270378.989196
实现 struct_time 类型与字符串类型之间的自由转换。
>>> time1 = time.strftime("%Y-%m-%d",time.localtime())
>>> time1
'2018-02-22'
>>> time2 = time.strptime(time1,"%Y-%m-%d")
time.struct_time(tm_year=2018, tm_mon=2, tm_mday=22, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=53, tm_isdst=0)
datetime 模块
datetime 模块提供了多个用于操作日期和时间的函数。
datetime 模块中定义的类包括:
datetime.date:表示日期的类。常用属性有 year、month、day。
datetime.time:表示时间的类。常用属性有 hour、minute、second、microsecond。
datetime.datetime:表示日期和时间。
datetime.timedelta:表示时间间隔,即两个时间点之间的长度。
datetime.tzinfo:与时区相关的相关信息。
>>> import datetime >>> datetime.datetime.now() datetime.datetime(2018, 2, 22, 11, 18, 15, 623160)
datetime 类型与字符串类型之间的相互转换。
>>> today = datetime.datetime.now().strftime("%Y-%m-%d")
>>> today
2018-02-22
>>> today2 = datetime.datetime.strptime(today,"%Y-%m-%d")
>>> today2
datetime.datetime(2018, 2, 22, 0, 0)
对时间间隔进行操作
datetime2 = datetime1 + timedelta:日期加上一个时间间隔,返回一个新的日期对象。
datetime2 = datetime1 - timedelta:日期减去一个时间间隔,返回一个新的日期对象。
timedelta = date1 - date2:两个日期相减,返回一个时间间隔对象。
time 与 datetime 之间的相互转换
date.fromtimestamp(timestamp):根据给定的时间戳,返回一个 date 对象。
>>> datetime.datetime.fromtimestamp(time.time()) datetime.datetime(2018, 2, 22, 14, 44, 847842)
格式化代码参考表
| 代码 | 作用 | 代码 | 作用 | |
|---|---|---|---|---|
| %a | 星期几的简写 | %A | 星期几的全称 | |
| %b | 月分的简写 | %B | 月份的全称 | |
| %c | 标准的日期的时间串 | %C | 年份的后两位数字 | |
| %d | 十进制表示的每月的第几天 | %D | 月/天/年 | |
| %e | 在两字符域中,十进制表示的每月的第几天 | %F | 年-月-日 | |
| %g | 年份的后两位数字,使用基于周的年 | %G | 年分,使用基于周的年 | |
| %h | 简写的月份名 | %H | 24小时制的小时 | |
| %I | 12小时制的小时 | %j | 十进制表示的每年的第几天 | |
| %m | 十进制表示的月份 | %M | 十时制表示的分钟数 | |
| %n | 新行符 | %p | 本地的AM或PM的等价显示 | |
| %r | 12小时的时间 | %R | 显示小时和分钟:hh:mm | |
| %S | 十进制的秒数 | %t | 水平制表符 | |
| %T | 显示时分秒:hh:mm:ss | %u | 每周的第几天,星期一为第一天 (值从0到6,星期一为0) | |
| %U | 第年的第几周,把星期日做为第一天(值从0到53) | %V | 每年的第几周,使用基于周的年 | |
| %w | 十进制表示的星期几(值从0到6,星期天为0) | %W | 每年的第几周,把星期一做为第一天(值从0到53) | |
| %x | 标准的日期串 | %X | 标准的时间串 | |
| %y | 不带世纪的十进制年份(值从0到99) | %Y | 带世纪部分的十制年份 | |
| %z | ,%Z 时区名称,如果不能得到时区名称则返回空字符。 | %% | 百分号 |
补充:Django 中 timezone.now() 转时间戳/秒数的属性。
Django 里 timezone 对象连度娘都不知道的使用技巧
今天碰到一个需求是:
从 MySQL 中取出一个时间字段存入 Redis,在前端发起请求后,将该时间字段与当前时间的差值(秒)响应给前端,以便前端用作倒计时显示。
整个项目使用 Django 框架,Django 在模型层定义时间字段时,使用的是 DateTimeField(default=timezone.now)。存入数据库的是字符串,但如果使用 Django 的方式取出:Model.objects.get(...),实际取到的是一个时间对象,而不是时间字符串。这个时间对象其实就是 Python 里面的 timedelta 对象。
这个对象非常强大,它允许你在看似某年某月某日的字符串形式上直接进行加减运算。例如,从 Redis 取出的时间对象可以直接与 timezone.now() 相减,得出预定时间和当前时间的差值,且该差值仍为一个时间对象。
print('========================:',cache.get('seckill_time'),'===============',timezone.now())
timeout = cache.get('seckill_time') - timezone.now() # type:timedelta
print(timeout)
cache.get('seckill_time') 是从 Redis 里取出的时间对象。
这个时间对象之所以能直接进行加减法运算,是因为它重写了 Python 父类中的加减法函数。此外,它还重写了包括比较大小在内的许多基本运算函数。具体可通过 ALT+左键点击查看,这里重点看另外两个函数:seconds 和 microseconds。seconds 效果为忽略天数后将剩余时间转换为秒,microseconds 效果为取秒之后的小数部分。
timeout = cache.get('seckill_time') - timezone.now() # type:timedelta
print(timeout)
print(timeout.seconds)
print(timeout.microseconds)
--------------------------------------
1 day, 0:37:35.099737
2255
99737
以上为个人经验,希望能给大家一个参考,也希望大家多多支持本站。如有错误或未考虑完全的地方,望不吝赐教。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













