Spaces:
Runtime error
Runtime error
| # -*- coding: utf-8 -*- | |
| import warnings | |
| import json | |
| from tarfile import TarFile | |
| from pkgutil import get_data | |
| from io import BytesIO | |
| from dateutil.tz import tzfile as _tzfile | |
| __all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] | |
| ZONEFILENAME = "dateutil-zoneinfo.tar.gz" | |
| METADATA_FN = 'METADATA' | |
| class tzfile(_tzfile): | |
| def __reduce__(self): | |
| return (gettz, (self._filename,)) | |
| def getzoneinfofile_stream(): | |
| try: | |
| return BytesIO(get_data(__name__, ZONEFILENAME)) | |
| except IOError as e: # TODO switch to FileNotFoundError? | |
| warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) | |
| return None | |
| class ZoneInfoFile(object): | |
| def __init__(self, zonefile_stream=None): | |
| if zonefile_stream is not None: | |
| with TarFile.open(fileobj=zonefile_stream) as tf: | |
| self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) | |
| for zf in tf.getmembers() | |
| if zf.isfile() and zf.name != METADATA_FN} | |
| # deal with links: They'll point to their parent object. Less | |
| # waste of memory | |
| links = {zl.name: self.zones[zl.linkname] | |
| for zl in tf.getmembers() if | |
| zl.islnk() or zl.issym()} | |
| self.zones.update(links) | |
| try: | |
| metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) | |
| metadata_str = metadata_json.read().decode('UTF-8') | |
| self.metadata = json.loads(metadata_str) | |
| except KeyError: | |
| # no metadata in tar file | |
| self.metadata = None | |
| else: | |
| self.zones = {} | |
| self.metadata = None | |
| def get(self, name, default=None): | |
| """ | |
| Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method | |
| for retrieving zones from the zone dictionary. | |
| :param name: | |
| The name of the zone to retrieve. (Generally IANA zone names) | |
| :param default: | |
| The value to return in the event of a missing key. | |
| .. versionadded:: 2.6.0 | |
| """ | |
| return self.zones.get(name, default) | |
| # The current API has gettz as a module function, although in fact it taps into | |
| # a stateful class. So as a workaround for now, without changing the API, we | |
| # will create a new "global" class instance the first time a user requests a | |
| # timezone. Ugly, but adheres to the api. | |
| # | |
| # TODO: Remove after deprecation period. | |
| _CLASS_ZONE_INSTANCE = [] | |
| def get_zonefile_instance(new_instance=False): | |
| """ | |
| This is a convenience function which provides a :class:`ZoneInfoFile` | |
| instance using the data provided by the ``dateutil`` package. By default, it | |
| caches a single instance of the ZoneInfoFile object and returns that. | |
| :param new_instance: | |
| If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and | |
| used as the cached instance for the next call. Otherwise, new instances | |
| are created only as necessary. | |
| :return: | |
| Returns a :class:`ZoneInfoFile` object. | |
| .. versionadded:: 2.6 | |
| """ | |
| if new_instance: | |
| zif = None | |
| else: | |
| zif = getattr(get_zonefile_instance, '_cached_instance', None) | |
| if zif is None: | |
| zif = ZoneInfoFile(getzoneinfofile_stream()) | |
| get_zonefile_instance._cached_instance = zif | |
| return zif | |
| def gettz(name): | |
| """ | |
| This retrieves a time zone from the local zoneinfo tarball that is packaged | |
| with dateutil. | |
| :param name: | |
| An IANA-style time zone name, as found in the zoneinfo file. | |
| :return: | |
| Returns a :class:`dateutil.tz.tzfile` time zone object. | |
| .. warning:: | |
| It is generally inadvisable to use this function, and it is only | |
| provided for API compatibility with earlier versions. This is *not* | |
| equivalent to ``dateutil.tz.gettz()``, which selects an appropriate | |
| time zone based on the inputs, favoring system zoneinfo. This is ONLY | |
| for accessing the dateutil-specific zoneinfo (which may be out of | |
| date compared to the system zoneinfo). | |
| .. deprecated:: 2.6 | |
| If you need to use a specific zoneinfofile over the system zoneinfo, | |
| instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call | |
| :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. | |
| Use :func:`get_zonefile_instance` to retrieve an instance of the | |
| dateutil-provided zoneinfo. | |
| """ | |
| warnings.warn("zoneinfo.gettz() will be removed in future versions, " | |
| "to use the dateutil-provided zoneinfo files, instantiate a " | |
| "ZoneInfoFile object and use ZoneInfoFile.zones.get() " | |
| "instead. See the documentation for details.", | |
| DeprecationWarning) | |
| if len(_CLASS_ZONE_INSTANCE) == 0: | |
| _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) | |
| return _CLASS_ZONE_INSTANCE[0].zones.get(name) | |
| def gettz_db_metadata(): | |
| """ Get the zonefile metadata | |
| See `zonefile_metadata`_ | |
| :returns: | |
| A dictionary with the database metadata | |
| .. deprecated:: 2.6 | |
| See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, | |
| query the attribute ``zoneinfo.ZoneInfoFile.metadata``. | |
| """ | |
| warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " | |
| "versions, to use the dateutil-provided zoneinfo files, " | |
| "ZoneInfoFile object and query the 'metadata' attribute " | |
| "instead. See the documentation for details.", | |
| DeprecationWarning) | |
| if len(_CLASS_ZONE_INSTANCE) == 0: | |
| _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) | |
| return _CLASS_ZONE_INSTANCE[0].metadata | |