question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Upgrade to 4.0 causes ZoneInfoNotFoundError

See original GitHub issue

Summary

Our project uses dateparses, which again depends on tzlocal. After auto-upgrading our packages, and therunder tzlocal from 3.0 to 4.0 our build started failing. Its either that or related to your dependency pytz-deprecation-shim.

When i pinned our build to tzlocal = 3.0, the problem went away.

Details

  • tzlocal version = 4.0
  • Docker image: python:3.9.6-buster

Other things:

  • We use poetry lock

Our code

def any_to_datetime(s: str) -> datetime:
    # Hack around that '9999-12-31T23:59:59' crashes when parsed
    if s[:4] == "9999":
        d = datetime(9999, 12, 31, 23, 59, 59)
        return ensure_timezoned(d)
    if parsed_datetime := dateparser.parse(s):  # <- Line 18, raises tzlocal.utils.ZoneInfoNotFoundError
        return ensure_timezoned(parsed_datetime)

    raise Exception(f"Could not parse {s=}")

Exception

../api_import/utils/date.py:18: in any_to_datetime
    if parsed_datetime := dateparser.parse(s):
/usr/local/lib/python3.9/site-packages/dateparser/conf.py:92: in wrapper
    return f(*args, **kwargs)
/usr/local/lib/python3.9/site-packages/dateparser/__init__.py:61: in parse
    data = parser.get_date_data(date_string, date_formats)
/usr/local/lib/python3.9/site-packages/dateparser/date.py:428: in get_date_data
    parsed_date = _DateLocaleParser.parse(
/usr/local/lib/python3.9/site-packages/dateparser/date.py:178: in parse
    return instance._parse()
/usr/local/lib/python3.9/site-packages/dateparser/date.py:182: in _parse
    date_data = self._parsers[parser_name]()
/usr/local/lib/python3.9/site-packages/dateparser/date.py:196: in _try_freshness_parser
    return freshness_date_parser.get_date_data(self._get_translated_date(), self._settings)
/usr/local/lib/python3.9/site-packages/dateparser/freshness_date_parser.py:156: in get_date_data
    date, period = self.parse(date_string, settings)
/usr/local/lib/python3.9/site-packages/dateparser/freshness_date_parser.py:93: in parse
    now = datetime.now(self.get_local_tz())
/usr/local/lib/python3.9/site-packages/dateparser/freshness_date_parser.py:41: in get_local_tz
    return get_localzone()
/usr/local/lib/python3.9/site-packages/tzlocal/unix.py:205: in get_localzone
    _cache_tz = _get_localzone()
/usr/local/lib/python3.9/site-packages/tzlocal/unix.py:167: in _get_localzone
    tzname = _get_localzone_name(_root)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
_root = '/'
    def _get_localzone_name(_root="/"):
        """Tries to find the local timezone configuration.
    
        This method finds the timezone name, if it can, or it returns None.
    
        The parameter _root makes the function look for files like /etc/localtime
        beneath the _root directory. This is primarily used by the tests.
        In normal usage you call the function without parameters."""
    
        # First try the ENV setting.
        tzenv = utils._tz_name_from_env()
        if tzenv:
            return tzenv
    
        # Are we under Termux on Android?
        if os.path.exists(os.path.join(_root, "system/bin/getprop")):
            import subprocess
    
            androidtz = (
                subprocess.check_output(["getprop", "persist.sys.timezone"])
                .strip()
                .decode()
            )
            return androidtz
    
        # Now look for distribution specific configuration files
        # that contain the timezone name.
    
        # Stick all of them in a dict, to compare later.
        found_configs = {}
    
        for configfile in ("etc/timezone", "var/db/zoneinfo"):
            tzpath = os.path.join(_root, configfile)
            try:
                with open(tzpath, "rt") as tzfile:
                    data = tzfile.read()
    
                    etctz = data.strip()
                    if not etctz:
                        # Empty file, skip
                        continue
                    for etctz in data.splitlines():
                        # Get rid of host definitions and comments:
                        if " " in etctz:
                            etctz, dummy = etctz.split(" ", 1)
                        if "#" in etctz:
                            etctz, dummy = etctz.split("#", 1)
                        if not etctz:
                            continue
    
                        found_configs[tzpath] = etctz.replace(" ", "_")
    
            except (IOError, UnicodeDecodeError):
                # File doesn't exist or is a directory, or it's a binary file.
                continue
    
        # CentOS has a ZONE setting in /etc/sysconfig/clock,
        # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and
        # Gentoo has a TIMEZONE setting in /etc/conf.d/clock
        # We look through these files for a timezone:
    
        zone_re = re.compile(r"\s*ZONE\s*=\s*\"")
        timezone_re = re.compile(r"\s*TIMEZONE\s*=\s*\"")
        end_re = re.compile('"')
    
        for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"):
            tzpath = os.path.join(_root, filename)
            try:
                with open(tzpath, "rt") as tzfile:
                    data = tzfile.readlines()
    
                for line in data:
                    # Look for the ZONE= setting.
                    match = zone_re.match(line)
                    if match is None:
                        # No ZONE= setting. Look for the TIMEZONE= setting.
                        match = timezone_re.match(line)
                    if match is not None:
                        # Some setting existed
                        line = line[match.end():]
                        etctz = line[: end_re.search(line).start()]
    
                        # We found a timezone
                        found_configs[tzpath] = etctz.replace(" ", "_")
    
            except (IOError, UnicodeDecodeError):
                # UnicodeDecode handles when clock is symlink to /etc/localtime
                continue
    
        # systemd distributions use symlinks that include the zone name,
        # see manpage of localtime(5) and timedatectl(1)
        tzpath = os.path.join(_root, "etc/localtime")
        if os.path.exists(tzpath) and os.path.islink(tzpath):
            etctz = tzpath = os.path.realpath(tzpath)
            start = etctz.find("/") + 1
            while start != 0:
                etctz = etctz[start:]
                try:
                    pds.timezone(etctz)
                    found_configs[tzpath] = etctz.replace(" ", "_")
                except pds.UnknownTimeZoneError:
                    pass
                start = etctz.find("/") + 1
    
        if len(found_configs) > 0:
            # We found some explicit config of some sort!
            if len(found_configs) > 1:
                # Uh-oh, multiple configs. See if they match:
                unique_tzs = set()
                for tzname in found_configs.values():
                    # Get rid of any Etc's
                    tzname = tzname.replace("Etc/", "")
                    # In practice these are the same:
                    tzname = tzname.replace("UTC", "GMT")
                    # Let's handle these synonyms as well. Many systems have tons
                    # of synonyms, including country names and "Zulu" and other
                    # nonsense. Those will be seen as different ones. Let's stick
                    # to the official zoneinfo Continent/City names.
                    if tzname in ["GMT0", "GMT+0", "GMT-0"]:
                        tzname = "GMT"
                    unique_tzs.add(tzname)
    
                if len(unique_tzs) != 1:
                    message = "Multiple conflicting time zone configurations found:\n"
                    for key, value in found_configs.items():
                        message += f"{key}: {value}\n"
                    message += "Fix the configuration, or set the time zone in a TZ environment variable.\n"
>                   raise utils.ZoneInfoNotFoundError(message)
E                   tzlocal.utils.ZoneInfoNotFoundError: 'Multiple conflicting time zone configurations found:\n/etc/timezone: Etc/UTC\n/usr/share/zoneinfo/UCT: UCT\nFix the configuration, or set the time zone in a TZ environment variable.\n'
/usr/local/lib/python3.9/site-packages/tzlocal/unix.py:146: ZoneInfoNotFoundError

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:2
  • Comments:11 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
regebrocommented, Oct 19, 2021

OK, I released 4.0.1.

3reactions
regebrocommented, Oct 18, 2021

I released a 4.0.1b1 with this fix, see if that fixes it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Django 4 Error: 'No time zone found with key ...'
After upgrades pytz and tzdata my Django project is working just fine again! ... test django in 4.0.5 and 4.1 will working: my...
Read more >
Time zones - Django documentation
The main reason is daylight saving time (DST). ... Changed in Django 4.0: ... Use them to build the time zone selection logic...
Read more >
[Django] #33674: Timezones cause 500 errors to not email the ...
and the users are no longer sent to my 500 page. Here's some information: * debug=True causes things to work correctly (no email...
Read more >
What is the zoneinfo.ZoneInfoNotFoundError in Python?
It is raised by the ZoneInfo class constructor if no matching file is found on the search path. Possible reasons for errors: System...
Read more >
zoneinfo — IANA time zone support — Python 3.11.1 ...
... recommended to declare a dependency on tzdata. If neither system data nor tzdata are available, all calls to ZoneInfo will raise ZoneInfoNotFoundError...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found