Reputation: 1471
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
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
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
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
Reputation: 11
import pytz
for tzname in list(pytz.common_timezones):
print(pytz.timezone(tzname)._tzname)
Upvotes: 0
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