diff options
author | Paul Ganssle <paul@ganssle.io> | 2020-05-16 16:14:58 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-16 16:14:58 (GMT) |
commit | b17e49e0def23238b9e7f48c8a02e2d7bbf1f653 (patch) | |
tree | 76e9f172832a34e53bdb4feeeedf91c33bedc771 | |
parent | 1b97b9b0ad9a2ff8eb5c8f2e2e7c2aec1d13a330 (diff) | |
download | cpython-b17e49e0def23238b9e7f48c8a02e2d7bbf1f653.zip cpython-b17e49e0def23238b9e7f48c8a02e2d7bbf1f653.tar.gz cpython-b17e49e0def23238b9e7f48c8a02e2d7bbf1f653.tar.bz2 |
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@python.org>
-rw-r--r-- | Doc/library/datatypes.rst | 1 | ||||
-rw-r--r-- | Doc/library/zoneinfo.rst | 390 | ||||
-rw-r--r-- | Doc/whatsnew/3.9.rst | 39 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-05-08-15-48-39.bpo-40503.elZyxc.rst | 1 |
4 files changed, 430 insertions, 1 deletions
diff --git a/Doc/library/datatypes.rst b/Doc/library/datatypes.rst index 94010c0..675bbb6 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 0000000..d8e2796 --- /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@ganssle.io> +.. sectionauthor:: Paul Ganssle <paul@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 bddb710..479c33b 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 0000000..d68797a --- /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. |