现在容器化部署已经很成熟了,我们很多服务都会使用容器部署,更新恢复也很方便,但是有一个问题比较麻烦,即时区处理。通常,都是通过注入TZ环境变量来解决的,但是实际的处理方法在django中是没有的。Django中时区相关的配置在Django的配置文件settings.py中,有两个与时间和时区相关的配置参数,分别是TIME_ZONE和USE_TZ。我们期望在settings.py中配置后,Django能够正确获取本地时间,但实际上适得其反。让我们来看看这两个设置的作用。USE_TZ=True如果USE_TZ设置为True,Django会默认使用系统设置的时区。这时候TIME_ZONE的设置基本无效,也就是设置不设置都不起作用。USE_TZ=False如果USE_TZ设置为False,TIME_ZONE设置为None,Django仍将使用默认时区。如果TIME_ZONE设置为其他时区,如果是Windows系统,TIME_ZONE设置是没有用的。Django将使用本地时间。如果是其他系统,则使用时区的UTC时间。例如,如果USE_TZ=False,TIME_ZONE='Asia/Shanghai',将使用上海的UTC时间。此时,您可能认为时机已经成熟,但实际上还没有。我们还需要注意系统时区的设置。Linux容器中的时区设置现在是我本地时间:16:15,而Django中的设置是:USE_TZ=False,TIME_ZONE='Asia/Shanghai'不要在容器中注入TZ=Asia/Shanghai环境变量查看容器时间和时区系统时间显示UTC时区,时间为:08:15,刚好8小时。进入Django环境查看时区pythonmanage.pyshellfromdatetimeimportdatetimedatetime.now()#outputdatetime.datetime(2021,10,8,8,24,8,289230)fromdjango.utilsimporttimezonetimezone.get_current_timezone_name()#Output'Asia/Shanghai'并在容器中注入环境变量TZ=Asia/Shanghai查看时间和时区系统时间显示的是亚洲时区,但是时间还是UTC时间,并没有显示实时时间输入本地时间进入Django环境查看时区pythonmanage.pyshellfromdatetimeimportdatetimedatetime.now()#outputdatetime.datetime(2021,10,8,8,24,8,289230)fromdjango.utilsimporttimezonetimezone.get_current_timezone_name()#output'Asia/上海'可以看到虽然时区变了,但是时间还是UTC时间。无论是容器本身还是Django,我们都知道要修改Linux系统的时区,需要修改/etc/localtime这个文件。通常修改linux容器时区的方法是将宿主机的/etc/localtime文件复制到容器的/etc/localtime文件中,但是我们发现/etc/localtime文件其实只是一个软连接通过查询,实际文件为:/usr/share/zoneinfo/Asia/Shanghaidockercp/usr/share/zoneinfo/Asia/Shanghaitest:/etc/localtime在不向容器注入TZ=Asia/Shanghai环境变量的情况下,我们登录容器,发现容器的系统时间已经正确获取了本地时间和时区。如果注入了TZ=Asia/Shanghai环境变量,即使/etc/下的localtime文件被替换了,也只是时区发生了变化。时间仍然是UTC。进入Django环境查看时间pythonmanage.pyshellfromdatetimeimportdatetimedatetime.now()#outputdatetime.datetime(2021,10,8,8,43,43,754698)Linux时间已经正常了,但是Django环境下的时间是仍然不正确,它仍然是UTC时间。这个时候,很多人都有些抓狂了。他们可能会认为settings.py中的USE_TZ和TIME_ZONE设置有问题,但问题不在这里原因是datetime库会在/usr/share/zoneinfo/目录下寻找Asia/Shanghai文件,但是我们的镜像不包含这个目录,所以Django仍然使用UTC时区。解决方法很简单:创建/usr/share/zoneinfo/Asia目录,并将文件复制到该目录。#容器内(如果该目录不存在)mkdir-p/usr/share/zoneinfo/Asia#容器外dockercp/usr/share/zoneinfo/Asia/Shanghaitest:/usr/share/zoneinfo/Asia/Shanghai,然后登录容器,进入Django环境查看时间pythonmanage.pyshellfromdatetimeimportdatetimedatetime.now()#outputdatetime.datetime(2021,10,8,16,49,32,57)现在时间正好。总结一下容器时区的问题,建议在容器生产阶段安装设置/etc/localtime,例如添加如下语句ADD/usr/share/zoneinfo/Asia/Shanghai/usr/share/dockerfile-s/usr/share/zoneinfo/Asia/Shanghai/etc/localtime中的zoneinfo/Asia/ShanghaiRUNln这样我们的容器在启动的时候就不需要关注时区了。如果容器已经制作好,启动时挂载时区文件dockerrun-d-v/etc/localtime:/etc/localtime-v/usr/share/zoneinfo/Asia/Shanghai:/usr/share/zoneinfo/Asia/ShanghaiimageName为比较麻烦。还有一种我们现在遇到的情况,服务已经上线了,如果时间有问题,手动把这两个文件复制到容器中,然后重启容器dockercp/usr/share/zoneinfo/Asia/Shanghaitest:/etc/localtimedockercp/usr/share/zoneinfo/Asia/Shanghaitest:/usr/share/zoneinfo/Asia/Shanghaidockerrestarttest
