"zoneinfo" --- IANA 時區支援
****************************

在 3.9 版被加入.

**原始碼：**Lib/zoneinfo

======================================================================

"zoneinfo" 模組提供了一個具體的時區實作，以支援 **PEP 615** 中最初指定
的 IANA 時區資料庫。預設情況下，如果可用，"zoneinfo" 會使用系統的時區
資料；如果沒有可用的系統時區資料，該函式庫將轉而使用 PyPI 上可用的第一
方 tzdata 套件。

也參考:

  "datetime" 模組
     提供了 "time" 與 "datetime" 型別，這些型別是設計來與 "ZoneInfo"
     類別一起使用的。

  tzdata 套件
     由 CPython 核心開發者維護的第一方套件，用於透過 PyPI 提供時區資料
     。

可用性: not WASI.

此模組在 WebAssembly 平台上不起作用或無法使用。更多資訊請參閱
WebAssembly 平台。


使用 "ZoneInfo"
===============

"ZoneInfo" 是 "datetime.tzinfo" 抽象基底類別的一個具體實作，旨在透過建
構函式、"datetime.replace" 方法或 "datetime.astimezone" 方法附加到
"tzinfo"：

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

以此方式建構的 Datetime 物件與 datetime 運算相容，並能處理日光節約時間
轉換而無需進一步干預：

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

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

   >>> dt_add.tzname()
   'PST'

這些時區也支援 **PEP 495** 中引入的 "fold" 屬性。在會引發時間模糊的時
差轉換期間（例如日光節約時間到標準時間的轉換），當 "fold=0" 時，會使用
轉換*前*的時差，而當 "fold=1" 時，會使用轉換*後*的時差，例如：

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

當從另一個時區轉換時，fold 將被設定為正確的值：

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

   >>> # 在 PDT 轉換為 PST 之前
   >>> print(dt_utc.astimezone(LOS_ANGELES))
   2020-11-01 01:00:00-07:00

   >>> # 在 PDT 轉換為 PST 之後
   >>> print((dt_utc + timedelta(hours=1)).astimezone(LOS_ANGELES))
   2020-11-01 01:00:00-08:00


資料來源
========

"zoneinfo" 模組不直接提供時區資料，而是從系統時區資料庫或可用的第一方
PyPI 套件 tzdata 中提取時區資訊。某些系統，特別是 Windows 系統，沒有可
用的 IANA 資料庫，因此對於需要時區資料且目標為跨平台相容性的專案，建議
宣告對 tzdata 的依賴。如果系統資料和 tzdata 都不可用，所有對
"ZoneInfo" 的呼叫都將引發 "ZoneInfoNotFoundError" 例外。


設定資料來源
------------

當呼叫 "ZoneInfo(key)" 時，建構函式會先在 "TZPATH" 中指定的目錄中搜尋
符合 "key" 的檔案，如果失敗，則在 tzdata 套件中尋找符合的項目。此行為
可以透過三種方式設定：

1. 未另外指定時的預設 "TZPATH" 可以在編譯時期設定。

2. "TZPATH" 可以使用 環境變數 來設定。

3. 在 runtime，可以使用 "reset_tzpath()" 函式來操作搜尋路徑。


編譯時期設定
~~~~~~~~~~~~

預設的 "TZPATH" 包含時區資料庫的幾個常見部署位置（Windows 除外，因為
Windows 上沒有時區資料的「眾所周知」位置）。在 POSIX 系統上，下游發行
者和從原始碼建置 Python 且知道其系統時區資料部署位置的人，可以透過指定
編譯時期選項 "TZPATH"（或更有可能的是使用 "configure 旗標 --with-
tzpath"）來變更預設時區路徑，它應該是一個由 "os.pathsep" 分隔的字串。

在所有平台上，設定的值可以透過 "sysconfig.get_config_var()" 中的
"TZPATH" 鍵取得。


環境設定
~~~~~~~~

在初始化 "TZPATH" 時（無論是在引入時，或是在呼叫不帶引數的
"reset_tzpath()" 時），如果環境變數 "PYTHONTZPATH" 存在，"zoneinfo" 模
組將使用它來設定搜尋路徑。

PYTHONTZPATH

   這是一個由 "os.pathsep" 分隔的字串，包含要使用的時區搜尋路徑。它必
   須只包含絕對路徑，而非相對路徑。在 "PYTHONTZPATH" 中指定的相對路徑
   元件將不會被使用，但除此之外，指定相對路徑時的行為是實作定義的；
   CPython 將引發 "InvalidTZPathWarning"，但其他實作可以自由地靜默忽略
   錯誤的元件或引發例外。

要讓系統忽略系統資料並改用 tzdata 套件，請設定 "PYTHONTZPATH="""。


Runtime 設定
~~~~~~~~~~~~

TZ 搜尋路徑也可以在 Runtime 使用 "reset_tzpath()" 函式進行設定。這通常
不是一個建議的操作，儘管在需要使用特定時區路徑（或需要停用對系統時區的
存取）的測試函式中使用它是合理的。


"ZoneInfo" 類別
===============

class zoneinfo.ZoneInfo(key)

   一個具體的 "datetime.tzinfo" 子類別，代表由字串 "key" 指定的 IANA
   時區。對主要建構函式的呼叫將總是回傳比較結果相同的物件；換句話說，
   除非透過 "ZoneInfo.clear_cache()" 使快取失效，否則對於所有 "key" 的
   值，以下斷言將永遠為真：

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

   "key" 必須是相對、正規化的 POSIX 路徑形式，且不含上層參照。如果傳入
   不符合規範的鍵，建構函式將引發 "ValueError" 例外。

   如果找不到符合 "key" 的檔案，建構函式將引發 "ZoneInfoNotFoundError"
   例外。

"ZoneInfo" 類別有兩個備用建構函式：

classmethod ZoneInfo.from_file(file_obj, /, key=None)

   從回傳位元組的類檔案物件（例如以二進位模式開啟的檔案或一個
   "io.BytesIO" 物件）建構一個 "ZoneInfo" 物件。與主要建構函式不同，這
   個建構函式總是會建構一個新物件。

   "key" 參數設定了時區的名稱，用於 "__str__()" 和 "__repr__()"。

   透過此建構函式建立的物件無法被封裝（參閱 pickling）。

   "ValueError" is raised if the data read from *file_obj* is not a
   valid TZif file.

classmethod ZoneInfo.no_cache(key)

   一個繞過建構函式快取的備用建構函式。它與主要建構函式相同，但每次呼
   叫都會回傳一個新物件。這對於測試或示範目的最可能有用，但它也可以用
   來建立一個具有不同快取失效策略的系統。

   透過此建構函式建立的物件，在拆封時也會繞過去序列化過程的快取。

   警示:

     使用此建構函式可能會以意想不到的方式改變你的 datetime 物件的語意
     ，只有在你確定需要時才使用它。

以下類別方法也可用：

classmethod ZoneInfo.clear_cache(*, only_keys=None)

   一個用於使 "ZoneInfo" 類別上的快取失效的方法。如果沒有傳入引數，所
   有快取都會失效，且下一次對每個鍵的主要建構函式呼叫都將回傳一個新的
   實例。

   如果將一個包含鍵名的可疊代物件傳遞給 "only_keys" 參數，則只有指定的
   鍵會從快取中移除。傳遞給 "only_keys" 但在快取中找不到的鍵會被忽略。

   警告:

     呼叫此函式可能會以意想不到的方式改變使用 "ZoneInfo" 的 datetime
     物件的語意；這會修改模組狀態，因此可能產生廣泛的影響。只有在你確
     定需要時才使用它。

該類別有一個屬性：

ZoneInfo.key

   這是一個唯讀 *屬性*，它回傳傳遞給建構函式的 "key" 值，該值應為 IANA
   時區資料庫中的查詢鍵（例如 "America/New_York"、"Europe/Paris" 或
   "Asia/Tokyo"）。

   對於從檔案建構且未指定 "key" 參數的時區，此屬性將被設定為 "None"。

   備註:

     雖然將這些值暴露給終端使用者是一種相當普遍的做法，但這些值被設計
     為表示相關時區的主鍵，而不一定是面向使用者的元素。像 CLDR（
     Unicode 通用地區資料儲存庫）這樣的專案可以用來從這些鍵中取得更友
     善的使用者字串。


字串表示
--------

在 "ZoneInfo" 物件上呼叫 "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" 參數的物件，"str" 會轉而呼叫 "repr()"。
"ZoneInfo" 的 "repr" 是實作定義的，且不保證在不同版本之間保持穩定，但
保證它不會是一個有效的 "ZoneInfo" 鍵。


封裝（Pickle）序列化
--------------------

"ZoneInfo" 物件不是序列化所有轉換資料，而是按鍵進行序列化，而從檔案建
構的 "ZoneInfo" 物件（即使是那些指定了 "key" 值的物件）也無法被封裝。

"ZoneInfo" 檔案的行為取決於它的建構方式：

1. "ZoneInfo(key)"：當使用主要建構函式建構時，"ZoneInfo" 物件會按鍵序
   列化，而在去序列化時，去序列化過程會使用主要建構函式，因此這些物件
   會與指向相同時區的其他參照是同一個物件。例如，如果
   "europe_berlin_pkl" 是一個包含從 "ZoneInfo("Europe/Berlin")" 建構並
   封裝的字串，會預期以下行為：

      >>> a = ZoneInfo("Europe/Berlin")
      >>> b = pickle.loads(europe_berlin_pkl)
      >>> a is b
      True

2. "ZoneInfo.no_cache(key)"：當從繞過快取的建構函式建構時，"ZoneInfo"
   物件也會按鍵序列化，但在去序列化時，去序列化過程會使用繞過快取的建
   構函式。如果 "europe_berlin_pkl_nc" 是一個包含從
   "ZoneInfo.no_cache("Europe/Berlin")" 建構並封裝的字串，會預期以下行
   為：

      >>> a = ZoneInfo("Europe/Berlin")
      >>> b = pickle.loads(europe_berlin_pkl_nc)
      >>> a is b
      False

3. "ZoneInfo.from_file(file_obj, /, key=None)"：當從檔案建構時，
   "ZoneInfo" 物件在封裝時會引發例外。如果終端使用者想要封裝一個從檔案
   建構的 "ZoneInfo"，建議他們使用包裝器型別或自訂序列化函式：可以按鍵
   序列化，或儲存檔案物件的內容並將其序列化。

這種序列化方法要求所需鍵的時區資料在序列化和去序列化兩端都可用，這與對
類別和函式的參照預期存在於序列化和去序列化環境中的方式相似。這也意味著
，當在具有不同版本時區資料的環境中拆封一個封裝的 "ZoneInfo" 時，不保證
結果的一致性。


函式
====

zoneinfo.available_timezones()

   取得一個集合，其中包含時區路徑上任何地方可用的所有 IANA 時區的有效
   鍵。這會在每次呼叫函式時重新計算。

   此函式僅包含規範的時區名稱，不包含「特殊」時區，例如 "posix/" 和
   "right/" 目錄下的時區，或 "posixrules" 時區。

   警示:

     此函式可能會開啟大量檔案，因為確定時區路徑上的檔案是否為有效時區
     的最佳方法是讀取開頭的「魔術字串」。

   備註:

     這些值並非設計來暴露給終端使用者；對於面向使用者的元素，應用程式
     應使用像 CLDR（Unicode 通用地區資料儲存庫）這樣的工具來取得更友善
     的使用者字串。另請參閱關於 "ZoneInfo.key" 的警示說明。

zoneinfo.reset_tzpath(to=None)

   為模組設定或重設時區搜尋路徑（"TZPATH"）。當不帶引數呼叫時，
   "TZPATH" 會被設定為預設值。

   呼叫 "reset_tzpath" 不會使 "ZoneInfo" 快取失效，因此對主要
   "ZoneInfo" 建構函式的呼叫只會在快取未命中時才使用新的 "TZPATH"。

   "to" 參數必須是一個由字串或 "os.PathLike" 組成的 *序列*，而不是一個
   字串，其中所有元素都必須是絕對路徑。如果傳入的不是絕對路徑，將會引
   發 "ValueError" 例外。


全域變數
========

zoneinfo.TZPATH

   一個表示時區搜尋路徑的唯讀序列 —— 當從一個鍵建構 "ZoneInfo" 時，該
   鍵會與 "TZPATH" 中的每個條目結合，並使用找到的第一個檔案。

   "TZPATH" 只能包含絕對路徑，絕不能包含相對路徑，無論如何設定。

   "zoneinfo.TZPATH" 指向的物件可能會因應對 "reset_tzpath()" 的呼叫而
   改變，因此建議使用 "zoneinfo.TZPATH"，而不是從 "zoneinfo" 引入
   "TZPATH" 或將一個長生命週期的變數賦值給 "zoneinfo.TZPATH"。

   有關設定時區搜尋路徑的更多資訊，請參閱 設定資料來源。


例外與警告
==========

exception zoneinfo.ZoneInfoNotFoundError

   當建構 "ZoneInfo" 物件因在系統上找不到指定的鍵而失敗時引發。這是
   "KeyError" 的一個子類別。

exception zoneinfo.InvalidTZPathWarning

   當 "PYTHONTZPATH" 包含將被過濾掉的無效元件（例如相對路徑）時引發。
