jeff00seattle
jeff00seattle

Reputation: 1471

Python: All possible Timezone Abbreviations for given Timezone Name (and vise versa)

Using pytz, I know how to get a listing a Timezone names, but I would like to get all possible Timezone abbreviations for each Timezone name:

import pytz
list(pytz.common_timezones)
['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa',...]

What I am looking for is given any Timezone abbreviation, example PST or PDT, ignoring current datetime (e.g. now), return the all possible Timezone name, in this case a list that would include America/Los_Angeles.

Thanks

Upvotes: 14

Views: 20347

Answers (5)

GG_Python
GG_Python

Reputation: 3541

Updating for py3.9+ (as discussed in PEP615), the new zoneinfo module may help:

from collections import defaultdict
from datetime import datetime as dt
from zoneinfo import available_timezones, ZoneInfo

now = dt.utcnow()
tz_key = lambda tz: ZoneInfo(tz).tzname(now)
tz_map = defaultdict(list)

for tz in available_timezones():
    tz_map[tz_key(tz)].append(tz)
tz_map = {k: sorted(v) for k, v in tz_map.items()}

e.g., print(tz_map['PDT']) would yield: ['America/Ensenada', 'America/Los_Angeles', 'America/Santa_Isabel', 'America/Tijuana', 'America/Vancouver', 'Canada/Pacific', 'Mexico/BajaNorte', 'PST8PDT', 'US/Pacific', 'US/Pacific-New']

Note: timezone draws from locally-installed timezone data. You can also add the 1st party tzdata library (not in standard lib, but maintained by python's core devs: use pip install tzdata).

Upvotes: 6

HansG600
HansG600

Reputation: 280

I like unutbu's answer, which led me to the answer that worked for me. I have the locale of the user, so I found that I can use that to remove the ambiguity between time zone abbreviations.

In other words, this fixes the problem with the following:

In [242]: 'Asia/Shanghai' in tzones['CST']
Out[242]: True

In [243]: 'US/Central' in tzones['CST']
Out[243]: True

This function requires a two letter country code:

def GetTimeZoneName(timezone, country_code):

    #see if it's already a valid time zone name
    if timezone in pytz.all_timezones:
        return timezone

    #if it's a number value, then use the Etc/GMT code
    try:
        offset = int(timezone)
        if offset > 0:
            offset = '+' + str(offset)
        else:
            offset = str(offset)
        return 'Etc/GMT' + offset
    except ValueError:
        pass

    #look up the abbreviation
    country_tzones = None
    try:
        country_tzones = pytz.country_timezones[country_code]
    except:
        pass

    set_zones = set()
    if country_tzones is not None and len(country_tzones) > 0:
        for name in country_tzones:
            tzone = pytz.timezone(name)
            for utcoffset, dstoffset, tzabbrev in getattr(tzone, '_transition_info', [[None, None, datetime.datetime.now(tzone).tzname()]]):
                if tzabbrev.upper() == timezone.upper():
                    set_zones.add(name)

        if len(set_zones) > 0:
            return min(set_zones, key=len)

        # none matched, at least pick one in the right country
        return min(country_tzones, key=len)


    #invalid country, just try to match the timezone abbreviation to any time zone
    for name in pytz.all_timezones:
        tzone = pytz.timezone(name)
        for utcoffset, dstoffset, tzabbrev in getattr(tzone, '_transition_info', [[None, None, datetime.datetime.now(tzone).tzname()]]):
            if tzabbrev.upper() == timezone.upper():
                set_zones.add(name)

    return min(set_zones, key=len)

This returns the correct time zones for CST:

>>> GetTimeZoneName('CST','CN')
'Asia/Shanghai'

>>> GetTimeZoneName('CST','US')
'America/Detroit'

Upvotes: 5

unutbu
unutbu

Reputation: 879093

Since you wish to ignore the current datetime, it sounds like you want to find any timezone which ever used the given abbreviation at any time in the past. That information is in the Olson database and accessible through pytz. However, pytz stores this information in the private attribute, tzone._transition_info:

import collections
import datetime as DT
import pytz

tzones = collections.defaultdict(set)
abbrevs = collections.defaultdict(set)

for name in pytz.all_timezones:
    tzone = pytz.timezone(name)
    for utcoffset, dstoffset, tzabbrev in getattr(
            tzone, '_transition_info', [[None, None, DT.datetime.now(tzone).tzname()]]):
        tzones[tzabbrev].add(name)
        abbrevs[name].add(tzabbrev)

The reason for the third (default) argument to gettattr is to handle a few timezones, such as Africa/Bujumbura, which never had any transitions. So the abbreviation in these cases is the current abbreviation.


In [94]: tzones['PST']
Out[94]: 
{'America/Bahia_Banderas',
 'America/Boise',
 'America/Creston',
 'America/Dawson',
 'America/Dawson_Creek',
 'America/Ensenada',
 'America/Hermosillo',
 'America/Inuvik',
 'America/Juneau',
 'America/Los_Angeles',
 'America/Mazatlan',
 'America/Metlakatla',
 'America/Santa_Isabel',
 'America/Sitka',
 'America/Tijuana',
 'America/Vancouver',
 'America/Whitehorse',
 'Canada/Pacific',
 'Canada/Yukon',
 'Mexico/BajaNorte',
 'Mexico/BajaSur',
 'PST8PDT',
 'Pacific/Pitcairn',
 'US/Pacific',
 'US/Pacific-New'}

In [95]: tzones['PDT']
Out[95]: 
{'America/Boise',
 'America/Dawson',
 'America/Dawson_Creek',
 'America/Ensenada',
 'America/Juneau',
 'America/Los_Angeles',
 'America/Metlakatla',
 'America/Santa_Isabel',
 'America/Sitka',
 'America/Tijuana',
 'America/Vancouver',
 'America/Whitehorse',
 'Canada/Pacific',
 'Canada/Yukon',
 'Mexico/BajaNorte',
 'PST8PDT',
 'US/Pacific',
 'US/Pacific-New'}

In [97]: abbrevs['America/Los_Angeles']
Out[97]: {'LMT', 'PDT', 'PPT', 'PST', 'PWT'}

As Paul points out, note that timezone abbreviations are ambiguous -- they do not necessarily map to timezones with the same utcoffset. For example, both Asia/Shanghai and US/Central use the CST timezone abbreviation.

In [242]: 'Asia/Shanghai' in tzones['CST']
Out[242]: True

In [243]: 'US/Central' in tzones['CST']
Out[243]: True

Upvotes: 11

naleves
naleves

Reputation: 11

import pytz
for tzname in list(pytz.common_timezones):
    print(pytz.timezone(tzname)._tzname)

Similar question

Upvotes: 0

Aaron Christiansen
Aaron Christiansen

Reputation: 11807

I did this using the datetime module's strftime function, as it has a %Z format specifier that returns the timezone abbreviation. Here is a filter statement that does what you need:

>>> filter(lambda x: datetime.datetime.now(pytz.timezone(x)).strftime("%Z") == "PDT", pytz.common_timezones)
['America/Dawson',
'America/Los_Angeles',
'America/Tijuana',
'America/Vancouver',
'America/Whitehorse',
'Canada/Pacific',
'US/Pacific']

You can replace "PDT" with whatever timezone you need. Remember you'll also need to import datetime.

Upvotes: 1

Related Questions