Skip to content

Commit 4ed008a

Browse files
committed
Add minimal documentation for the ZoneInfo module
This could also use a section on the behavior during data updates and a section on how the cache behavior affects the semantics of datetimes, but those can be left to later updates.
1 parent abbc149 commit 4ed008a

2 files changed

Lines changed: 394 additions & 0 deletions

File tree

docs/index.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
``zoneinfo``: IANA Time Zones for the Standard Library
2+
======================================================
3+
4+
5+
This is the reference implementation for :pep:`615`, which adds support for the IANA time zone database to the Python standard library.
6+
7+
See :mod:`zoneinfo` for the module's full documentation.
8+
9+
Documentation
10+
=============
11+
Contents:
12+
13+
.. toctree::
14+
:maxdepth: 1
15+
16+
zoneinfo
17+
18+
19+
Indices and tables
20+
==================
21+
22+
* :ref:`genindex`
23+
* :ref:`modindex`
24+
* :ref:`search`

docs/zoneinfo.rst

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
:mod:`zoneinfo` --- IANA time zone support
2+
==========================================
3+
4+
.. module:: zoneinfo
5+
:synopsis: IANA time zone support
6+
7+
.. versionadded:: 3.9
8+
9+
.. moduleauthor:: Paul Ganssle <[email protected]>
10+
.. sectionauthor:: Paul Ganssle <[email protected]>
11+
12+
--------------
13+
14+
The :mod:`zoneinfo` module provides a concrete time zone implementation to
15+
support the IANA time zone database as originally specified in :pep:`615`. By
16+
default, :mod:`zoneinfo` uses the system's time zone data if available; if no
17+
system time zone data is available, the library will fall back to using the
18+
first-party ``tzdata`` package available on PyPI.
19+
20+
.. seealso::
21+
22+
Module: :mod:`datetime`
23+
Provides the time and datetime types with which the ZoneInfo class was
24+
designed to be used.
25+
26+
Package `tzdata <https://pypi.org/project/tzdata/>`_
27+
First-party package maintained by the CPython core developers to supply
28+
time zone data via PyPI.
29+
30+
31+
Using ``ZoneInfo``
32+
------------------
33+
34+
:class:`ZoneInfo` is a concrete implementation of the :class:`datetime.tzinfo`
35+
abstract base class, and is intended to be attached to ``tzinfo``, either via
36+
the constructor, the ``.replace`` method or ``.astimezone``::
37+
38+
>>> from zoneinfo import ZoneInfo
39+
>>> from datetime import datetime, timedelta
40+
41+
>>> dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
42+
>>> print(dt)
43+
2020-10-31 12:00:00-07:00
44+
45+
>>> dt.tzname()
46+
'PDT'
47+
48+
Datetimes constructed thusly are compatible with datetime arithmetic and handle
49+
daylight saving time transitions with no further intervention::
50+
51+
>>> dt_add = (dt + timedelta(days=1))
52+
53+
>>> print(dt_add)
54+
2020-11-01 12:00:00-08:00
55+
56+
>>> dt_add.tzname()
57+
'PST'
58+
59+
These time zones also support the ``fold`` attribute introduced in :pep:`495`.
60+
During offset transitions which induce ambiguous times (such as a daylight
61+
saving time to standard time transition), the offset from *before* the
62+
transition is used when ``fold=0``, and the offset *after* the transition is
63+
used when ``fold=1``, for example::
64+
65+
>>> dt = datetime(2020, 11, 1, 1, tzinfo=ZoneInfo("America/Los_Angeles"))
66+
>>> print(dt)
67+
2020-11-01 01:00:00-07:00
68+
69+
>>> print(dt.replace(fold=1))
70+
2020-11-01 01:00:00-08:00
71+
72+
When converting from another time zone, the fold will be set to the correct
73+
value::
74+
75+
>>> from datetime import timezone
76+
>>> LOS_ANGELES = ZoneInfo("America/Los_Angeles")
77+
>>> dt_utc = datetime(2020, 11, 1, 8, tzinfo=timezone.utc)
78+
79+
>>> print(dt_utc.astimezone(LOS_ANGELES))
80+
2020-11-01 01:00:00-07:00
81+
82+
>>> print((dt_utc + timedelta(hours=1)).astimezone(LOS_ANGELES))
83+
2020-11-01 01:00:00-07:00
84+
85+
Data sources
86+
------------
87+
88+
The ``zoneinfo`` module does not directly provide time zone data, and instead
89+
pulls time zone information from the system time zone database or the
90+
first-party PyPI package ``tzdata``, if available. Some systems, including
91+
notably Windows systems, do not have an IANA database available, and so for
92+
projects targeting cross-platform compatibility that require time zone data, it
93+
is recommended to declare a dependency on ``tzdata``.
94+
95+
.. _zoneinfo_data_configuration:
96+
97+
Configuring the data sources
98+
****************************
99+
100+
When ``ZoneInfo(key)`` is called, the constructor first searches the
101+
directories specified in :data:`TZPATH` a match to ``key``, and on failure
102+
looks for a match in the ``tzdata`` package. This behavior can be configured
103+
in three ways:
104+
105+
1. The default :data:`TZPATH` when not otherwise specified can be configured at
106+
compile time.
107+
2. :data:`TZPATH` can be configured using an environment variable.
108+
3. At runtime, the search path can be manipulated using the
109+
:func:`reset_tzpath` function.
110+
111+
Compile-time configuration
112+
^^^^^^^^^^^^^^^^^^^^^^^^^^
113+
114+
The default :data:`TZPATH` includes several common deployment locations for the
115+
time zone database (except on Windows, where there are no "well-known"
116+
locations for time zone data). Downstream distributors and those building
117+
Python from source who know where their system time zone data is deployed may
118+
change the default time zone path by specifying the compile-time option
119+
``PYTHONTZPATHDEFAULT``, which should be a string delimited by
120+
:data:`os.pathsep`.
121+
122+
Environment configuration
123+
^^^^^^^^^^^^^^^^^^^^^^^^^
124+
125+
When initializing :data:`TZPATH` (either at import time or whenever
126+
:func:`reset_tzpath` is called with no arguments), the ``zoneinfo`` module will
127+
use the environment variable ``PYTHONTZPATH``, if it exists, to set the search
128+
path.
129+
130+
.. envvar:: PYTHONTZPATH
131+
132+
This is an :data:`os.pathsep`-separated string containing the time zone
133+
search path to use. It must consist of only absolute rather than relative
134+
paths. Relative components specified in ``PYTHONTZPATH`` will not be used,
135+
but otherwise the behavior when a relative path is specified is
136+
implementation-defined; CPython will raise :exc:`InvalidTZPathWarning`, but
137+
other implementations are free to silently ignore the erroneous component
138+
or raise an exception.
139+
140+
To set the system to ignore the system data and use the ``tzdata`` package
141+
instead, set ``PYTHONTZPATH=""``.
142+
143+
Runtime configuration
144+
^^^^^^^^^^^^^^^^^^^^^
145+
146+
The TZ search path can also be configured at runtime using the
147+
:func:`reset_tzpath` function. This is generally not an advisable operation,
148+
though it is reasonable to use it in test functions that require the use of a
149+
specific time zone path (or require disabling access to the system time zones).
150+
151+
152+
The ``ZoneInfo`` class
153+
----------------------
154+
155+
.. class:: ZoneInfo(key)
156+
157+
A concrete :class:`datetime.tzinfo` subclass that represents an IANA time
158+
zone specified by the string ``key``. Calls to the primary constructor will
159+
always return objects that compare identically; put another way, barring
160+
cache invalidation via :meth:`ZoneInfo.clear_cache`, for all values of
161+
``key``, the following assertion will always be true:
162+
163+
.. code-block:: python
164+
165+
a = ZoneInfo(key)
166+
b = ZoneInfo(key)
167+
assert a is b
168+
169+
``key`` must be in the form of a relative, normalized POSIX path, with no
170+
up-level references. The constructor will raise :exc:`ValueError` if a
171+
non-conforming key is passed.
172+
173+
If no file matching ``key`` is found, the constructor will raise
174+
:exc:`ZoneInfoNotFoundError`.
175+
176+
177+
The ``ZoneInfo`` class has two alternate constructors:
178+
179+
.. classmethod:: ZoneInfo.from_file(fobj, /, key=None)
180+
181+
Constructs a ``ZoneInfo`` object from a file-like object returning bytes
182+
(e.g. a file opened in binary mode or a :class:`io.BytesIO` object). Unlike
183+
the primary constructor, this always constructs a new object.
184+
185+
The ``key`` parameter sets the name of the zone for the purposes of
186+
``__str__`` and ``__repr__``.
187+
188+
Objects created via this constructor cannot be pickled (see `pickling`_).
189+
190+
.. classmethod:: ZoneInfo.no_cache(key)
191+
192+
An alternate constructor that bypasses the constructor's cache. It is
193+
identical to the primary constructor, but returns a new object on each
194+
call. This is most likely to be useful for testing or demonstration
195+
purposes, but it can also be used to create a system with a different cache
196+
invalidation strategy.
197+
198+
Objects created via this constructor will also bypass the cache of a
199+
deserializing process when unpickled.
200+
201+
.. TODO: Replace ... with a section reference.
202+
203+
.. caution::
204+
205+
Using this constructor may change the semantics of your datetimes in
206+
surprising ways, only use it if you know that you need to.
207+
208+
The following class methods are also available:
209+
210+
.. classmethod:: ZoneInfo.clear_cache(\*, only_keys=None)
211+
212+
A method for invalidating the cache on the ``ZoneInfo`` class. If no
213+
arguments are passed, all caches are invalidated and the next call to
214+
the primary constructor for each key will return a new instance.
215+
216+
If an iterable if key names is passed to the ``only_keys`` parameter, only
217+
the specified keys will be removed from the cache. Keys passed to
218+
``only_keys`` but not found in the cache are ignored.
219+
220+
.. TODO: Replace ... with a section reference
221+
222+
.. warning::
223+
224+
Invoking this function may change the semantics of datetimes using
225+
``ZoneInfo`` in surprising ways; this modifies process-wide global state
226+
and thus may have wide-ranging effects. Only use it if you know that you
227+
need to.
228+
229+
The class has one attribute:
230+
231+
.. attribute:: ZoneInfo.key
232+
233+
This is a read-only attribute that returns the value of ``key`` passed to
234+
the constructor, which should be a lookup key in the IANA time zone
235+
database (e.g. ``America/New_York``, ``Europe/Paris`` or ``Asia/Tokyo``).
236+
237+
For zones constructed from file without specifying a ``key`` parameter,
238+
this will be set to ``None``.
239+
240+
.. note::
241+
242+
Although it is a somewhat common practice to expose these to end users,
243+
these values are designed to be primary keys for representing the
244+
relevant zones and not necessarily user-facing elements. Projects like
245+
CLDR (the Unicode Common Locale Data Repository) can be used to get
246+
more user-friendly strings from these keys.
247+
248+
String representations
249+
**********************
250+
251+
The string representation returned when calling :func:`str` on a
252+
:class:`ZoneInfo` object defaults to using the :attribute:`key` attribute (see
253+
the note on usage in the attribute documentation)::
254+
255+
>>> zone = ZoneInfo("Pacific/Kwajalein")
256+
>>> str(zone)
257+
'Pacific/Kwajalein'
258+
259+
>>> dt = datetime(2020, 4, 1, 3, 15, tzinfo=zone)
260+
>>> f"{dt.isoformat()} [{dt.tzinfo}]"
261+
'2020-04-01T03:15:00+12:00 [Pacific/Kwajalein]'
262+
263+
For objects constructed from a file without specifying a ``key`` parameter,
264+
``str`` falls back to calling :func:`repr`. ``ZoneInfo``'s ``repr`` is
265+
implementation-defined and not necessarily stable between versions, but it is
266+
guaranteed not to be a valid ``ZoneInfo`` key.
267+
268+
.. _pickling:
269+
270+
Pickle serialization
271+
********************
272+
273+
Rather than serializing all transition data, ``ZoneInfo`` objects are
274+
serialized by key, and ``ZoneInfo`` objects constructed from files (even those
275+
with a value for ``key`` specified) cannot be pickled.
276+
277+
The behavior of a ``ZoneInfo`` file depends on how it was constructed:
278+
279+
1. ``ZoneInfo(key)``: When constructed with the primary constructor, a
280+
``ZoneInfo`` object is serialized by key, and when deserialized, the
281+
deserializing process uses the primary and thus it is expected that these
282+
are expected to be the same object as other references to the same time
283+
zone. For example, if ``europe_berlin_pkl`` is a string containing a pickle
284+
constructed from ``ZoneInfo("Europe/Berlin")``, one would expect the
285+
following behavior:
286+
287+
.. code-block::
288+
289+
>>> a = ZoneInfo("Europe/Berlin")
290+
>>> b = pickle.loads(europe_berlin_pkl)
291+
>>> a is b
292+
True
293+
294+
2. ``ZoneInfo.no_cache(key)``: When constructed from the cache-bypassing
295+
constructor, the ``ZoneInfo`` object is also serialized by key, but when
296+
deserialized, the deserializing process uses the cache bypassing
297+
constructor. If ``europe_berlin_pkl_nc`` is a string containing a pickle
298+
constructed from ``ZoneInfo.no_cache("Europe/Berlin")``, one would expect
299+
the following behavior:
300+
301+
.. code-block::
302+
303+
>>> a = ZoneInfo("Europe/Berlin")
304+
>>> b = pickle.loads(europe_berlin_pkl_nc)
305+
>>> a is b
306+
False
307+
308+
3. ``ZoneInfo.from_file(fobj, /, key=None)``: When constructed from a file, the
309+
``ZoneInfo`` object raises an exception on pickling. If an end user wants to
310+
pickle a ``ZoneInfo`` constructed from a file, it is recommended that they
311+
use a wrapper type or a custom serialization function: either serializing by
312+
key or storing the contents of the file object and serializing that.
313+
314+
This method of serialization requires that the time zone data for the required
315+
key be available on both the serializing and deserializing side, similar to the
316+
way that references to classes and functions are expected to exist in both the
317+
serializing and deserializing environments. It also means that no guarantees
318+
are made about the consistency of results when unpickling a ZoneInfo pickled in
319+
an environment with a different version of the time zone data.
320+
321+
Functions
322+
---------
323+
324+
.. function:: reset_tzpath(to=None)
325+
326+
Sets or resets the time zone search path (:data:`TZPATH`) for the module.
327+
When called with no arguments, :data:`TZPATH` is set to the default value.
328+
329+
Calling ``reset_tzpath`` will not invalidate the :class:`ZoneInfo` cache,
330+
and so calls to the primary ``ZoneInfo`` constructor will only use the new
331+
``TZPATH`` in the case of a cache miss.
332+
333+
The ``to`` parameter must be a sequence (such as a list or a tuple) of
334+
strings or :class:`os.Pathlike` and not a string, all of which must be
335+
absolute paths. :exc:`ValueError` will be raised if something other than an
336+
absolute path is passed.
337+
338+
Globals
339+
-------
340+
341+
.. data:: TZPATH
342+
343+
A read-only sequence representing the time zone search path -- when
344+
constructing a ``ZoneInfo`` from a key, the key is joined to each entry in
345+
the ``TZPATH``, and the first file found is used.
346+
347+
``TZPATH`` may contain only absolute paths, never relative paths,
348+
regardless of how it is configured.
349+
350+
The object that ``zoneinfo.TZPATH`` points to may change in response to a
351+
call to :func:`reset_tzpath`, so it is recommended to use
352+
``zoneinfo.TZPATH`` rather than importing ``TZPATH`` from ``zoneinfo`` or
353+
assigning a long-lived variable to ``zoneinfo.TZPATH``.
354+
355+
For more information on configuring the time zone search path, see
356+
:ref:`zoneinfo_data_configuration`.
357+
358+
Exceptions and warnings
359+
-----------------------
360+
361+
.. exception:: ZoneInfoNotFoundError
362+
363+
Raised when construction of a :class:`ZoneInfo` object fails because the
364+
specified key could not be found on the system. This is a subclass of
365+
:exc:`KeyError`.
366+
367+
.. exception:: InvalidTZPathWarning
368+
369+
Raised when :envvar:`PYTHONTZPATH` contains an invalid component that will
370+
be filtered out, such as a relative path.

0 commit comments

Comments
 (0)