最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Django時(shí)區(qū)詳解

 更新時(shí)間:2019年07月24日 09:12:30   作者:codeLeaves  
這篇文章主要介紹了Django時(shí)區(qū)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

引言

相信使用Django的各位開(kāi)發(fā)者在存儲(chǔ)時(shí)間的時(shí)候經(jīng)常會(huì)遇到這樣子的錯(cuò)誤:

RuntimeWarning: DateTimeField received a naive datetime while time zone support is active.

這個(gè)錯(cuò)誤到底是什么意思呢?什么是naive datetime object?什么又是aware datetime object?

在Django配置中如果將settings.TIME_ZONE設(shè)置為中國(guó)時(shí)區(qū)(Asia/Shanghai),為什么以下時(shí)間函數(shù)會(huì)得到時(shí)間相差較大的結(jié)果?

# settings.py
TIME_ZONE = 'Asia/Shanghai'

# python manage.py shell
>>> from datetime import datetime
>>> datetime.now()
datetime.datetime(2016, 12, 7, 12, 41, 22, 729326)
>>> from django.utils import timezone
>>> timezone.now()
datetime.datetime(2016, 12, 7, 4, 41, 36, 685921, tzinfo=<UTC>)

接下來(lái)筆者將詳細(xì)揭秘在Django中關(guān)于時(shí)區(qū)的種種內(nèi)幕,如有不對(duì),敬請(qǐng)指教。

準(zhǔn)備

UTC與DST

UTC可以視為一個(gè)世界統(tǒng)一的時(shí)間,以原子時(shí)為基礎(chǔ),其他時(shí)區(qū)的時(shí)間都是在這個(gè)基礎(chǔ)上增加或減少的,比如中國(guó)的時(shí)區(qū)就為UTC+8。

DST(夏時(shí)制)則是為了充分利用夏天日照長(zhǎng)的特點(diǎn),充分利用光照節(jié)約能源而人為調(diào)整時(shí)間的一種機(jī)制。通過(guò)在夏天將時(shí)間向前加一小時(shí),使人們?cè)缢缙鸸?jié)約能源。雖然很多西方國(guó)家都采用了DST,但是中國(guó)不采用DST。(資料來(lái)源:DST 百度百科)

naive datetime object vs aware datetime object

當(dāng)使用datetime.now()得到一個(gè)datetime對(duì)象的時(shí)候,此時(shí)該datetime對(duì)象沒(méi)有任何關(guān)于時(shí)區(qū)的信息,即datetime對(duì)象的tzinfo屬性為None(tzinfo屬性被用于存儲(chǔ)datetime object關(guān)于時(shí)區(qū)的信息),該datetime對(duì)象就被稱為naive datetime object。

>>> import datetime
>>> naive = datetime.datetime.now()
>>> naive.tzinfo
>>>

既然naive datetime object沒(méi)有關(guān)于時(shí)區(qū)的信息存儲(chǔ),相對(duì)的aware datetime object就是指存儲(chǔ)了時(shí)區(qū)信息的datetime object。
在使用now函數(shù)的時(shí)候,可以指定時(shí)區(qū),但該時(shí)區(qū)參數(shù)必須是datetime.tzinfo的子類。(tzinfo是一個(gè)抽象類,必須有一個(gè)具體的子類才能使用,筆者在這里使用了pytz.utc,在Django中的timezone源碼中也實(shí)現(xiàn)了一個(gè)UTC類以防沒(méi)有pytz庫(kù)的時(shí)候timezone功能能正常使用)

>>> import datetime
>>> import pytz
>>> aware = datetime.datetime.now(pytz.utc)
>>> aware
datetime.datetime(2016, 12, 7, 8, 32, 7, 864077, tzinfo=<UTC>)
>>> aware.tzinfo
<UTC>

 在Django中提供了幾個(gè)簡(jiǎn)單的函數(shù)如is_aware, is_naive, make_aware和make_naive用于辨別和轉(zhuǎn)換naive datetime object和aware datetime object。

datetime.now簡(jiǎn)析

在調(diào)用datetime.now()的時(shí)候時(shí)間是如何返回的呢?在官方文檔里只是簡(jiǎn)單地說(shuō)明了now函數(shù)返回當(dāng)前的具體時(shí)間,以及可以�指定時(shí)區(qū)參數(shù),并沒(méi)有具體的說(shuō)明now函數(shù)的實(shí)現(xiàn)。

classmethod datetime.now(tz=None)
Return the current local date and time. If optional argument tz is None or not specified, this is like today(), but, if possible, supplies more precision than can be gotten from going through a time.time() timestamp (for example, this may be possible on platforms supplying the C gettimeofday() function).

If tz is not None, it must be an instance of a tzinfo subclass, and the current date and time are converted to tz's time zone. In this case the result is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). See also today(), utcnow().

OK,那么接下來(lái)直接從datetime.now()的源碼入手吧。

@classmethod
 def now(cls, tz=None):
  "Construct a datetime from time.time() and optional time zone info."
  t = _time.time()
  return cls.fromtimestamp(t, tz)

大家可以看到datetime.now函數(shù)通過(guò)time.time()返回了一個(gè)時(shí)間戳,然后調(diào)用了datetime.fromtimestamp()將一個(gè)時(shí)間戳轉(zhuǎn)化成一個(gè)datetime對(duì)象。

那么,不同時(shí)區(qū)的時(shí)間戳?xí)粫?huì)不一樣呢?不,時(shí)間戳不會(huì)隨著時(shí)區(qū)的改變而改變,時(shí)間戳是唯一的,被定義為格林威治時(shí)間1970年01月01日00時(shí)00分00秒(北京時(shí)間1970年01月01日08時(shí)00分00秒)起至現(xiàn)在的總秒數(shù)。

datetime.fromtimestamp

既然時(shí)間戳不會(huì)隨時(shí)區(qū)改變,那么在fromtimestamp中應(yīng)該對(duì)時(shí)間戳的轉(zhuǎn)換做了時(shí)區(qū)的處理。

直接上源碼:

 @classmethod
 def _fromtimestamp(cls, t, utc, tz):
  """Construct a datetime from a POSIX timestamp (like time.time()).

  A timezone info object may be passed in as well.
  """
  frac, t = _math.modf(t)
  us = round(frac * 1e6)
  if us >= 1000000:
   t += 1
   us -= 1000000
  elif us < 0:
   t -= 1
   us += 1000000

  converter = _time.gmtime if utc else _time.localtime
  y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
  ss = min(ss, 59) # clamp out leap seconds if the platform has them
  return cls(y, m, d, hh, mm, ss, us, tz)

 @classmethod
 def fromtimestamp(cls, t, tz=None):
  """Construct a datetime from a POSIX timestamp (like time.time()).

  A timezone info object may be passed in as well.
  """
  _check_tzinfo_arg(tz)

  result = cls._fromtimestamp(t, tz is not None, tz)
  if tz is not None:
   result = tz.fromutc(result)
  return result

當(dāng)直接調(diào)用datetime.now()的時(shí)候,并沒(méi)有傳進(jìn)tz的參數(shù),因此_fromtimestamp中的utc參數(shù)為False,所以converter被賦值為time.localtime函數(shù)。

time.localtime

localtime函數(shù)的使用只需要知道它返回一個(gè)九元組表示當(dāng)前的時(shí)區(qū)的具體時(shí)間即可:

def localtime(seconds=None): # real signature unknown; restored from __doc__
 """
 localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,
        tm_sec,tm_wday,tm_yday,tm_isdst)

 Convert seconds since the Epoch to a time tuple expressing local time.
 When 'seconds' is not passed in, convert the current time instead.
 """
 pass

筆者覺(jué)得更需要注意的是什么因素影響了time.localtime返回的時(shí)區(qū)時(shí)間,那么,就需要談及time.tzset函數(shù)了。

在Python官方文檔中關(guān)于time.tzset函數(shù)解釋如下:

time.tzset()

Resets the time conversion rules used by the library routines. The environment variable TZ specifies how this is done.

Availability: Unix.

Note Although in many cases, changing the TZ environment variable may affect the output of functions like localtime() without calling tzset(), this behavior should not be relied on.
The TZ environment variable should contain no whitespace.

可以看到,一個(gè)名為TZ的環(huán)境變量的設(shè)置會(huì)影響localtime的時(shí)區(qū)時(shí)間的返回。(有興趣的同學(xué)可以去在Unix下執(zhí)行man tzset,就知道TZ變量是如何影響localtime了)

最后,筆者給出一些測(cè)試的例子,由于獲取的時(shí)間戳不隨時(shí)間改變,因此直接調(diào)用fromtimestamp即可:

>>> from datetime import datetime
>>> from time import time, tzset
>>> china = datetime.fromtimestamp(time())
>>> import os
>>> os.environ['TZ'] = 'UTC'
>>> tzset()
>>> utc = datetime.fromtimestamp(time())
>>> china
datetime.datetime(2016, 12, 7, 16, 3, 34, 453664)
>>> utc
datetime.datetime(2016, 12, 7, 8, 4, 30, 108349)

以及直接調(diào)用localtime的例子:

>>> from time import time, localtime, tzset
>>> import os
>>> china = localtime()
>>> china
time.struct_time(tm_year=2016, tm_mon=12, tm_mday=7, tm_hour=16, tm_min=7, tm_sec=5, tm_wday=2, tm_yday=342, tm_isdst=0)
>>> os.environ['TZ'] = 'UTC'
>>> tzset()
>>> utc = localtime()
>>> utc
time.struct_time(tm_year=2016, tm_mon=12, tm_mday=7, tm_hour=8, tm_min=7, tm_sec=34, tm_wday=2, tm_yday=342, tm_isdst=0)
 

(提前劇透:TZ這一個(gè)環(huán)境變量在Django的時(shí)區(qū)中發(fā)揮了重大的作用)

Django TimeZone

timezone.now() vs datetime.now()

筆者在前面花費(fèi)了大量的篇幅來(lái)講datetime.now函數(shù)的原理,并且提及了TZ這一個(gè)環(huán)境變量,這是因?yàn)樵贒jango導(dǎo)入settings的時(shí)候也設(shè)置了TZ環(huán)境變量。

當(dāng)執(zhí)行以下語(yǔ)句的時(shí)候:

from django.conf import settings

毫無(wú)疑問(wèn),首先會(huì)訪問(wèn)django.conf.__init__.py文件。

在這里settings是一個(gè)lazy object,但是這不是本章的重點(diǎn),只需要知道當(dāng)訪問(wèn)settings的時(shí)候,真正實(shí)例化的是以下這一個(gè)Settings類。

class Settings(BaseSettings):
 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 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 = '/usr/share/zoneinfo'
   if (os.path.exists(zoneinfo_root) and not
     os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))):
    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()

 def is_overridden(self, setting):
  return setting in self._explicit_settings

 def __repr__(self):
  return '<%(cls)s "%(settings_module)s">' % {
   'cls': self.__class__.__name__,
   'settings_module': self.SETTINGS_MODULE,
  }

在該類的初始化函數(shù)的最后,可以看到當(dāng)USE_TZ=True的時(shí)候(即開(kāi)啟Django的時(shí)區(qū)功能),設(shè)置了TZ變量為settings.TIME_ZONE。

OK,知道了TZ變量被設(shè)置為TIME_ZONE之后,就能解釋一些很奇怪的事情了。

比如,新建一個(gè)Django項(xiàng)目,保留默認(rèn)的時(shí)區(qū)設(shè)置,并啟動(dòng)django shell:

# settings.py
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

# python3 manage.py shell
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2016, 12, 7, 9, 19, 34, 741124)
>>> datetime.datetime.utcnow()
datetime.datetime(2016, 12, 7, 9, 19, 45, 753843)

默認(rèn)的Python Shell通過(guò)datetime.now返回的應(yīng)該是當(dāng)?shù)貢r(shí)間,在這里即中國(guó)時(shí)區(qū),但是當(dāng)settings.TIME_ZONE設(shè)置為UTC的時(shí)候,通過(guò)datetime.now返回的就是UTC時(shí)間。

可以試試將TIME_ZONE設(shè)置成中國(guó)時(shí)區(qū):

# settings.py
TIME_ZONE = 'Asia/Shanghai'

# python3 manage.py shell
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2016, 12, 7, 17, 22, 21, 172761)
>>> datetime.datetime.utcnow()
datetime.datetime(2016, 12, 7, 9, 22, 26, 373080)

此時(shí)datetime.now返回的就是中國(guó)時(shí)區(qū)了。

當(dāng)使用timezone.now函數(shù)的時(shí)候,情況則不一樣,在支持時(shí)區(qū)功能的時(shí)候,該函數(shù)返回的是一個(gè)帶有UTC時(shí)區(qū)信息的aware datetime obeject,即它不受TIME_ZONE變量的影響。

直接看它的源碼實(shí)現(xiàn):

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()
 

不支持時(shí)區(qū)功能,就返回一個(gè)受TIME_ZONE影響的naive datetime object。

實(shí)踐場(chǎng)景

假設(shè)現(xiàn)在有這樣一個(gè)場(chǎng)景,前端通過(guò)固定格式提交一個(gè)時(shí)間字符串供后端的form驗(yàn)證,后端解析得到datetime object之后再通過(guò)django orm存儲(chǔ)到DatetimeField里面。

Form.DateTimeField

在django關(guān)于timezone的官方文檔中,已經(jīng)說(shuō)明了經(jīng)過(guò)form.DatetimeField返回的在cleaned_data中的時(shí)間都是當(dāng)前時(shí)區(qū)的aware datetime object。

Time zone aware input in forms¶

When you enable time zone support, Django interprets datetimes entered in forms in the current time zone and returns aware datetime objects in cleaned_data.

If the current time zone raises an exception for datetimes that don't exist or are ambiguous because they fall in a DST transition (the timezones provided by pytz do this), such datetimes will be reported as invalid values.

Models.DatetimeField

在存儲(chǔ)時(shí)間到MySQL的時(shí)候,首先需要知道在Models里面的DatetimeField通過(guò)ORM映射到MySQL的時(shí)候是什么類型。
筆者首先建立了一個(gè)Model作為測(cè)試:

# models.py
class Time(models.Model):

 now = models.DateTimeField()

# MySQL Tables Schema
+-------+-------------+------+-----+---------+----------------+
| Field | Type  | Null | Key | Default | Extra   |
+-------+-------------+------+-----+---------+----------------+
| id | int(11)  | NO | PRI | NULL | auto_increment |
| now | datetime(6) | NO |  | NULL |    |
+-------+-------------+------+-----+---------+----------------+

可以看到,在MySQL中是通過(guò)datetime類型存儲(chǔ)Django ORM中的DateTimeField類型,其中datetime類型是不受MySQL的時(shí)區(qū)設(shè)置影響,與timestamp類型不同。

關(guān)于datetime和timestamp類型可以參考這篇文章。

因此,如果筆者關(guān)閉了時(shí)區(qū)功能,卻向MySQL中存儲(chǔ)了一個(gè)aware datetime object,就會(huì)得到以下報(bào)錯(cuò):

"ValueError: MySQL backend does not support timezone-aware datetimes. "

關(guān)于對(duì)時(shí)區(qū)在業(yè)務(wù)開(kāi)發(fā)中的一些看法

后端應(yīng)該在數(shù)據(jù)庫(kù)統(tǒng)一存儲(chǔ)UTC時(shí)間并返回UTC時(shí)間給前端,前端在發(fā)送時(shí)間和接收時(shí)間的時(shí)候要把時(shí)間分別從當(dāng)前時(shí)區(qū)轉(zhuǎn)換成UTC發(fā)送給后端,以及接收后端的UTC時(shí)間轉(zhuǎn)換成當(dāng)?shù)貢r(shí)區(qū)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python中的裝飾器類詳解

    Python中的裝飾器類詳解

    Python?裝飾器在很多情況下是一個(gè)非常有用的工具,它們可以用于修改或增強(qiáng)函數(shù)或類的行為,本篇文章將深入探討如何在?Python?中使用類裝飾器
    2023-06-06
  • 關(guān)于Python可視化Dash工具之plotly基本圖形示例詳解

    關(guān)于Python可視化Dash工具之plotly基本圖形示例詳解

    這篇文章主要介紹了關(guān)于Python可視化Dash工具之plotly基本圖形示例詳解,需要的朋友可以參考下
    2021-03-03
  • python3+PyQt5使用數(shù)據(jù)庫(kù)表視圖

    python3+PyQt5使用數(shù)據(jù)庫(kù)表視圖

    這篇文章主要為大家詳細(xì)介紹了python3+PyQt5使用數(shù)據(jù)庫(kù)表視圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Matplotlib實(shí)戰(zhàn)之平行坐標(biāo)系繪制詳解

    Matplotlib實(shí)戰(zhàn)之平行坐標(biāo)系繪制詳解

    平行坐標(biāo)系是一種統(tǒng)計(jì)圖表,它包含多個(gè)垂直平行的坐標(biāo)軸,每個(gè)軸表示一個(gè)字段,并用刻度標(biāo)明范圍,下面我們就來(lái)看看如何繪制平行坐標(biāo)系吧
    2023-08-08
  • 詳解Python import方法引入模塊的實(shí)例

    詳解Python import方法引入模塊的實(shí)例

    這篇文章主要介紹了詳解Python import方法引入模塊的實(shí)例的相關(guān)資料,在Python用import或者from…import或者from…import…as…來(lái)導(dǎo)入相應(yīng)的模塊,需要的朋友可以參考下
    2017-08-08
  • python使用magic模塊進(jìn)行文件類型識(shí)別方法

    python使用magic模塊進(jìn)行文件類型識(shí)別方法

    今天小編就為大家分享一篇python使用magic模塊進(jìn)行文件類型識(shí)別方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • python如何處理matlab的mat數(shù)據(jù)

    python如何處理matlab的mat數(shù)據(jù)

    這篇文章主要介紹了python如何處理matlab的mat數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • python中的reduce內(nèi)建函數(shù)使用方法指南

    python中的reduce內(nèi)建函數(shù)使用方法指南

    python中的reduce內(nèi)建函數(shù)是一個(gè)二元操作函數(shù),他用來(lái)將一個(gè)數(shù)據(jù)集合(鏈表,元組等)中的所有數(shù)據(jù)進(jìn)行下列操作:用傳給reduce中的函數(shù) func()(必須是一個(gè)二元操作函數(shù))先對(duì)集合中的第1,2個(gè)數(shù)據(jù)進(jìn)行操作,得到的結(jié)果再與第三個(gè)數(shù)據(jù)用func()函數(shù)運(yùn)算,最后得到一個(gè)結(jié)果
    2014-08-08
  • PyQt5每天必學(xué)之事件與信號(hào)

    PyQt5每天必學(xué)之事件與信號(hào)

    這篇文章主要為大家詳細(xì)介紹了PyQt5每天必學(xué)之事件與信號(hào)的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • 詳解如何使用SQLAlchemy連接數(shù)據(jù)庫(kù)

    詳解如何使用SQLAlchemy連接數(shù)據(jù)庫(kù)

    這篇文章主要為大家詳細(xì)介紹了如何使用 SQLAlchemy 連接數(shù)據(jù)庫(kù)、建立模型、操作表、以及查詢操作表數(shù)據(jù)等內(nèi)容,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11

最新評(píng)論

扎囊县| 井冈山市| 司法| 中山市| 台中市| 南雄市| 百色市| 惠东县| 开封县| 濉溪县| 武乡县| 伊宁市| 万山特区| 高州市| 长葛市| 霍州市| 宕昌县| 湟源县| 东兴市| 林甸县| 黔东| 淮滨县| 静宁县| 衡东县| 东乌| 绥德县| 宝山区| 庐江县| 宁晋县| 获嘉县| 兴仁县| 大庆市| 临沂市| 永寿县| 信阳市| 上栗县| 玛多县| 喀喇| 二连浩特市| 大埔县| 开江县|