Using Python dateutil, how to judge a timezone string is "valid" or not?

Question:

I am using the following code to do the UTC time to local time conversion:

def UTC_to_local(timezone_str, datetime_UTC):
    """
    convert UTC datetime to local datetime. Input datetime is naive
    """
    try:
        from_zone = dateutil.tz.gettz('UTC')
        to_zone = dateutil.tz.gettz(timezone_str)

        datetime_UTC = datetime_UTC.replace(tzinfo=from_zone)

        # Convert time zone
        datetime_local = datetime_UTC.astimezone(to_zone)

    except Exception as e:
        raise

    return datetime_local

If I gave the correct timezone_str (e.g., ‘America/Chicago’), it works as expected.
But even I give the unexpected timezone_str (e.g., ‘America/Chicago1’ or ‘Americaerror/Chicago’), there is still no exception and it just returns different numbers! I think it’s more reasonable to get an exception for an unexpected timezone string than just “making the best guess”.

Furthermore, I have found(using IPYTHON):

In [171]: tz.gettz("America/Chicago")
Out[171]: tzfile('/usr/share/zoneinfo/America/Chicago')

In [172]: tz.gettz("America/Chicago1")
Out[172]: tzstr('America/Chicago1')

In [173]: tz.gettz("Americaerror/Chicago")
          (None)
Asked By: Hao Shen

||

Answers:

Solution #1: If you can use pytz

import pytz

if timezone_str in pytz.all_timezones:
    ...
else:
    raise ValueError('Invalid timezone string!')

Solution #2:

import os
import tarfile
import dateutil.zoneinfo

zi_path = os.path.abspath(os.path.dirname(dateutil.zoneinfo.__file__))
zonesfile = tarfile.TarFile.open(os.path.join(zi_path, 'dateutil-zoneinfo.tar.gz'))
zonenames = zonesfile.getnames()

if timezone_str in zonenames:
    ...
else:
    raise ValueError('Invalid timezone string!')
Answered By: Sergey Gornostaev

Sergey’s answer is great, but if you go for Solution #1 then it is better to use pytz.all_timezones_set:

if location in pytz.all_timezones_set
    ...

Here’s the speed comparison between the two:

In [14]: %timeit "Asia/Omsk" in pytz.all_timezones
3.09 µs ± 7.66 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [15]: %timeit "Asia/Omsk" in pytz.all_timezones_set
96.5 ns ± 0.0991 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Note: the speed of checking in pytz.all_timezones depends on the item searched. I took the one in the middle of the list (Asia/Omsk is the 296th item out of 593 total).

Answered By: stasdeep

As of python 3.9, zoneinfo is a built-in module, and you can test it without needing external modules like pytz:

from zoneinfo import available_timezones

valid_timezone = "America/Los_Angeles"
invalid_timezone = "spam"

def timezone_is_valid(timezone_string):
  return timezone_string in available_timezones()

timezone_is_valid(valid_timezone)  # Returns True
timezone_is_valid(invalid_timezone)  # Returns False
Answered By: rnorris
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.