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

3.9 新版功能.


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

參見

模塊: datetime

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

tzdata

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

使用 ZoneInfo?

ZoneInfodatetime.tzinfo 抽象基類的具體實現(xiàn),其目標是通過構(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)造的日期時間對象可兼容日期時間運算并可在無需進一步干預(yù)的情況下處理夏令時轉(zhuǎn)換:

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

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

>>> dt_add.tzname()
'PST'

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

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

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

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

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

  1. 默認的 TZPATH 未通過其他方式指定時可在 編譯時 進行配置。

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

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

編譯時配置?

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

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

環(huán)境配置?

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

PYTHONTZPATH?

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

要設(shè)置讓系統(tǒng)忽略系統(tǒng)數(shù)據(jù)并改用 tzdata 包,請設(shè)置 PYTHONTZPATH=""。

運行時配置?

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

ZoneInfo?

class zoneinfo.ZoneInfo(key)?

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

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

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

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

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

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

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

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

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

classmethod ZoneInfo.no_cache(key)?

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

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

小心

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

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

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

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

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

警告

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

該類具有一個屬性:

ZoneInfo.key?

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

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

備注

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

字符串表示?

當在 ZoneInfo 對象上調(diào)用 str 時返回的字符串表示默認會使用 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]'

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

封存序列化?

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

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

  1. ZoneInfo(key): 當使用主構(gòu)造器構(gòu)造時,會基于鍵序列化一個 ZoneInfo 對象,而當反序列化時,反序列化過程會使用主構(gòu)造器,因此預(yù)期它們與其他對同一時區(qū)的引用會是同一對象。 例如,如果 europe_berlin_pkl 是一個包含基于 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): 當通過繞過緩存的構(gòu)造器構(gòu)造時,ZoneInfo 對象也會基于鍵序列化,但當反序列化時,反序列化過程會使用繞過緩存的構(gòu)造器。 如果 europe_berlin_pkl_nc 是一個包含基于 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): 當通過文件構(gòu)造時,ZoneInfo 對象會在封存時引發(fā)異常。 如果最終用戶想要封存通過文件構(gòu)造的 ZoneInfo,則推薦他們使用包裝類型或自定義序列化函數(shù):或者基于鍵序列化,或者存儲文件對象的內(nèi)容并將其序列化。

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

函數(shù)?

zoneinfo.available_timezones()?

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

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

小心

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

備注

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

zoneinfo.reset_tzpath(to=None)?

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

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

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

全局變量?

zoneinfo.TZPATH?

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

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

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

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

異常與警告?

exception zoneinfo.ZoneInfoNotFoundError?

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

exception zoneinfo.InvalidTZPathWarning?

PYTHONTZPATH 包含將被過濾掉的無效組件,例如一個相對路徑時引發(fā)。