timezone: Correct common_timezones dictionary.

The changes are as follows:

• Fix one day offset in all western zones.
• Correct CST from -64800 to -21600 and CDT from -68400 to -18000.
• Disambiguate PST in favor of -28000 over +28000.
• Add GMT, UTC, WET, previously excluded for being at offset 0.
• Add ACDT, AEDT, AKST, MET, MSK, NST, NZDT, PKT, which the previous
  code did not find.
• Remove numbered abbreviations -12, …, +14, which are unnecessary.
• Remove MSD and PKST, which are no longer used.

Hardcode the dict and verify it with a test, so that future
discrepancies won’t go silently unnoticed.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2021-01-27 13:12:36 -08:00
committed by Anders Kaseorg
parent fd8504e06b
commit 4ca66e7278
3 changed files with 101 additions and 44 deletions

View File

@@ -1,7 +1,6 @@
import datetime
from functools import lru_cache
from io import TextIOWrapper
from typing import Any, Dict, Union
from typing import Dict
import pytz
@@ -21,42 +20,58 @@ def get_canonical_timezone_map() -> Dict[str, str]:
def canonicalize_timezone(key: str) -> str:
return get_canonical_timezone_map().get(key, key)
# This method carefully trims a list of common timezones in the pytz
# database and handles duplicate abbreviations in favor of the most
# common/popular offset. The output of this can be directly passed as
# tz_data to dateutil.parser. It takes about 25ms to run, so we want
# to cache its results (while avoiding running it on process startup
# since we only need it for Markdown rendering).
@lru_cache(maxsize=None)
def get_common_timezones() -> Dict[str, Union[int, Any]]:
tzdata = {}
normal = datetime.datetime(2009, 9, 1) # Any random date is fine here.
for str in pytz.all_timezones:
tz = pytz.timezone(str)
timedelta = tz.utcoffset(normal)
if not timedelta:
continue
offset = timedelta.seconds
tz_name = tz.tzname(normal)
tzdata[tz_name] = offset
# Handle known duplicates/exceptions.
# IST: Asia/Kolkata and Europe/Dublin.
if tz_name == 'IST':
tzdata[tz_name] = 19800 # Asia/Kolkata
# CDT: America/AlmostAll and America/Havana.
if tz_name == 'CDT':
tzdata[tz_name] = -68400 # America/AlmostAll
# CST America/Belize -64800
# CST America/Costa_Rica -64800
# CST America/El_Salvador -64800
# CST America/Guatemala -64800
# CST America/Managua -64800
# CST America/Regina -64800
# CST America/Swift_Current -64800
# CST America/Tegucigalpa -64800
# CST Asia/Macau 28800
# CST Asia/Shanghai 28800
# CST Asia/Taipei 28800
if tz_name == 'CST':
tzdata[tz_name] = -64800 # America/All
return tzdata
# Note: some of these abbreviations are fundamentally ambiguous (see
# zerver/tests/test_timezone.py), so you should never rely on them as
# anything more than a heuristic.
common_timezones = {
"SST": -39600,
"HST": -36000,
"AKST": -32400,
"HDT": -32400,
"AKDT": -28800,
"PST": -28800,
"MST": -25200,
"PDT": -25200,
"CST": -21600,
"MDT": -21600,
"CDT": -18000,
"EST": -18000,
"AST": -14400,
"EDT": -14400,
"NST": -12600,
"ADT": -10800,
"NDT": -9000,
"GMT": 0,
"UTC": 0,
"WET": 0,
"BST": +3600,
"CET": +3600,
"MET": +3600,
"WAT": +3600,
"WEST": +3600,
"CAT": +7200,
"CEST": +7200,
"EET": +7200,
"MEST": +7200,
"SAST": +7200,
"EAT": +10800,
"EEST": +10800,
"IDT": +10800,
"MSK": +10800,
"PKT": +18000,
"IST": +19800,
"WIB": +25200,
"AWST": +28800,
"HKT": +28800,
"WITA": +28800,
"JST": +32400,
"KST": +32400,
"WIT": +32400,
"ACST": +34200,
"AEST": +36000,
"ChST": +36000,
"ACDT": +37800,
"AEDT": +39600,
"NZST": +43200,
"NZDT": +46800,
}