Django中的时间问题

参考 => Django中与时区相关的安全问题

两种时间对象aware datetime vs naive datetime

aware datime               naive datetime

tzinfo属性有设置时区值      无

可以换成unix时间戳          无法直接转换成一个准确unix时间戳(会以当前系统时区为准)

from datetime import datetime
from django.utils import timezone

t = datetime.now()

print(timezone.is_aware(t))
print(timezone.is_naive(t))

# t是一个naive datetime,因为我们没有给他设置时区

Django的时区 USE_TZ TIME_ZONE

USE_TZ用来指定整个项目是否使用时区

如果USE_TZ的值设置为False,那么Django项目中所有时间都使用naive datetime(除非有明确指定时区的情况)

弊端:
    数据库中保存的是naive datetime,导致在跨区域迁移数据的时候,可能无法准确定位到某个时间点
    国际化企业可能面向不同国家有不同的网站,但后台数据库相同,此时究竟使用哪个时区保存和展示时间,将引起混乱
    
    即使是同一个网站的用户,他们可能来自于全球各地,查看到的时间却是统一的服务器时间,对于高交互式的应用十分不友好
    
    即使网站面向的用户仅来自于某一个地区,也会涉及到“夏时令”(Daylight Saving Time)相关的问题,
    每年可能将会导致两次时间误差

TIME_ZONE是默认时区的值

    默认情况下,用django-admin生成的项目,其设置中USE_TZ等于True,这也是Django官方建议的配置。
    
    此时  
          在网站内部存储与使用的是UTC时间
    
          而与用户交互时使用TIME_ZONE或手工的时区

使用Django的 时间函数django.utils.timezone

now(),返回当前的UTC时间

localtime(),返回当前的本地时间(默认是TIME_ZONE配置指定的时区时间)

is_aware(),传入的时间是否是aware datetime

is_naive(),传入的时间是否是naive datetime

make_aware(),将naive时间转换成aware时间

make_naive(),将aware时间转换成naive时间

注意:

我们在获取当前时间的时候,一定要使用Django自带的now()localtime()函数, 而不能使用Python的datetime.datetime.now()函数

数据库存储的时间 ORM 的 DatetimeField


class Archive(models.Model):
    title = models.CharField('title', max_length=256)

    now_time = models.DateTimeField(default=timezone.now)
    local_time = models.DateTimeField(default=timezone.localtime)
    
    
    # 不管传入的时间对象时区是什么,其内部存储的时间均为UTC时区
    
    # 不管使用a.now_time还是a.local_time,读取到的datetime对象的tzinfo都是UTC
    

返回给前端展示需要进行时区转换

data = dict(
        id=object.pk,
        now_time=object.now_time,
        local_time=timezone.localtime(object.local_time) # 转换本地时间
    )
    return JsonResponse(data=data)

时间比较

1) 不涉及__day、__date、__year等时间lookups,使用任何aware时间均可(会被自动转换成UTC)

    timezone.now timezone.localtime  都可以
from django.db import models
from django.utils import timezone

class Account(models.Model):
    username = models.CharField(max_length=256)
    password = models.CharField(max_length=64)

    created_time = models.DateTimeField(default=timezone.now)
    expired_time = models.DateTimeField()
    

if models.Account.objects.filter(expired_time__gt=timezone.now()).exists():
    ...
    
    

2) queryset查询,涉及到时间lookups时,使用本地时间

timezone.localtime

Django在使用日期、时间有关的lookups时,会在数据库层面对时间进行时区的转换再进行比较, 所以我们需要使用本地时间而不是UTC时间

models.Account.objects.filter(created_time__day=timezone.localtime().day).all()

# SQL语句中使用了django_datetime_extract('day', "sample_account"."created_time", 'Asia/Shanghai', 'UTC')
# 将UTC时间转换成了北京时间

注意:

任何比较都使用aware时间,不能使用naive时间

时间属性直接比较时,使用任何aware时间均可(会被自动转换成UTC)

queryset查询,不涉及__day、__date、__year等时间lookups时,使用任何aware时间均可(会被自动转换成UTC)

queryset查询,涉及到时间lookups时,使用本地时间
Buy me a 肥仔水!