Django 时区处理全指南:UTC 配置、Naive/Aware 对象与实战技巧

Noah
Noah
发布时间:2026-10-12| 3人浏览过
原创

本文详解 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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持本站。如有错误或未考虑完全的地方,望不吝赐教。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。