彻底理解 Django 中的时区 TIME_ZONE 和 USE_TZ 的设置 timezone的使用
开始之前你需要了解下面的知识!!!
1. 时区 以及 UTC 和DST
时区:由于世界各国家与地区经度不同,地方时也有所不同,因此会划分为不同的时区。【参考百度百科】
UTC: 可以视为一个世界统一的时间,以原子时为基础,其他时区的时间都是根据自己所在的时区在这个基础上增加或减少的,比如中国的时区就为 UTC+8。【参考百度百科】
DST(夏时制, day saving time)则是为了充分利用夏天日照长的特点,充分利用光照节约能源而人为调整时间的一种机制。在夏天将时间人为的向前加一小时,使人们早睡早起节约能源。虽然很多西方国家都采用了DST,但是中国不采用DST。【参考百度百科】
在现实环境中,存在有多个时区。用户之间很有可能存在于不同的时区(不同时区时间UTC +差值),并且许多国家都拥有自己的一套夏令时系统(用到夏时令时也是要进行+-1)。所以如果网站面向的是多个时区用户,只以当前本地时间为标准开发,是会对不同时区的用户产生影响的。
2.Naive 和 Aware 类型的 datetime 对象
Python 的 datatime.datetime对象有一个 tzinfo 属性,该属性是 datetime.tzinfo 子类的一个实例,他被用来存储时区信息。
当某个 datetime 对象的 tzinfo 属性被设置为一个时区,并给出一个时间偏移量时,我们称该 datetime 对象是 aware (已知) 的。否则称其为 naive (原生) 的。
naive 是tzinfo=None 的datetime 对象,
aware 是 tzinfo=某个时区 的 datetime对象
可以使用 from django.utils import timezone 类的 is_aware() 和 is_naive() 方法来判断某个 datetime 对象是 aware 类型或 naive 类型。
3.datetime类和 timezone 类
python 自带的 datetime模块下的 datetime 类 和 Django 的 utils 模块下的 timezone 类
datetime 类: 这个是python 自带的类,当然也可以在 django 的代码中使用
timezone类:是django 中 处理 datetime 时候用的类,是对 dateime 类的二次封装
4.Django 中的 TIME_ZONE 和 USE_TZ 参数
在 settings 中 TIME_ZOME 和 USE_TZ 两个参数会对django 在使用处理 时间日期时起到影响
4.1.TIME_ZONE
默认即不设置时time_zone时,值为America/Chicago ;设置time_zone为某个时区时, 则为设置的值
TIME_ZONE所做的动作:
在Unix环境(time.tzset()已实现)上,Django将os.environ['TZ'] 变量设置为 您在TIME_ZONE设置中指定的时区 。因此,您所有的视图和模型都将在该时区自动运行。但是,TZ 如果您使用手动配置的选项(settings.configure(DEBUG=True)),则Django不会设置环境变量。如果Django没有设置TZ 环境变量,则取决于您的进程在正确的环境中运行。
在 windows 中,TIME_ZONE 要和系统的设置为一样,因为它不能在windows 中设定 time zones 环境变量
USE_TZ 对 TIME_ZONE 的影响:
USE_TZ=True:当设置为 true 时候,则在 template 和 forms 中时间显示会自动转为这个timezone的navie 类型的datetime对象
USE_TZ=False:不使用 aware 类型的datetime ;django 用到的所有地方都是这个timezone 的 navie 类型的 datetime对象
我们看看 Django 源码,在django初始化时设置的 TIME_ZONE 具体做了什么
from django.conf import settings # 看到如下代码 class Settings: def __init__(self, settings_module): # update this dict from global settings (but only for ALL_CAPS settings) for setting in dir(global_settings): if setting.isupper(): setattr(self, setting, getattr(global_settings, setting)) # store the settings module in case someone later cares self.SETTINGS_MODULE = settings_module mod = importlib.import_module(self.SETTINGS_MODULE) tuple_settings = ( "INSTALLED_APPS", "TEMPLATE_DIRS", "LOCALE_PATHS", ) self._explicit_settings = set() for setting in dir(mod): if setting.isupper(): setting_value = getattr(mod, setting) if (setting in tuple_settings and not isinstance(setting_value, (list, tuple))): raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting) setattr(self, setting, setting_value) self._explicit_settings.add(setting) if not self.SECRET_KEY: raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.") if self.is_overridden('FILE_CHARSET'): warnings.warn(FILE_CHARSET_DEPRECATED_MSG, RemovedInDjango31Warning) if hasattr(time, 'tzset') and self.TIME_ZONE: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. zoneinfo_root = Path('/usr/share/zoneinfo') zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split('/')) if zoneinfo_root.exists() and not zone_info_file.exists(): raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE) # Move the time zone info into os.environ. See ticket #2315 for why # we don't do this unconditionally (breaks Windows). os.environ['TZ'] = self.TIME_ZONE time.tzset()
毫无疑问,首先会访问django.conf.__init__.py文件。在这里settings是一个lazy object,当访问settings的时候,真正实例化的是以下这一个Settings类。
可以看到如果设置了 TIME_ZONE 会将os.environ['TZ'] 设置为 TIME_ZOME 变量的值
4.2. USE_TZ
一个布尔值,用于指定datetime对象 默认情况下是否是 aware 类型
如果设置为False,django 使用 naive 类型的时间
如果设置为True,Django使用 aware即带时区的日期时间。
4.3.TIME_ZONE 和 USE_TZ 的影响面
有了上面的知识,下面就来说说 django 中的时区问题,即对 settings 中的 TIME_ZONE 和 USE_TZ 设置及其产生的影响。
仅对 datetime类和 timezone类分析:
TIME_ZONE 的实质是改变Django 运行环境的时区;所以它会影响到 datetime 和timezone的时区。
USE_TZ 它只作用于timezone,不会影响 datetime 类,datetime.now() 它返回的永远都是一个 naive 类型的 datetime 对象。
from django.utils.timeout import now # 源码如下 def now(): """ Return 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()
要看 TIME_ZONE 和 USE_TZ 对 DJANGO 最全面影响----读源码
方法:在 Django 模块中
cd /home/vagrant/.pyenv/versions/3.8.3/lib/python3.8/site-packages/django grep "TIME_ZONE" ./ -r grep "USE_TZ" ./ -r
5.timezone类使用
In [67]: dir(timezone) Out[67]: ['ContextDecorator', 'FixedOffset', 'Local', 'RemovedInDjango31Warning', 'ZERO', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_active', '_get_timezone_name', 'activate', 'datetime', 'deactivate', 'functools', 'get_current_timezone', 'get_current_timezone_name', 'get_default_timezone', 'get_default_timezone_name', 'get_fixed_timezone', 'is_aware', 'is_naive', 'localdate', 'localtime', 'make_aware', 'make_naive', 'now', 'override', 'pytz', 'settings', 'template_localtime', 'timedelta', 'timezone', 'tzinfo', 'utc', 'warnings']
这里不做全面赘述,只将几个重点方法:
is_aware: 是否是一个 aware 类型的 datetime(即带时区)
is_naive:是否是一个 naive类型的 datetime
make_aware:将 naive 转为 aware
make_naive:将 aware 转为 naive
now:
若USE_TZ=True返回一个时区是 utc的 aware datetime对象;
若USE_TZ=False 等同于 datetime.datetime.now() 返回一个 naive 的datetime对象
localtime / localdate:
若USE_TZ=True返回一个时区是 TIME_ZONE的 aware datetime对象
若USE_TZ=False 报错
datetime: 导入的是 python datetime,即 timezone.datetime == datetime.datetime
6.总结
在Django 中:
建议 开启 USE_TZ;
代码要用到 datetime,使用 django 的timzone 函数
在所有的系统中:
强烈建议在代码和数据库中统一使用 UTC 时间
仅在与最终用户进行展示时使用本地时间
共 0 条评论