Django时区感知
本案例使用 python3.8 + django3.2.18
项目配置
settings.py
# 时区设置为中国上海 TIME_ZONE = 'Asia/Shanghai' # 开启时区感知功能 USE_TZ = True获取当前时间
执行代码:
from django.utils import timezone from datetime import datetime date_time = datetime.now() zone_time = timezone.now() local_time = timezone.localtime() print("date_time:", type(date_time), date_time, date_time.tzinfo) print("django_time:", type(zone_time), zone_time, zone_time.tzinfo) print("local_time:", type(local_time), local_time, local_time.tzinfo)执行结果:
date_time: <class 'datetime.datetime'> 2025-09-11 10:28:50.410969 None
django_time: <class 'datetime.datetime'> 2025-09-11 02:28:50.410969+00:00 UTC
local_time: <class 'datetime.datetime'> 2025-09-11 10:28:50.410969+08:00 Asia/Shanghai
三者数据类型均为datetime.datetime,主要区别在于时间和时区
源码解析:
datetime.now()python自带的包,一般都用这个获取当前时间,不带时区信息,没啥好说的。
timezone.now()django的包,用于获取当前时间,并且携带时区信息,如果开启了时区感知,则获取的是UTC时区的时间,否则效果和datetime.now()相同
源码:
timezone.localtime()django的包,默认用于获取当前时间,并将其转换为TIME_ZONE所配置的时区;此外这个函数还可以将指定的时间转换为指定的时区,但是要注意传递的时间必须是携带时区信息,否则就会抛出179行的异常:localtime() cannot be applied to a naive datetime,表示这个时间没有时区信息,无法转换到目标时区
源码:
时区转换
from django.utils import timezone from datetime import datetime import pytz date_str = "2025-09-10 10:00:00" # 将字符串格式的时间转换为datetime类型 date_time = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") # 获取当前时区(settings.TIME_ZONE) tz = timezone.get_current_timezone() # 将时间标记为utc时区,timezone默认为当前时区,也可以使用pytz.timezone()指定时区 utc_time = timezone.make_aware(date_time, timezone=timezone.utc) # 转换为指定时区的时间 sh_time = timezone.localtime(utc_time, tz) at_time = timezone.localtime(utc_time, pytz.timezone('Asia/Tokyo')) print("tz:", type(tz), tz) print("date_time:", type(date_time), date_time, date_time.tzinfo) print("utc_time:", type(utc_time), utc_time, utc_time.tzinfo) print("中国上海时间:", type(sh_time), sh_time, sh_time.tzinfo) print("日本东京时间:", type(at_time), at_time, at_time.tzinfo)输出结果:
tz: <class 'pytz.tzfile.Asia/Shanghai'> Asia/Shanghai
date_time: <class 'datetime.datetime'> 2025-09-10 10:00:00 None
utc_time: <class 'datetime.datetime'> 2025-09-10 10:00:00+00:00 UTC
中国上海时间: <class 'datetime.datetime'> 2025-09-10 18:00:00+08:00 Asia/Shanghai
日本东京时间: <class 'datetime.datetime'> 2025-09-10 19:00:00+09:00 Asia/Tokyo
时区感知对数据库操作的影响
创建数据库表:
CREATE TABLE user ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NULL, `update_time` datetime NULL, PRIMARY KEY (`id`) );DajngoModel层:
class User(models.Model): name = models.CharField(max_length=255) update_time = models.DateTimeField() class Meta: db_table = "user"执行代码:
from django.utils import timezone from datetime import datetime import pytz date_str = "2025-09-10 10:00:00" date_time = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S") tz = timezone.get_current_timezone() utc_time = timezone.make_aware(date_time, timezone=timezone.utc) sh_time = timezone.localtime(utc_time, tz) at_time = timezone.localtime(utc_time, pytz.timezone('Asia/Tokyo')) user_1 = User.objects.filter(update_time=date_str) user_2 = User.objects.filter(update_time=date_time) user_3 = User.objects.filter(update_time=utc_time) user_4 = User.objects.filter(update_time=sh_time) user_5 = User.objects.filter(update_time=at_time) print(f"date_str: {date_str}\n{user_1.query}") # 输出sql,实际 where update_time=2025-09-10 02:00:00 print(f"date_time: {date_time}\n{user_2.query}") # 输出sql,实际 where update_time=2025-09-10 02:00:00 print(f"utc_time: {utc_time}\n{user_3.query}") # 输出sql,实际 where update_time=2025-09-10 10:00:00 print(f"sh_time: {sh_time}\n{user_4.query}") # 输出sql,实际 where update_time=2025-09-10 10:00:00 print(f"at_time: {at_time}\n{user_5.query}") # 输出sql,实际 where update_time=2025-09-10 10:00:00输出结果(观察时间变量和sql中的update_time条件值):
date_str: 2025-09-10 10:00:00
SELECT `user`.`id`, `user`.`name`, `user`.`update_time` FROM `user` WHERE `user`.`update_time` = 2025-09-10 02:00:00
date_time: 2025-09-10 10:00:00
SELECT `user`.`id`, `user`.`name`, `user`.`update_time` FROM `user` WHERE `user`.`update_time` = 2025-09-10 02:00:00utc_time: 2025-09-10 10:00:00+00:00
SELECT `user`.`id`, `user`.`name`, `user`.`update_time` FROM `user` WHERE `user`.`update_time` = 2025-09-10 10:00:00sh_time: 2025-09-10 18:00:00+08:00
SELECT `user`.`id`, `user`.`name`, `user`.`update_time` FROM `user` WHERE `user`.`update_time` = 2025-09-10 10:00:00at_time: 2025-09-10 19:00:00+09:00
SELECT `user`.`id`, `user`.`name`, `user`.`update_time` FROM `user` WHERE `user`.`update_time` = 2025-09-10 10:00:00
总结:当开启了时区感知,使用DjangoORM操作数据库时会自动将时间转换为UTC时区,对于没有时区信息的时间,会将其视为settings.py中所配置的TIME_ZONE时区,因此推荐使用timezone.localtime()来获取当前时间
API接口返回指定格式时间
假设现在有一个API接口要返回时间相关的数据:
from django.utils import timezone from django.http import JsonResponse def getTime(request): data = { 'message': '成功获取当前时间', 'localtime': timezone.localtime() } return JsonResponse(data)API接口返回结果:
{
"message": "成功获取当前时间",
"localtime": "2025-09-13T14:22:45.675+08:00"
}
所返回的时间可能并非我们期望的格式,此时可以使用自定义json编码器对数据进行格式化
CustomizeEncoder.py
import json from datetime import datetime from decimal import Decimal from django.utils import timezone # 自定义json编码器,将指定类型的数据进行格式化 class CustomizeEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, datetime): if o.tzinfo: return timezone.localtime(o).strftime('%Y-%m-%d %H:%M:%S') else: return o.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(o, Decimal): return float(o) # 使用默认的编码 return json.JSONEncoder.default(self, o)修改后的代码:
from django.utils import timezone from django.http import JsonResponse from XX.XX.CustomizeEncoder import CustomizeEncoder def getTime(request): data = { 'message': '成功获取当前时间', 'localtime': timezone.localtime() } return JsonResponse(data, encoder=CustomizeEncoder)再次请求API接口,返回结果:
{
"message": "成功获取当前时间",
"localtime": "2025-09-13 14:40:59"
}