[Python-checkins] bpo-40503: Add documentation and what's new entry for zoneinfo (GH-20006)

Paul Ganssle webhook-mailer at python.org
Sat May 16 12:15:07 EDT 2020


https://github.com/python/cpython/commit/b17e49e0def23238b9e7f48c8a02e2d7bbf1f653
commit: b17e49e0def23238b9e7f48c8a02e2d7bbf1f653
branch: master
author: Paul Ganssle <paul at ganssle.io>
committer: GitHub <noreply at github.com>
date: 2020-05-16T12:14:58-04:00
summary:

bpo-40503: Add documentation and what's new entry for zoneinfo (GH-20006)

This adds the documentation for the `zoneinfo` module added in PEP 615: https://www.python.org/dev/peps/pep-0615/

The implementation itself was GH-19909: https://github.com/python/cpython/pull/19909

bpo-40503: https://bugs.python.org/issue40503

Co-authored-by: Victor Stinner <vstinner at python.org>

files:
A Doc/library/zoneinfo.rst
A Misc/NEWS.d/next/Library/2020-05-08-15-48-39.bpo-40503.elZyxc.rst
M Doc/library/datatypes.rst
M Doc/whatsnew/3.9.rst

diff --git a/Doc/library/datatypes.rst b/Doc/library/datatypes.rst
index 94010c0e391b0..675bbb6fafdca 100644
--- a/Doc/library/datatypes.rst
+++ b/Doc/library/datatypes.rst
@@ -20,6 +20,7 @@ The following modules are documented in this chapter:
 .. toctree::
 
    datetime.rst
+   zoneinfo.rst
    calendar.rst
    collections.rst
    collections.abc.rst
diff --git a/Doc/library/zoneinfo.rst b/Doc/library/zoneinfo.rst
new file mode 100644
index 0000000000000..d8e2796c7f631
--- /dev/null
+++ b/Doc/library/zoneinfo.rst
@@ -0,0 +1,390 @@
+:mod:`zoneinfo` --- IANA time zone support
+==========================================
+
+.. module:: zoneinfo
+    :synopsis: IANA time zone support
+
+.. versionadded:: 3.9
+
+.. moduleauthor:: Paul Ganssle <paul at ganssle.io>
+.. sectionauthor:: Paul Ganssle <paul at ganssle.io>
+
+--------------
+
+The :mod:`zoneinfo` module provides a concrete time zone implementation to
+support the IANA time zone database as originally specified in :pep:`615`. By
+default, :mod:`zoneinfo` uses the system's time zone data if available; if no
+system time zone data is available, the library will fall back to using the
+first-party `tzdata`_ package available on PyPI.
+
+.. seealso::
+
+    Module: :mod:`datetime`
+        Provides the :class:`~datetime.time` and :class:`~datetime.datetime`
+        types with which the :class:`ZoneInfo` class is designed to be used.
+
+    Package `tzdata`_
+        First-party package maintained by the CPython core developers to supply
+        time zone data via PyPI.
+
+
+Using ``ZoneInfo``
+------------------
+
+:class:`ZoneInfo` is a concrete implementation of the :class:`datetime.tzinfo`
+abstract base class, and is intended to be attached to ``tzinfo``, either via
+the constructor, the :meth:`datetime.replace <datetime.datetime.replace>`
+method or :meth:`datetime.astimezone <datetime.datetime.astimezone>`::
+
+    >>> 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'
+
+Datetimes constructed in this way are compatible with datetime arithmetic and
+handle daylight saving time transitions with no further intervention::
+
+    >>> dt_add = dt + timedelta(days=1)
+
+    >>> print(dt_add)
+    2020-11-01 12:00:00-08:00
+
+    >>> dt_add.tzname()
+    'PST'
+
+These time zones also support the :attr:`~datetime.datetime.fold` attribute
+introduced in :pep:`495`.  During offset transitions which induce ambiguous
+times (such as a daylight saving time to standard time transition), the offset
+from *before* the transition is used when ``fold=0``, and the offset *after*
+the transition is used when ``fold=1``, for example::
+
+    >>> 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
+
+When converting from another time zone, the fold will be set to the correct
+value::
+
+    >>> 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
+
+Data sources
+------------
+
+The ``zoneinfo`` module does not directly provide time zone data, and instead
+pulls time zone information from the system time zone database or the
+first-party PyPI package `tzdata`_, if available. Some systems, including
+notably Windows systems, do not have an IANA database available, and so for
+projects targeting cross-platform compatibility that require time zone data, it
+is recommended to declare a dependency on tzdata. If neither system data nor
+tzdata are available, all calls to :class:`ZoneInfo` will raise
+:exc:`ZoneInfoNotFoundError`.
+
+.. _zoneinfo_data_configuration:
+
+Configuring the data sources
+****************************
+
+When ``ZoneInfo(key)`` is called, the constructor first searches the
+directories specified in :data:`TZPATH` for a file matching ``key``, and on
+failure looks for a match in the tzdata package. This behavior can be
+configured in three ways:
+
+1. The default :data:`TZPATH` when not otherwise specified can be configured at
+   :ref:`compile time <zoneinfo_data_compile_time_config>`.
+2. :data:`TZPATH` can be configured using :ref:`an environment variable
+   <zoneinfo_data_environment_var>`.
+3. At :ref:`runtime <zoneinfo_data_runtime_config>`, the search path can be
+   manipulated using the :func:`reset_tzpath` function.
+
+.. _zoneinfo_data_compile_time_config:
+
+Compile-time configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The default :data:`TZPATH` includes several common deployment locations for the
+time zone database (except on Windows, where there are no "well-known"
+locations for time zone data). On POSIX systems, downstream distributors and
+those building Python from source who know where their system
+time zone data is deployed may change the default time zone path by specifying
+the compile-time option ``TZPATH`` (or, more likely, the ``configure`` flag
+``--with-tzpath``), which should be a string delimited by :data:`os.pathsep`.
+
+On all platforms, the configured value is available as the ``TZPATH`` key in
+:func:`sysconfig.get_config_var`.
+
+.. _zoneinfo_data_environment_var:
+
+Environment configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When initializing :data:`TZPATH` (either at import time or whenever
+:func:`reset_tzpath` is called with no arguments), the ``zoneinfo`` module will
+use the environment variable ``PYTHONTZPATH``, if it exists, to set the search
+path.
+
+.. envvar:: PYTHONTZPATH
+
+    This is an :data:`os.pathsep`-separated string containing the time zone
+    search path to use. It must consist of only absolute rather than relative
+    paths. Relative components specified in ``PYTHONTZPATH`` will not be used,
+    but otherwise the behavior when a relative path is specified is
+    implementation-defined; CPython will raise :exc:`InvalidTZPathWarning`, but
+    other implementations are free to silently ignore the erroneous component
+    or raise an exception.
+
+To set the system to ignore the system data and use the tzdata package
+instead, set ``PYTHONTZPATH=""``.
+
+.. _zoneinfo_data_runtime_config:
+
+Runtime configuration
+^^^^^^^^^^^^^^^^^^^^^
+
+The TZ search path can also be configured at runtime using the
+:func:`reset_tzpath` function. This is generally not an advisable operation,
+though it is reasonable to use it in test functions that require the use of a
+specific time zone path (or require disabling access to the system time zones).
+
+
+The ``ZoneInfo`` class
+----------------------
+
+.. class:: ZoneInfo(key)
+
+    A concrete :class:`datetime.tzinfo` subclass that represents an IANA time
+    zone specified by the string ``key``. Calls to the primary constructor will
+    always return objects that compare identically; put another way, barring
+    cache invalidation via :meth:`ZoneInfo.clear_cache`, for all values of
+    ``key``, the following assertion will always be true:
+
+    .. code-block:: python
+
+        a = ZoneInfo(key)
+        b = ZoneInfo(key)
+        assert a is b
+
+    ``key`` must be in the form of a relative, normalized POSIX path, with no
+    up-level references. The constructor will raise :exc:`ValueError` if a
+    non-conforming key is passed.
+
+    If no file matching ``key`` is found, the constructor will raise
+    :exc:`ZoneInfoNotFoundError`.
+
+
+The ``ZoneInfo`` class has two alternate constructors:
+
+.. classmethod:: ZoneInfo.from_file(fobj, /, key=None)
+
+    Constructs a ``ZoneInfo`` object from a file-like object returning bytes
+    (e.g. a file opened in binary mode or an :class:`io.BytesIO` object).
+    Unlike the primary constructor, this always constructs a new object.
+
+    The ``key`` parameter sets the name of the zone for the purposes of
+    :py:meth:`~object.__str__` and :py:meth:`~object.__repr__`.
+
+    Objects created via this constructor cannot be pickled (see `pickling`_).
+
+.. classmethod:: ZoneInfo.no_cache(key)
+
+    An alternate constructor that bypasses the constructor's cache. It is
+    identical to the primary constructor, but returns a new object on each
+    call. This is most likely to be useful for testing or demonstration
+    purposes, but it can also be used to create a system with a different cache
+    invalidation strategy.
+
+    Objects created via this constructor will also bypass the cache of a
+    deserializing process when unpickled.
+
+    .. TODO: Add "See `cache_behavior`_" reference when that section is ready.
+
+    .. caution::
+
+        Using this constructor may change the semantics of your datetimes in
+        surprising ways, only use it if you know that you need to.
+
+The following class methods are also available:
+
+.. classmethod:: ZoneInfo.clear_cache(*, only_keys=None)
+
+    A method for invalidating the cache on the ``ZoneInfo`` class. If no
+    arguments are passed, all caches are invalidated and the next call to
+    the primary constructor for each key will return a new instance.
+
+    If an iterable of key names is passed to the ``only_keys`` parameter, only
+    the specified keys will be removed from the cache. Keys passed to
+    ``only_keys`` but not found in the cache are ignored.
+
+    .. TODO: Add "See `cache_behavior`_" reference when that section is ready.
+
+    .. warning::
+
+        Invoking this function may change the semantics of datetimes using
+        ``ZoneInfo`` in surprising ways; this modifies process-wide global state
+        and thus may have wide-ranging effects. Only use it if you know that you
+        need to.
+
+The class has one attribute:
+
+.. attribute:: ZoneInfo.key
+
+    This is a read-only :term:`attribute` that returns the value of ``key``
+    passed to the constructor, which should be a lookup key in the IANA time
+    zone database (e.g. ``America/New_York``, ``Europe/Paris`` or
+    ``Asia/Tokyo``).
+
+    For zones constructed from file without specifying a ``key`` parameter,
+    this will be set to ``None``.
+
+    .. note::
+
+        Although it is a somewhat common practice to expose these to end users,
+        these values are designed to be primary keys for representing the
+        relevant zones and not necessarily user-facing elements.  Projects like
+        CLDR (the Unicode Common Locale Data Repository) can be used to get
+        more user-friendly strings from these keys.
+
+String representations
+**********************
+
+The string representation returned when calling :py:class:`str` on a
+:class:`ZoneInfo` object defaults to using the :attr:`ZoneInfo.key` attribute (see
+the note on usage in the attribute documentation)::
+
+    >>> 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]'
+
+For objects constructed from a file without specifying a ``key`` parameter,
+``str`` falls back to calling :func:`repr`. ``ZoneInfo``'s ``repr`` is
+implementation-defined and not necessarily stable between versions, but it is
+guaranteed not to be a valid ``ZoneInfo`` key.
+
+.. _pickling:
+
+Pickle serialization
+********************
+
+Rather than serializing all transition data, ``ZoneInfo`` objects are
+serialized by key, and ``ZoneInfo`` objects constructed from files (even those
+with a value for ``key`` specified) cannot be pickled.
+
+The behavior of a ``ZoneInfo`` file depends on how it was constructed:
+
+1. ``ZoneInfo(key)``: When constructed with the primary constructor, a
+   ``ZoneInfo`` object is serialized by key, and when deserialized, the
+   deserializing process uses the primary and thus it is expected that these
+   are expected to be the same object as other references to the same time
+   zone.  For example, if ``europe_berlin_pkl`` is a string containing a pickle
+   constructed from ``ZoneInfo("Europe/Berlin")``, one would expect the
+   following behavior:
+
+   .. code-block::
+
+       >>> a = ZoneInfo("Europe/Berlin")
+       >>> b = pickle.loads(europe_berlin_pkl)
+       >>> a is b
+       True
+
+2. ``ZoneInfo.no_cache(key)``: When constructed from the cache-bypassing
+   constructor, the ``ZoneInfo`` object is also serialized by key, but when
+   deserialized, the deserializing process uses the cache bypassing
+   constructor. If ``europe_berlin_pkl_nc`` is a string containing a pickle
+   constructed from ``ZoneInfo.no_cache("Europe/Berlin")``, one would expect
+   the following behavior:
+
+   .. code-block::
+
+       >>> a = ZoneInfo("Europe/Berlin")
+       >>> b = pickle.loads(europe_berlin_pkl_nc)
+       >>> a is b
+       False
+
+3. ``ZoneInfo.from_file(fobj, /, key=None)``: When constructed from a file, the
+   ``ZoneInfo`` object raises an exception on pickling. If an end user wants to
+   pickle a ``ZoneInfo`` constructed from a file, it is recommended that they
+   use a wrapper type or a custom serialization function: either serializing by
+   key or storing the contents of the file object and serializing that.
+
+This method of serialization requires that the time zone data for the required
+key be available on both the serializing and deserializing side, similar to the
+way that references to classes and functions are expected to exist in both the
+serializing and deserializing environments. It also means that no guarantees
+are made about the consistency of results when unpickling a ``ZoneInfo``
+pickled in an environment with a different version of the time zone data.
+
+Functions
+---------
+
+.. function:: reset_tzpath(to=None)
+
+    Sets or resets the time zone search path (:data:`TZPATH`) for the module.
+    When called with no arguments, :data:`TZPATH` is set to the default value.
+
+    Calling ``reset_tzpath`` will not invalidate the :class:`ZoneInfo` cache,
+    and so calls to the primary ``ZoneInfo`` constructor will only use the new
+    ``TZPATH`` in the case of a cache miss.
+
+    The ``to`` parameter must be a :term:`sequence` of strings or
+    :class:`os.PathLike` and not a string, all of which must be absolute paths.
+    :exc:`ValueError` will be raised if something other than an absolute path
+    is passed.
+
+Globals
+-------
+
+.. data:: TZPATH
+
+    A read-only sequence representing the time zone search path -- when
+    constructing a ``ZoneInfo`` from a key, the key is joined to each entry in
+    the ``TZPATH``, and the first file found is used.
+
+    ``TZPATH`` may contain only absolute paths, never relative paths,
+    regardless of how it is configured.
+
+    The object that ``zoneinfo.TZPATH`` points to may change in response to a
+    call to :func:`reset_tzpath`, so it is recommended to use
+    ``zoneinfo.TZPATH`` rather than importing ``TZPATH`` from ``zoneinfo`` or
+    assigning a long-lived variable to ``zoneinfo.TZPATH``.
+
+    For more information on configuring the time zone search path, see
+    :ref:`zoneinfo_data_configuration`.
+
+Exceptions and warnings
+-----------------------
+
+.. exception:: ZoneInfoNotFoundError
+
+    Raised when construction of a :class:`ZoneInfo` object fails because the
+    specified key could not be found on the system. This is a subclass of
+    :exc:`KeyError`.
+
+.. exception:: InvalidTZPathWarning
+
+    Raised when :envvar:`PYTHONTZPATH` contains an invalid component that will
+    be filtered out, such as a relative path.
+
+.. Links and references:
+
+.. _tzdata: https://pypi.org/project/tzdata/
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index bddb7102d90ee..479c33b4a7fa1 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -205,7 +205,44 @@ Other Language Changes
 New Modules
 ===========
 
-* None yet.
+zoneinfo
+--------
+
+The :mod:`zoneinfo` module brings support for the IANA time zone database to
+the standard library. It adds :class:`zoneinfo.ZoneInfo`, a concrete
+:class:`datetime.tzinfo` implementation backed by the system's time zone data.
+
+Example::
+
+    >>> from zoneinfo import ZoneInfo
+    >>> from datetime import datetime, timedelta
+
+    >>> # Daylight saving time
+    >>> dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
+    >>> print(dt)
+    2020-10-31 12:00:00-07:00
+    >>> dt.tzname()
+    'PDT'
+
+    >>> # Standard time
+    >>> dt += timedelta(days=7)
+    >>> print(dt)
+    2020-11-07 12:00:00-08:00
+    >>> print(dt.tzname())
+    PST
+
+
+As a fall-back source of data for platforms that don't ship the IANA database,
+the |tzdata|_ module was released as a first-party package -- distributed via
+PyPI and maintained by the CPython core team.
+
+.. |tzdata| replace:: ``tzdata``
+.. _tzdata: https://pypi.org/project/tzdata/
+
+.. seealso::
+
+    :pep:`615` -- Support for the IANA Time Zone Database in the Standard Library
+        PEP written and implemented by Paul Ganssle
 
 
 Improved Modules
diff --git a/Misc/NEWS.d/next/Library/2020-05-08-15-48-39.bpo-40503.elZyxc.rst b/Misc/NEWS.d/next/Library/2020-05-08-15-48-39.bpo-40503.elZyxc.rst
new file mode 100644
index 0000000000000..d68797a738c2c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-05-08-15-48-39.bpo-40503.elZyxc.rst
@@ -0,0 +1 @@
+:pep:`615`, the :mod:`zoneinfo` module. Adds support for the IANA time zone database.



More information about the Python-checkins mailing list