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

3.9 新版功能.


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

參見(jiàn)

模塊: datetime

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

tzdata

由 CPython 核心開(kāi)發(fā)者維護以通過(guò) PyPI 提供時(shí)區數據的第一方包。

使用 ZoneInfo?

ZoneInfodatetime.tzinfo 抽象基類(lèi)的具體實(shí)現,其目標是通過(guò)構造器、 datetime.replace 方法或 datetime.astimezone 來(lái)與 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'

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

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

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

>>> dt_add.tzname()
'PST'

這些時(shí)區還支持在 PEP 495 中引入的 fold。 在可能導致時(shí)間歧義的時(shí)差轉換中(例如夏令時(shí)到標準時(shí)的轉換),當 fold=0 時(shí)會(huì )使用轉換 之前 的時(shí)差,而當 fold=1 時(shí)則使用轉換 之后 的時(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

當執行來(lái)自另一時(shí)區的轉換時(shí),fold 將被設置為正確的值:

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

數據源?

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

配置數據源?

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

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

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

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

編譯時(shí)配置?

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

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

環(huán)境配置?

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

PYTHONTZPATH?

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

要設置讓系統忽略系統數據并改用 tzdata 包,請設置 PYTHONTZPATH=""。

運行時(shí)配置?

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

ZoneInfo 類(lèi)?

class zoneinfo.ZoneInfo(key)?

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

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

key 必須采用相對的標準化 POSIX 路徑的形式,其中沒(méi)有對上一層級的引用。 如果傳入了不合要求的鍵則構造器將引發(fā) ValueError。

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

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

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

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

key 形參設置時(shí)區名稱(chēng)以供 __str__()__repr__() 使用。

由此構造器創(chuàng )建的對象不可被封存 (參見(jiàn) pickling)。

classmethod ZoneInfo.no_cache(key)?

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

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

小心

使用此構造器可以會(huì )以令人驚訝的方式改變日期時(shí)間對象的語(yǔ)義,只有在你確定你的需求時(shí)才使用它。

也可以使用以下的類(lèi)方法:

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

一個(gè)可在 ZoneInfo 類(lèi)上禁用緩存的方法。 如果不傳入參數,則會(huì )禁用所有緩存并且下次對每個(gè)鍵調用主構造器將返回一個(gè)新實(shí)例。

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

警告

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

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

ZoneInfo.key?

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

對于不指定 key 形參而是基于文件構造時(shí)區,該屬性將設為 None。

備注

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

字符串表示?

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

>>>
>>> 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]'

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

封存序列化?

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

ZoneInfo 文件的行為取決于它的構造方式:

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

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

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

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

函數?

zoneinfo.available_timezones()?

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

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

小心

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

備注

這些值并不被設計用來(lái)對外公開(kāi)給最終用戶(hù);對于面向用戶(hù)的元素,應用程序應當使用 CLDR (Unicode 通用區域數據存儲庫) 之類(lèi)來(lái)獲取更為用戶(hù)友好的字符串。 另請參閱 ZoneInfo.key 中的提示性說(shuō)明。

zoneinfo.reset_tzpath(to=None)?

設置或重置模塊的時(shí)區搜索路徑 (TZPATH)。 當不帶參數調用時(shí),TZPATH 會(huì )被設為默認值。

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

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

全局變量?

zoneinfo.TZPATH?

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

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

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

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

異常與警告?

exception zoneinfo.ZoneInfoNotFoundError?

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

exception zoneinfo.InvalidTZPathWarning?

PYTHONTZPATH 包含將被過(guò)濾掉的無(wú)效組件,例如一個(gè)相對路徑時(shí)引發(fā)。