zoneinfo --- IANA 時(shí)區(qū)支持?

3.9 新版功能.


zoneinfo 模塊根據(jù) PEP 615 的最初說明提供了具體的時(shí)區(qū)實(shí)現(xiàn)來支持 IANA 時(shí)區(qū)數(shù)據(jù)庫。 按照默認(rèn)設(shè)置,zoneinfo 會(huì)在可能的情況下使用系統(tǒng)的時(shí)區(qū)數(shù)據(jù);如果系統(tǒng)時(shí)區(qū)數(shù)據(jù)不可用,該庫將回退為使用 PyPI 上提供的 tzdata 第一方包。

參見

模塊: datetime

提供 timedatetime 類型,ZoneInfo 類被設(shè)計(jì)為可配合這兩個(gè)類型使用。

tzdata

由 CPython 核心開發(fā)者維護(hù)以通過 PyPI 提供時(shí)區(qū)數(shù)據(jù)的第一方包。

使用 ZoneInfo?

ZoneInfodatetime.tzinfo 抽象基類的具體實(shí)現(xiàn),其目標(biāo)是通過構(gòu)造器、 datetime.replace 方法或 datetime.astimezone 來與 tzinfo 建立關(guān)聯(lián):

>>>
>>> from zoneinfo import ZoneInfo
>>> from datetime import datetime, timedelta

>>> dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
>>> print(dt)
2020-10-31 12:00:00-07:00

>>> dt.tzname()
'PDT'

以此方式構(gòu)造的日期時(shí)間對(duì)象可兼容日期時(shí)間運(yùn)算并可在無需進(jìn)一步干預(yù)的情況下處理夏令時(shí)轉(zhuǎn)換:

>>>
>>> dt_add = dt + timedelta(days=1)

>>> print(dt_add)
2020-11-01 12:00:00-08:00

>>> dt_add.tzname()
'PST'

這些時(shí)區(qū)還支持在 PEP 495 中引入的 fold。 在可能導(dǎo)致時(shí)間歧義的時(shí)差轉(zhuǎn)換中(例如夏令時(shí)到標(biāo)準(zhǔn)時(shí)的轉(zhuǎn)換),當(dāng) fold=0 時(shí)會(huì)使用轉(zhuǎn)換 之前 的時(shí)差,而當(dāng) fold=1 時(shí)則使用轉(zhuǎn)換 之后 的時(shí)差,例如:

>>>
>>> dt = datetime(2020, 11, 1, 1, tzinfo=ZoneInfo("America/Los_Angeles"))
>>> print(dt)
2020-11-01 01:00:00-07:00

>>> print(dt.replace(fold=1))
2020-11-01 01:00:00-08:00

當(dāng)執(zhí)行來自另一時(shí)區(qū)的轉(zhuǎn)換時(shí),fold 將被設(shè)置為正確的值:

>>>
>>> from datetime import timezone
>>> LOS_ANGELES = ZoneInfo("America/Los_Angeles")
>>> dt_utc = datetime(2020, 11, 1, 8, tzinfo=timezone.utc)

>>> # Before the PDT -> PST transition
>>> print(dt_utc.astimezone(LOS_ANGELES))
2020-11-01 01:00:00-07:00

>>> # After the PDT -> PST transition
>>> print((dt_utc + timedelta(hours=1)).astimezone(LOS_ANGELES))
2020-11-01 01:00:00-08:00

數(shù)據(jù)源?

zoneinfo 模塊不直接提供時(shí)區(qū)數(shù)據(jù),而是在可能的情況下從系統(tǒng)時(shí)區(qū)數(shù)據(jù)庫或 PyPI 上的第一方包 tzdata 獲取時(shí)區(qū)信息。 某些系統(tǒng),重要的一點(diǎn)是 Windows 系統(tǒng)也包括在內(nèi),并沒有可用的 IANA 數(shù)據(jù)庫,因此對(duì)于要保證獲取時(shí)區(qū)信息的跨平臺(tái)兼容性的項(xiàng)目,推薦對(duì) tzdata 聲明依賴。 如果系統(tǒng)數(shù)據(jù)和 tzdata 均不可用,則所有對(duì) ZoneInfo 的調(diào)用都將引發(fā) ZoneInfoNotFoundError。

配置數(shù)據(jù)源?

當(dāng) ZoneInfo(key) 被調(diào)用時(shí),此構(gòu)造器首先會(huì)在 TZPATH 所指定的目錄下搜索匹配 key 的文件,失敗時(shí)則會(huì)在 tzdata 包中查找匹配。 此行為可通過三種方式來配置:

  1. 默認(rèn)的 TZPATH 未通過其他方式指定時(shí)可在 編譯時(shí) 進(jìn)行配置。

  2. TZPATH 可使用 環(huán)境變量 進(jìn)行配置。

  3. 運(yùn)行時(shí),搜索路徑可使用 reset_tzpath() 函數(shù)來修改。

編譯時(shí)配置?

默認(rèn)的 TZPATH 包括一些時(shí)區(qū)數(shù)據(jù)庫的通用部署位置(Windows 除外,該系統(tǒng)沒有時(shí)區(qū)數(shù)據(jù)的“通用”位置)。 在 POSIX 系統(tǒng)中,下游分發(fā)者和從源碼編譯 Python 的開發(fā)者知道系統(tǒng)時(shí)區(qū)數(shù)據(jù)部署位置,它們可以通過指定編譯時(shí)選項(xiàng) TZPATH (或者更常見的是通過 配置旗標(biāo) --with-tzpath) 來改變默認(rèn)的時(shí)區(qū)路徑,該選項(xiàng)應(yīng)當(dāng)是一個(gè)由 os.pathsep 分隔的字符串。

在所有平臺(tái)上,配置值會(huì)在 sysconfig.get_config_var() 中以 TZPATH 鍵的形式提供。

環(huán)境配置?

當(dāng)初始化 TZPATH 時(shí)(在導(dǎo)入時(shí)或不帶參數(shù)調(diào)用 reset_tzpath() 時(shí)),zoneinfo 模塊將使用環(huán)境變量 PYTHONTZPATH,如果變量存在則會(huì)設(shè)置搜索路徑。

PYTHONTZPATH?

這是一個(gè)以 os.pathsep 分隔的字符串,其中包含要使用的時(shí)區(qū)搜索路徑。 它必須僅由絕對(duì)路徑而非相對(duì)路徑組成。 在 PYTHONTZPATH 中指定的相對(duì)路徑部分將不會(huì)被使用,但在其他情況下當(dāng)指定相對(duì)路徑時(shí)的行為該實(shí)現(xiàn)是有定義的;CPython 將引發(fā) InvalidTZPathWarning,而其他實(shí)現(xiàn)可自由地忽略錯(cuò)誤部分或是引發(fā)異常。

要設(shè)置讓系統(tǒng)忽略系統(tǒng)數(shù)據(jù)并改用 tzdata 包,請(qǐng)?jiān)O(shè)置 PYTHONTZPATH=""

運(yùn)行時(shí)配置?

TZ 搜索路徑也可在運(yùn)行時(shí)使用 reset_tzpath() 函數(shù)來配置。 通常并不建議如此操作,不過在需要使用指定時(shí)區(qū)路徑(或者需要禁止訪問系統(tǒng)時(shí)區(qū))的測(cè)試函數(shù)中使用它則是合理的。

ZoneInfo?

class zoneinfo.ZoneInfo(key)?

一個(gè)具體的 datetime.tzinfo 子類,它代表一個(gè)由字符串 key 所指定的 IANA 時(shí)區(qū)。 對(duì)主構(gòu)造器的調(diào)用將總是返回可進(jìn)行標(biāo)識(shí)比較的對(duì)象;但是另一種方式,對(duì)所有的 key 值通過 ZoneInfo.clear_cache() 禁止緩存失效,對(duì)以下斷言將總是為真值:

a = ZoneInfo(key)
b = ZoneInfo(key)
assert a is b

key 必須采用相對(duì)的標(biāo)準(zhǔn)化 POSIX 路徑的形式,其中沒有對(duì)上一層級(jí)的引用。 如果傳入了不合要求的鍵則構(gòu)造器將引發(fā) ValueError。

如果沒有找到匹配 key 的文件,構(gòu)造器將引發(fā) ZoneInfoNotFoundError。

ZoneInfo 類具有兩個(gè)替代構(gòu)造器:

classmethod ZoneInfo.from_file(fobj, /, key=None)?

基于一個(gè)返回字節(jié)串的文件類對(duì)象(例如一個(gè)以二進(jìn)制模式打開的文件或是一個(gè) io.BytesIO 對(duì)象)構(gòu)造 ZoneInfo 對(duì)象。 不同于主構(gòu)造器,此構(gòu)造器總是會(huì)構(gòu)造一個(gè)新對(duì)象。

key 形參設(shè)置時(shí)區(qū)名稱以供 __str__()__repr__() 使用。

由此構(gòu)造器創(chuàng)建的對(duì)象不可被封存 (參見 pickling)。

classmethod ZoneInfo.no_cache(key)?

一個(gè)繞過構(gòu)造器緩存的替代構(gòu)造器。 它與主構(gòu)造器很相似,但每次調(diào)用都會(huì)返回一個(gè)新對(duì)象。 此構(gòu)造器在進(jìn)行測(cè)試或演示時(shí)最為適用,但它也可以被用來創(chuàng)建具有不同緩存失效策略的系統(tǒng)。

由此構(gòu)造器創(chuàng)建的對(duì)象在被解封時(shí)也會(huì)繞過反序列化進(jìn)程的緩存。

小心

使用此構(gòu)造器可以會(huì)以令人驚訝的方式改變?nèi)掌跁r(shí)間對(duì)象的語義,只有在你確定你的需求時(shí)才使用它。

也可以使用以下的類方法:

classmethod ZoneInfo.clear_cache(*, only_keys=None)?

一個(gè)可在 ZoneInfo 類上禁用緩存的方法。 如果不傳入?yún)?shù),則會(huì)禁用所有緩存并且下次對(duì)每個(gè)鍵調(diào)用主構(gòu)造器將返回一個(gè)新實(shí)例。

如果將一個(gè)鍵名稱的可迭代對(duì)象傳給 only_keys 形參,則將只有指定的鍵會(huì)被從緩存中移除。 傳給 only_keys 但在緩存中找不到的鍵會(huì)被忽略。

警告

發(fā)起調(diào)用此函數(shù)可能會(huì)以令人驚訝的方式改變使用 ZoneInfo 的日期時(shí)間對(duì)象的語義;這會(huì)修改進(jìn)程范圍內(nèi)的全局狀態(tài)并因此可能產(chǎn)生大范圍的影響。 只有在你確定你的需求時(shí)才使用它。

該類具有一個(gè)屬性:

ZoneInfo.key?

這是一個(gè)只讀的 attribute,它返回傳給構(gòu)造器的 key 的值,該值應(yīng)為一個(gè) IANA 時(shí)區(qū)數(shù)據(jù)庫的查找鍵 (例如 America/New_York, Europe/ParisAsia/Tokyo)。

對(duì)于不指定 key 形參而是基于文件構(gòu)造時(shí)區(qū),該屬性將設(shè)為 None

備注

盡管將這些信息暴露給最終用戶是一種比較普通的做法,但是這些值被設(shè)計(jì)作為代表相關(guān)時(shí)區(qū)的主鍵而不一定是面向用戶的元素。 CLDR (Unicode 通用區(qū)域數(shù)據(jù)存儲(chǔ)庫) 之類的項(xiàng)目可被用來根據(jù)這些鍵獲取更為用戶友好的字符串。

字符串表示?

當(dāng)在 ZoneInfo 對(duì)象上調(diào)用 str 時(shí)返回的字符串表示默認(rèn)會(huì)使用 ZoneInfo.key 屬性(參見該屬性文檔中的用法注釋):

>>>
>>> zone = ZoneInfo("Pacific/Kwajalein")
>>> str(zone)
'Pacific/Kwajalein'

>>> dt = datetime(2020, 4, 1, 3, 15, tzinfo=zone)
>>> f"{dt.isoformat()} [{dt.tzinfo}]"
'2020-04-01T03:15:00+12:00 [Pacific/Kwajalein]'

對(duì)于基于文件而非指定 key 形參所構(gòu)建的對(duì)象,str 會(huì)回退為調(diào)用 repr()。 ZoneInforepr 是由具體實(shí)現(xiàn)定義的并且不一定會(huì)在不同版本間保持穩(wěn)定,但它保證不會(huì)是一個(gè)有效的 ZoneInfo 鍵。

封存序列化?

ZoneInfo 對(duì)象的序列化是基于鍵的,而不是序列化所有過渡數(shù)據(jù),并且基于文件構(gòu)造的 ZoneInfo 對(duì)象(即使是指定了 key 值的對(duì)象)不能被封存。

ZoneInfo 文件的行為取決于它的構(gòu)造方式:

  1. ZoneInfo(key): 當(dāng)使用主構(gòu)造器構(gòu)造時(shí),會(huì)基于鍵序列化一個(gè) ZoneInfo 對(duì)象,而當(dāng)反序列化時(shí),反序列化過程會(huì)使用主構(gòu)造器,因此預(yù)期它們與其他對(duì)同一時(shí)區(qū)的引用會(huì)是同一對(duì)象。 例如,如果 europe_berlin_pkl 是一個(gè)包含基于 ZoneInfo("Europe/Berlin") 構(gòu)建的封存數(shù)據(jù)的字符串,你可以預(yù)期出現(xiàn)以下的行為:

    >>>
    >>> a = ZoneInfo("Europe/Berlin")
    >>> b = pickle.loads(europe_berlin_pkl)
    >>> a is b
    True
    
  2. ZoneInfo.no_cache(key): 當(dāng)通過繞過緩存的構(gòu)造器構(gòu)造時(shí),ZoneInfo 對(duì)象也會(huì)基于鍵序列化,但當(dāng)反序列化時(shí),反序列化過程會(huì)使用繞過緩存的構(gòu)造器。 如果 europe_berlin_pkl_nc 是一個(gè)包含基于 ZoneInfo.no_cache("Europe/Berlin") 構(gòu)造的封存數(shù)據(jù)的字符串,你可以預(yù)期出現(xiàn)以下的行為:

    >>>
    >>> a = ZoneInfo("Europe/Berlin")
    >>> b = pickle.loads(europe_berlin_pkl_nc)
    >>> a is b
    False
    
  3. ZoneInfo.from_file(fobj, /, key=None): 當(dāng)通過文件構(gòu)造時(shí),ZoneInfo 對(duì)象會(huì)在封存時(shí)引發(fā)異常。 如果最終用戶想要封存通過文件構(gòu)造的 ZoneInfo,則推薦他們使用包裝類型或自定義序列化函數(shù):或者基于鍵序列化,或者存儲(chǔ)文件對(duì)象的內(nèi)容并將其序列化。

該序列化方法要求所需鍵的時(shí)區(qū)數(shù)據(jù)在序列化和反序列化中均可用,類似于在序列化和反序列化環(huán)境中都預(yù)期存在對(duì)類和函數(shù)的引用的方式。 這還意味著在具有不同時(shí)區(qū)數(shù)據(jù)版本的環(huán)境中當(dāng)解封被封存的 ZoneInfo 時(shí)并不會(huì)保證結(jié)果的一致性。

函數(shù)?

zoneinfo.available_timezones()?

獲取一個(gè)包含可用 IANA 時(shí)區(qū)的在時(shí)區(qū)路徑的任何位置均可用的全部有效鍵的集合。 每次調(diào)用該函數(shù)時(shí)都會(huì)重新計(jì)算。

此函數(shù)僅包括規(guī)范時(shí)區(qū)名稱而不包括“特殊”時(shí)區(qū)如位于 posix/right/ 目錄下的時(shí)區(qū)或 posixrules 時(shí)區(qū)。

小心

此函數(shù)可能會(huì)打開大量的文件,因?yàn)榇_定時(shí)區(qū)路徑上某個(gè)文件是否為有效時(shí)區(qū)的最佳方式是讀取開頭位置的“魔術(shù)字符串”。

備注

這些值并不被設(shè)計(jì)用來對(duì)外公開給最終用戶;對(duì)于面向用戶的元素,應(yīng)用程序應(yīng)當(dāng)使用 CLDR (Unicode 通用區(qū)域數(shù)據(jù)存儲(chǔ)庫) 之類來獲取更為用戶友好的字符串。 另請(qǐng)參閱 ZoneInfo.key 中的提示性說明。

zoneinfo.reset_tzpath(to=None)?

設(shè)置或重置模塊的時(shí)區(qū)搜索路徑 (TZPATH)。 當(dāng)不帶參數(shù)調(diào)用時(shí),TZPATH 會(huì)被設(shè)為默認(rèn)值。

調(diào)用 reset_tzpath 將不會(huì)使 ZoneInfo 緩存失效,因而在緩存未命中的情況下對(duì)主 ZoneInfo 構(gòu)造器的調(diào)用將只使用新的 TZPATH

to 形參必須是由字符串或 os.PathLike 組成的 sequence 或而不是字符串,它們必須都是絕對(duì)路徑。 如果所傳入的不是絕對(duì)路徑則將引發(fā) ValueError。

全局變量?

zoneinfo.TZPATH?

一個(gè)表示時(shí)區(qū)搜索路徑的只讀序列 -- 當(dāng)通過鍵構(gòu)造 ZoneInfo 時(shí),鍵會(huì)與 TZPATH 中的每個(gè)條目進(jìn)行合并,并使用所找到的第一個(gè)文件。

TZPATH 可以只包含絕對(duì)路徑,絕不包含相對(duì)路徑,無論它是如何配置的。

zoneinfo.TZPATH 所指向的對(duì)象可能隨著對(duì) reset_tzpath() 的調(diào)用而改變,因此推薦使用 zoneinfo.TZPATH 而不是從 zoneinfo 導(dǎo)入 TZPATH 或是將 zoneinfo.TZPATH 賦值給一個(gè)長(zhǎng)期變量。

有關(guān)配置時(shí)區(qū)搜索路徑的更多信息,請(qǐng)參閱 配置數(shù)據(jù)源

異常與警告?

exception zoneinfo.ZoneInfoNotFoundError?

當(dāng)一個(gè) ZoneInfo 對(duì)象的構(gòu)造由于在系統(tǒng)中找不到指定的鍵而失敗時(shí)引發(fā)。 這是 KeyError 的一個(gè)子類。

exception zoneinfo.InvalidTZPathWarning?

當(dāng) PYTHONTZPATH 包含將被過濾掉的無效組件,例如一個(gè)相對(duì)路徑時(shí)引發(fā)。