Django学习笔记4 解决Django时区设置问题(从官方文档到源码)

问题背景

前段时间一直长期使用Django做一些项目开发,但因为时间比较紧,很多问题没有注意,比如库表中的时间戳一直使用的都是UTC时间,而在中国的话我们使用UTC+8时间才顺眼呀(比如现在是北京时间14:57那么UTC时间就是6:57)。这不是一个很大的问题,通过修改几个配置项即可完成,但是作为开发者你一定要做到对你所做的修改有了解,就像Linux经常提醒的一样“你一定要知道自己在做什么”。

问题现场

  • 北京时间:2018年8月18日 19:05
  • Django内部时间:2018年8月18日11:05
    这里写图片描述

问题分析

timezone.now这个函数是来自django.utils包中的,也是django的时间基础,按照目前的情况,我们django项目中所有的时间都会被影响,比如对数据库中设置了自动更新时间戳的自断进行更新操作的时候,这个时间就会默认成UTC时间。
解决这个问题的第一想法就是在配置文件中找到相关配置去解决,我确实找到了这个参数,如下所示。
Django学习笔记4 解决Django时区设置问题(从官方文档到源码)_第1张图片

可以看出TIME_ZONE这个参数就是导致我们这个问题的罪魁祸首了,但是我不知道该如何去正确修改这个参数,我们先去求助下官方文档。官方文档传送们通过阅读官方文档我发现想解决我们的问题不仅要使用TIME_ZONE参数而且还要使用USE_TZ参数。我截取官方文档中的一段话来做证明。

  • 原文(原为中this代表TIME_ZONE的值)

    When USE_TZ is False, this is the time zone in which Django will store all datetimes. When USE_TZ is True, this is the default time zone that Django will use to display datetimes in templates and to interpret datetimes entered in forms.

  • 译文
    USE_TZ的值是False的时候,这个值代表Django存储所有时间所使用的时区。当USE_TZ的值是True的时候,这个值将是Django在前端中显示与form表单中输入的默认时间所使用的时区。

大家看完这里可能还是没有理解到底该如何解决我们的这个问题,但是可以确定光通过TIME_ZONE一个参数是无法解决问题的,还要理解USE_TZ的用法,下面我用实验来展示下这两个参数的作用。

实验验证

实验的思路来源依旧来自官方文档关于timezones的文章传送门,有兴趣的朋友可以好好读一读。
根据上一节中官方文档对这两个参数的表述,USE_TZ=True的时候Django的templates中会使用TIME_ZONE的值而其他情况不会使用,当USE_TZ=False的时候Django在所有情况下都会使用TIME_ZONE的值。

那么下面我们就实验下使用Django templates来显示时间。关键参数的设置如下

实验一

  • USE_TZ=True TIME_ZONE=Asia/Shanghai

templates代码:

{% load tz %}

{% timezone None %}
    Paris time: {{ value }}
{% endtimezone %}

我们访问一下这个页面:
这里写图片描述
看到时间是2018年8月19日下午11:05和我现在的是
这里写图片描述(因为访问页面的时候和截真实时间的时候有一个时间差,所以不完全一样)

现在可以证明在前端显示的时候我们所使用的时区就是我在TIME_ZONE中设置的。但是在其他情况下呢,比如数据库中。
正好的这个Django项目有一个User表,里面有一个字段是创建用户的时间,这个表的model如下所示。
Django学习笔记4 解决Django时区设置问题(从官方文档到源码)_第2张图片
现在我们测试一下在这个表中创建一个用户,这个add_date字段会是什么。我们使用django的shell工具来做这些操作。

  • 首先在User表中创建一条数据

Django学习笔记4 解决Django时区设置问题(从官方文档到源码)_第3张图片

  • 之后查看现在的实际时间

Django学习笔记4 解决Django时区设置问题(从官方文档到源码)_第4张图片

  • 最后看下库表中的时间(库表中的时间是2018年8月20日 4:47:55)

Django学习笔记4 解决Django时区设置问题(从官方文档到源码)_第5张图片

通过上面的实验我们也就理解了Django官方文档中对TIME_ZONE与USE_TZ的解释了。

实验二

为了完整性,我们在看下另外一种情况,也就是USE_TZ=False的情况,根据文档所说所有的时间都会设置为TIME_ZONE所在的时区。
当前时间:
这里写图片描述
templates的时间:
这里写图片描述

在向User表中添加一条数据,查看时间。
当前时间:
这里写图片描述
数据库中的时间:
这里写图片描述

实验总结

两个实验表现了USE_TZ=True与False的时候Django所表现的对时间的不同处理方法,现在回过头来看我的问题,就是想让库表的时间戳变成与我当地时间一致,那么我直接将USE_TZ=False,TIME_ZONE=我的本地时区 就可以完成了,但是这样真的好吗?对于在Asia/Shanghai这个时区的我来说这样做没有任何问题,但是如果某些开发者他们处在一个夏令时制的国家就会触发一些你无法理解的bug,Django默认给我们设置了USE_TZ=True不是没有道理的,因为无论你身处何地,UTC时间是唯一不变的一个尺度,防止了很多莫名其妙的BUG产生,对这部分感兴趣的朋友移步官方文档仔细学习传送门。

源码中的答案

在查找过官方文档后,我又去看了Django中timezone.now()的源码,其实源码表现的反而异常简单。

def now():
    """
    Returns an aware or naive datetime.datetime, depending on settings.USE_TZ.
    """
    if settings.USE_TZ:
        # timeit shows that datetime.now(tz=utc) is 24% slower
        return datetime.utcnow().replace(tzinfo=utc)
    else:
        return datetime.now()

这里的USE_TZ就来自于配置文件,可以看到当其值为True的时候,那么就使用utc时间,否则就使用当地时间,当然这只是一个小部分的源码,其他部分比如template对时间的实现还要仔细挖掘。

你可能感兴趣的:(Django)