diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -793,7 +793,7 @@ are ignored. -.. classmethod:: datetime.strptime(date_string, format) +.. classmethod:: datetime.strptime(date_string, format, *, timezones=None) Return a :class:`.datetime` corresponding to *date_string*, parsed according to *format*. This is equivalent to ``datetime(*(time.strptime(date_string, @@ -802,7 +802,8 @@ time tuple. For a complete list of formatting directives, see :ref:`strftime-strptime-behavior`. - +.. versionchanged:: 3.5 + %Z directive can use *timezones* keyword-only parameter. Class attributes: @@ -1861,7 +1862,7 @@ | | or -HHMM (empty string if the | +1030 | | | | the object is naive). | | | +-----------+--------------------------------+------------------------+-------+ -| ``%Z`` | Time zone name (empty string | (empty), UTC, EST, CST | | +| ``%Z`` | Time zone name (empty string | (empty), UTC, EST, CST | \(6) | | | if the object is naive). | | | +-----------+--------------------------------+------------------------+-------+ | ``%j`` | Day of the year as a | 001, 002, ..., 366 | | @@ -1960,6 +1961,26 @@ aware :class:`.datetime` object will be produced. The ``tzinfo`` of the result will be set to a :class:`timezone` instance. + .. versionchanged:: 3.5 + + When the ``%Z`` directive is provided to the :meth:`strptime` method and + the *timezones* parameter is true, an aware :class:`.datetime` object will + be produced in UTC, local or any timezone in the *timezones* mapping e.g., + ``timezones={'CST': timedelta(hours=-5)}``. The ``tzinfo`` of the result + will be set to a :class:`timezone` instance if *timezones* doesn't provide + a different object. + + Default *timezones* is ``None`` that means ``timezones=False`` for backward + compatibility. It will be ``True`` in Python 3.6. Pass ``timezones=False`` + to preserve the old behaviour: return a naive :class:`.datetime` object + without validating the given time zone abbreviation/name if it is UTC, GMT, + any of ``time.tzname`` (current local timezone), or (since Python 3.5) + belongs to the list of recognized timezone names + ``_strptime._get_timezone_abbreviations()`` + +.. XXX find public name for _strptime._get_timezone_abbreviations() -- ideally + it would be tzdata.get_timezone_abbreviations() + (7) When used with the :meth:`strptime` method, ``%U`` and ``%W`` are only used in calculations when the day of the week and the year are specified. diff --git a/Lib/_strptime.py b/Lib/_strptime.py --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -19,6 +19,7 @@ from datetime import (date as datetime_date, timedelta as datetime_timedelta, timezone as datetime_timezone) +from warnings import warn try: from _thread import allocate_lock as _thread_allocate_lock except ImportError: @@ -30,6 +31,2587 @@ # Figure out what the current language is set to. return locale.getlocale(locale.LC_TIME) +def _get_timezone_abbreviations(): + """Return a list of all tznames (%Z) known to the tz database.""" + # Data is generated using pytz 2014.7. The tz data is in public + # domain, some of its code - BSD, pytz itself - MIT license. + #Akira: I don't think we need to update the list -- if the issue + # is important then an ability to access tzdata should be + # added to stdlib instead + + # Officially, nobody keeps track of all possible timezone abbreviations + # + # - timezone abbreviations are not unique (~1/3 corresponds to + # multiple UTC offsets) e.g., CST may be used in North America, + # China, Australia (all different timezones with different UTC + # offsets). + # + # - in reverse, the same timezone may use different timezone + # abbreviations (~2/3) e.g., CST, CDT (due to DST) and even at the + # same time: HAST (Hawaii-Aleutian Standard Time), HST + # (Pacific/Honolulu)) + # + # - the same timezone abbreviation may refer to different UTC + # offsets even in the same timezone e.g., uses HST -10:00 and + # -10:30 at different times. + # + # Even if the tz database is available: + # + # - It doesn't know the future -- there are several updates per year + # - It won't tell us that HAST is also acceptable instead of HST for Hawaii + # + + # There is a small difference with data from wikipedia.org, + # timetemperature.com, timeanddate.com. Most notably, no + # single-letter military time zone codes such as A - UTC+1, B - + # UTC+2, Z - UTC+0, N - UTC-1, O - UTC-2, etc + """# Generate the tzname list for _strptime._get_timezone_abbreviations() + from collections import defaultdict + from datetime import datetime, timedelta, timezone + import pytz # $ pip install pytz + + def format_offset(offset): + # format utc offset as +HHMM, -HHMM + hours, minutes = divmod(offset, timedelta(minutes=60)) + return '{:+03d}{:02d}'.format(hours, minutes // timedelta(minutes=1)) + + print("pytz version:", pytz.__version__) + # use now for zones without transitions + utc_dt = datetime.now(timezone.utc) + # tzname -> (dst, utc_offset) -> {zonename} + tznames = defaultdict(lambda: defaultdict(set)) + for zonename in pytz.all_timezones: + tz = pytz.timezone(zonename) + tzinfos = getattr(tz, '_transition_info', None) + if not tzinfos: # no transition info available + dt = utc_dt.astimezone(tz) + z = tznames[dt.tzname()] + z[int(bool(dt.dst())), dt.utcoffset()].add(zonename) + else: + for off, dst, name in tzinfos: + tznames[name][int(bool(dst)), off].add(zonename) + + for zname, info in sorted(tznames.items()): + zname_repr = repr(zname) + print(zname_repr, ",", sep='', end=' ') + width = len(zname_repr) + for i, ((dst, offset), zonenames) in enumerate(sorted(info.items())): + if i != 0: + print(end=' '*(width + 2)) + print("# %d | %s | " % (dst, format_offset(offset)), end=' ') + for j, zone in enumerate(sorted(zonenames)): + if j == 0: + print(zone) + else: + print(" "*(width + 2) + "#" + " "*14 + zone) + """ + # 'tzname', # is_dst | utc offset(s) | zonename(s) + return [ + 'ACDT', # 1 | +1030 | Australia/Adelaide + # Australia/Broken_Hill + # Australia/Darwin + # Australia/North + # Australia/South + # Australia/Yancowinna + 'ACST', # 0 | +0900 | Australia/Adelaide + # Australia/Broken_Hill + # Australia/Darwin + # Australia/North + # Australia/South + # Australia/Yancowinna + # 0 | +0930 | Asia/Jayapura + # Australia/Adelaide + # Australia/Broken_Hill + # Australia/Darwin + # Australia/North + # Australia/South + # Australia/Yancowinna + # 1 | -0400 | America/Eirunepe + # America/Porto_Acre + # America/Rio_Branco + # Brazil/Acre + 'ACT', # 0 | -0500 | America/Eirunepe + # America/Porto_Acre + # America/Rio_Branco + # Brazil/Acre + 'ACWDT', # 1 | +0945 | Australia/Eucla + 'ACWST', # 0 | +0845 | Australia/Eucla + 'ADDT', # 1 | -0200 | America/Goose_Bay + # America/Pangnirtung + 'ADMT', # 0 | +0235 | Africa/Addis_Ababa + # Africa/Asmara + # Africa/Asmera + 'ADT', # 1 | -0300 | America/Barbados + # America/Blanc-Sablon + # America/Glace_Bay + # America/Goose_Bay + # America/Halifax + # America/Martinique + # America/Moncton + # America/Pangnirtung + # America/Thule + # Atlantic/Bermuda + # Canada/Atlantic + # 1 | +0400 | Asia/Baghdad + 'AEDT', # 1 | +1100 | Antarctica/Macquarie + # Australia/ACT + # Australia/Brisbane + # Australia/Canberra + # Australia/Currie + # Australia/Hobart + # Australia/Lindeman + # Australia/Melbourne + # Australia/NSW + # Australia/Queensland + # Australia/Sydney + # Australia/Tasmania + # Australia/Victoria + 'AEST', # 0 | +1000 | Antarctica/Macquarie + # Australia/ACT + # Australia/Brisbane + # Australia/Canberra + # Australia/Currie + # Australia/Hobart + # Australia/LHI + # Australia/Lindeman + # Australia/Lord_Howe + # Australia/Melbourne + # Australia/NSW + # Australia/Queensland + # Australia/Sydney + # Australia/Tasmania + # Australia/Victoria + 'AFT', # 0 | +0400 | Asia/Kabul + # 0 | +0430 | Asia/Kabul + 'AHDT', # 1 | -0900 | America/Anchorage + # US/Alaska + 'AHST', # 0 | -1000 | America/Adak + # America/Anchorage + # America/Atka + # US/Alaska + # US/Aleutian + 'AKDT', # 1 | -0800 | America/Anchorage + # America/Juneau + # America/Nome + # America/Sitka + # America/Yakutat + # US/Alaska + 'AKST', # 0 | -0900 | America/Anchorage + # America/Juneau + # America/Nome + # America/Sitka + # America/Yakutat + # US/Alaska + 'AKTST', # 1 | +0600 | Asia/Aqtobe + 'AKTT', # 0 | +0400 | Asia/Aqtobe + # 0 | +0500 | Asia/Aqtobe + # 0 | +0600 | Asia/Aqtobe + 'ALMST', # 1 | +0700 | Asia/Almaty + 'ALMT', # 0 | +0500 | Asia/Almaty + # 0 | +0600 | Asia/Almaty + 'AMST', # 1 | -0300 | America/Boa_Vista + # America/Campo_Grande + # America/Cuiaba + # America/Manaus + # America/Porto_Velho + # America/Santarem + # Brazil/West + # 1 | +0400 | Asia/Yerevan + # 1 | +0500 | Asia/Yerevan + 'AMT', # 0 | -0400 | America/Boa_Vista + # America/Campo_Grande + # America/Cuiaba + # America/Eirunepe + # America/Manaus + # America/Porto_Acre + # America/Porto_Velho + # America/Rio_Branco + # America/Santarem + # Brazil/Acre + # Brazil/West + # 0 | -0409 | America/Asuncion + # 0 | +0020 | Europe/Amsterdam + # 0 | +0135 | Europe/Athens + # 0 | +0236 | Africa/Asmara + # Africa/Asmera + # 0 | +0300 | Asia/Yerevan + # 0 | +0400 | Asia/Yerevan + 'ANAST', # 1 | +1200 | Asia/Anadyr + # 1 | +1300 | Asia/Anadyr + # 1 | +1400 | Asia/Anadyr + 'ANAT', # 0 | +1100 | Asia/Anadyr + # 0 | +1200 | Asia/Anadyr + # 0 | +1300 | Asia/Anadyr + 'ANT', # 0 | -0530 | America/Aruba + # America/Curacao + # America/Kralendijk + # America/Lower_Princes + 'APT', # 1 | -0300 | America/Blanc-Sablon + # America/Glace_Bay + # America/Halifax + # America/Moncton + # America/Pangnirtung + # America/Puerto_Rico + # Canada/Atlantic + 'AQTST', # 1 | +0500 | Asia/Aqtau + # 1 | +0600 | Asia/Aqtau + # Asia/Aqtobe + 'AQTT', # 0 | +0400 | Asia/Aqtau + # 0 | +0500 | Asia/Aqtau + # Asia/Aqtobe + 'ARST', # 0 | -0300 | America/Argentina/Buenos_Aires + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/Salta + # America/Buenos_Aires + # America/Cordoba + # America/Jujuy + # America/Rosario + # 1 | -0300 | America/Argentina/Buenos_Aires + # America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/La_Rioja + # America/Argentina/Mendoza + # America/Argentina/Rio_Gallegos + # America/Argentina/Salta + # America/Argentina/San_Juan + # America/Argentina/San_Luis + # America/Argentina/Tucuman + # America/Argentina/Ushuaia + # America/Buenos_Aires + # America/Catamarca + # America/Cordoba + # America/Jujuy + # America/Mendoza + # America/Rosario + # Antarctica/Palmer + # 1 | -0200 | America/Argentina/Buenos_Aires + # America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/La_Rioja + # America/Argentina/Mendoza + # America/Argentina/Rio_Gallegos + # America/Argentina/Salta + # America/Argentina/San_Juan + # America/Argentina/San_Luis + # America/Argentina/Tucuman + # America/Argentina/Ushuaia + # America/Buenos_Aires + # America/Catamarca + # America/Cordoba + # America/Jujuy + # America/Mendoza + # America/Rosario + # Antarctica/Palmer + 'ART', # 0 | -0400 | America/Argentina/Buenos_Aires + # America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/La_Rioja + # America/Argentina/Mendoza + # America/Argentina/Rio_Gallegos + # America/Argentina/Salta + # America/Argentina/San_Juan + # America/Argentina/San_Luis + # America/Argentina/Tucuman + # America/Argentina/Ushuaia + # America/Buenos_Aires + # America/Catamarca + # America/Cordoba + # America/Jujuy + # America/Mendoza + # America/Rosario + # Antarctica/Palmer + # 0 | -0300 | America/Argentina/Buenos_Aires + # America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/La_Rioja + # America/Argentina/Mendoza + # America/Argentina/Rio_Gallegos + # America/Argentina/Salta + # America/Argentina/San_Juan + # America/Argentina/San_Luis + # America/Argentina/Tucuman + # America/Argentina/Ushuaia + # America/Buenos_Aires + # America/Catamarca + # America/Cordoba + # America/Jujuy + # America/Mendoza + # America/Rosario + # Antarctica/Palmer + 'ASHST', # 1 | +0500 | Asia/Ashgabat + # Asia/Ashkhabad + # 1 | +0600 | Asia/Ashgabat + # Asia/Ashkhabad + 'ASHT', # 0 | +0400 | Asia/Ashgabat + # Asia/Ashkhabad + # 0 | +0500 | Asia/Ashgabat + # Asia/Ashkhabad + 'AST', # 0 | -0400 | America/Anguilla + # America/Antigua + # America/Aruba + # America/Barbados + # America/Blanc-Sablon + # America/Curacao + # America/Dominica + # America/Glace_Bay + # America/Goose_Bay + # America/Grand_Turk + # America/Grenada + # America/Guadeloupe + # America/Halifax + # America/Kralendijk + # America/Lower_Princes + # America/Marigot + # America/Martinique + # America/Miquelon + # America/Moncton + # America/Montserrat + # America/Pangnirtung + # America/Port_of_Spain + # America/Puerto_Rico + # America/Santo_Domingo + # America/St_Barthelemy + # America/St_Kitts + # America/St_Lucia + # America/St_Thomas + # America/St_Vincent + # America/Thule + # America/Tortola + # America/Virgin + # Atlantic/Bermuda + # Canada/Atlantic + # 0 | +0300 | Asia/Aden + # Asia/Baghdad + # Asia/Bahrain + # Asia/Kuwait + # Asia/Qatar + # Asia/Riyadh + 'AWDT', # 1 | +0900 | Australia/Perth + # Australia/West + 'AWST', # 0 | +0800 | Antarctica/Casey + # Australia/Perth + # Australia/West + 'AWT', # 1 | -0300 | America/Blanc-Sablon + # America/Glace_Bay + # America/Halifax + # America/Moncton + # America/Pangnirtung + # America/Puerto_Rico + # Canada/Atlantic + 'AZOMT', # 1 | +0000 | Atlantic/Azores + 'AZOST', # 1 | -0100 | Atlantic/Azores + # 1 | +0000 | Atlantic/Azores + 'AZOT', # 0 | -0200 | Atlantic/Azores + # 0 | -0100 | Atlantic/Azores + 'AZST', # 1 | +0400 | Asia/Baku + # 1 | +0500 | Asia/Baku + 'AZT', # 0 | +0300 | Asia/Baku + # 0 | +0400 | Asia/Baku + 'BAKST', # 1 | +0400 | Asia/Baku + # 1 | +0500 | Asia/Baku + 'BAKT', # 0 | +0300 | Asia/Baku + # 0 | +0400 | Asia/Baku + 'BDST', # 1 | +0200 | Europe/Belfast + # Europe/Gibraltar + # Europe/Guernsey + # Europe/Isle_of_Man + # Europe/Jersey + # Europe/London + # GB + # GB-Eire + # 1 | +0700 | Asia/Dacca + # Asia/Dhaka + 'BDT', # 0 | +0600 | Asia/Dacca + # Asia/Dhaka + # 1 | -1000 | America/Adak + # America/Atka + # America/Nome + # US/Aleutian + 'BEAT', # 0 | +0230 | Africa/Kampala + # Africa/Mogadishu + # Africa/Nairobi + 'BEAUT', # 0 | +0245 | Africa/Dar_es_Salaam + # Africa/Kampala + # Africa/Nairobi + 'BMT', # 0 | -0504 | America/Bogota + # 0 | -0402 | America/Barbados + # 0 | +0018 | Europe/Brussels + # 0 | +0030 | Europe/Busingen + # Europe/Vaduz + # Europe/Zurich + # 0 | +0144 | Europe/Bucharest + # Europe/Chisinau + # Europe/Tiraspol + # 0 | +0258 | Asia/Baghdad + # 0 | +0642 | Asia/Bangkok + # 0 | +0707 | Asia/Jakarta + 'BNT', # 0 | +0730 | Asia/Brunei + # 0 | +0800 | Asia/Brunei + 'BORT', # 0 | +0730 | Asia/Kuching + # 0 | +0800 | Asia/Kuching + 'BORTST', # 1 | +0820 | Asia/Kuching + 'BOST', # 1 | -0427 | America/La_Paz + 'BOT', # 0 | -0400 | America/La_Paz + 'BRST', # 1 | -0200 | America/Araguaina + # America/Bahia + # America/Belem + # America/Fortaleza + # America/Maceio + # America/Recife + # America/Sao_Paulo + # Brazil/East + 'BRT', # 0 | -0300 | America/Araguaina + # America/Bahia + # America/Belem + # America/Fortaleza + # America/Maceio + # America/Recife + # America/Santarem + # America/Sao_Paulo + # Brazil/East + 'BST', # 0 | -1100 | America/Adak + # America/Atka + # America/Nome + # Pacific/Midway + # Pacific/Pago_Pago + # Pacific/Samoa + # US/Aleutian + # US/Samoa + # 0 | +0100 | Europe/Belfast + # Europe/Guernsey + # Europe/Isle_of_Man + # Europe/Jersey + # Europe/London + # GB + # GB-Eire + # 1 | +0100 | Eire + # Europe/Belfast + # Europe/Dublin + # Europe/Gibraltar + # Europe/Guernsey + # Europe/Isle_of_Man + # Europe/Jersey + # Europe/London + # GB + # GB-Eire + 'BTT', # 0 | +0600 | Asia/Thimbu + # Asia/Thimphu + 'BURT', # 0 | +0630 | Asia/Calcutta + # Asia/Dacca + # Asia/Dhaka + # Asia/Kolkata + # Asia/Rangoon + 'CANT', # 0 | -0100 | Atlantic/Canary + 'CAPT', # 1 | -0900 | America/Anchorage + # US/Alaska + 'CAST', # 0 | +1100 | Antarctica/Casey + # 1 | +0300 | Africa/Gaborone + # Africa/Juba + # Africa/Khartoum + 'CAT', # 0 | -1000 | America/Anchorage + # US/Alaska + # 0 | +0200 | Africa/Blantyre + # Africa/Bujumbura + # Africa/Gaborone + # Africa/Harare + # Africa/Juba + # Africa/Khartoum + # Africa/Kigali + # Africa/Lubumbashi + # Africa/Lusaka + # Africa/Maputo + # Africa/Windhoek + 'CAWT', # 1 | -0900 | America/Anchorage + # US/Alaska + 'CCT', # 0 | +0630 | Indian/Cocos + 'CDDT', # 1 | -0400 | America/Rankin_Inlet + # America/Resolute + 'CDT', # 0 | -0500 | America/Indiana/Marengo + # America/Kentucky/Louisville + # America/Louisville + # 1 | -0500 | America/Atikokan + # America/Bahia_Banderas + # America/Belize + # America/Cambridge_Bay + # America/Cancun + # America/Chicago + # America/Chihuahua + # America/Coral_Harbour + # America/Costa_Rica + # America/El_Salvador + # America/Fort_Wayne + # America/Guatemala + # America/Indiana/Indianapolis + # America/Indiana/Knox + # America/Indiana/Marengo + # America/Indiana/Petersburg + # America/Indiana/Tell_City + # America/Indiana/Vevay + # America/Indiana/Vincennes + # America/Indiana/Winamac + # America/Indianapolis + # America/Iqaluit + # America/Kentucky/Louisville + # America/Kentucky/Monticello + # America/Knox_IN + # America/Louisville + # America/Managua + # America/Matamoros + # America/Menominee + # America/Merida + # America/Mexico_City + # America/Monterrey + # America/North_Dakota/Beulah + # America/North_Dakota/Center + # America/North_Dakota/New_Salem + # America/Ojinaga + # America/Pangnirtung + # America/Rainy_River + # America/Rankin_Inlet + # America/Resolute + # America/Tegucigalpa + # America/Winnipeg + # CST6CDT + # Canada/Central + # Mexico/General + # US/Central + # US/East-Indiana + # US/Indiana-Starke + # 1 | -0400 | America/Havana + # Cuba + # 1 | +0900 | Asia/Chongqing + # Asia/Chungking + # Asia/Harbin + # Asia/Shanghai + # Asia/Taipei + # PRC + # ROC + 'CEMT', # 1 | +0300 | Europe/Berlin + 'CEST', # 1 | +0200 | Africa/Algiers + # Africa/Ceuta + # Africa/Tripoli + # Africa/Tunis + # Antarctica/Troll + # Arctic/Longyearbyen + # Atlantic/Jan_Mayen + # CET + # Europe/Amsterdam + # Europe/Andorra + # Europe/Athens + # Europe/Belgrade + # Europe/Berlin + # Europe/Bratislava + # Europe/Brussels + # Europe/Budapest + # Europe/Busingen + # Europe/Chisinau + # Europe/Copenhagen + # Europe/Gibraltar + # Europe/Kaliningrad + # Europe/Kiev + # Europe/Lisbon + # Europe/Ljubljana + # Europe/Luxembourg + # Europe/Madrid + # Europe/Malta + # Europe/Minsk + # Europe/Monaco + # Europe/Oslo + # Europe/Paris + # Europe/Podgorica + # Europe/Prague + # Europe/Riga + # Europe/Rome + # Europe/San_Marino + # Europe/Sarajevo + # Europe/Simferopol + # Europe/Skopje + # Europe/Sofia + # Europe/Stockholm + # Europe/Tallinn + # Europe/Tirane + # Europe/Tiraspol + # Europe/Uzhgorod + # Europe/Vaduz + # Europe/Vatican + # Europe/Vienna + # Europe/Vilnius + # Europe/Warsaw + # Europe/Zagreb + # Europe/Zaporozhye + # Europe/Zurich + # Libya + # Poland + # Portugal + # 1 | +0300 | Europe/Kaliningrad + 'CET', # 0 | +0100 | Africa/Algiers + # Africa/Casablanca + # Africa/Ceuta + # Africa/Tripoli + # Africa/Tunis + # Arctic/Longyearbyen + # Atlantic/Jan_Mayen + # CET + # Europe/Amsterdam + # Europe/Andorra + # Europe/Athens + # Europe/Belgrade + # Europe/Berlin + # Europe/Bratislava + # Europe/Brussels + # Europe/Budapest + # Europe/Busingen + # Europe/Chisinau + # Europe/Copenhagen + # Europe/Gibraltar + # Europe/Kaliningrad + # Europe/Kiev + # Europe/Lisbon + # Europe/Ljubljana + # Europe/Luxembourg + # Europe/Madrid + # Europe/Malta + # Europe/Minsk + # Europe/Monaco + # Europe/Oslo + # Europe/Paris + # Europe/Podgorica + # Europe/Prague + # Europe/Riga + # Europe/Rome + # Europe/San_Marino + # Europe/Sarajevo + # Europe/Simferopol + # Europe/Skopje + # Europe/Sofia + # Europe/Stockholm + # Europe/Tallinn + # Europe/Tirane + # Europe/Tiraspol + # Europe/Uzhgorod + # Europe/Vaduz + # Europe/Vatican + # Europe/Vienna + # Europe/Vilnius + # Europe/Warsaw + # Europe/Zagreb + # Europe/Zaporozhye + # Europe/Zurich + # Libya + # Poland + # Portugal + # 0 | +0200 | Europe/Kaliningrad + 'CGST', # 1 | -0100 | America/Scoresbysund + 'CGT', # 0 | -0200 | America/Scoresbysund + 'CHADT', # 1 | +1345 | NZ-CHAT + # Pacific/Chatham + 'CHAST', # 0 | +1215 | NZ-CHAT + # Pacific/Chatham + # 0 | +1245 | NZ-CHAT + # Pacific/Chatham + 'CHDT', # 1 | -0630 | America/Belize + 'CHOST', # 1 | +1000 | Asia/Choibalsan + 'CHOT', # 0 | +0800 | Asia/Choibalsan + # 0 | +0900 | Asia/Choibalsan + 'CHUT', # 0 | +1000 | Pacific/Chuuk + # Pacific/Truk + # Pacific/Yap + 'CKHST', # 1 | -1030 | Pacific/Rarotonga + 'CKT', # 0 | -1130 | Pacific/Rarotonga + # 0 | -1000 | Pacific/Rarotonga + 'CLST', # 1 | -0400 | America/Santiago + # Chile/Continental + # 1 | -0300 | America/Santiago + # Antarctica/Palmer + # Chile/Continental + 'CLT', # 0 | -0500 | America/Santiago + # Chile/Continental + # 0 | -0400 | America/Santiago + # Antarctica/Palmer + # Chile/Continental + 'CMT', # 0 | -0640 | America/Panama + # 0 | -0527 | America/La_Paz + # 0 | -0532 | America/Caracas + # 0 | -0543 | America/Argentina/Buenos_Aires + # America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/La_Rioja + # America/Argentina/Mendoza + # America/Argentina/Rio_Gallegos + # America/Argentina/Salta + # America/Argentina/San_Juan + # America/Argentina/San_Luis + # America/Argentina/Tucuman + # America/Argentina/Ushuaia + # America/Buenos_Aires + # America/Catamarca + # America/Cordoba + # America/Jujuy + # America/Mendoza + # America/Rosario + # 0 | +0050 | Europe/Copenhagen + # 0 | +0155 | Europe/Chisinau + # Europe/Tiraspol + 'COST', # 1 | -0400 | America/Bogota + 'COT', # 0 | -0500 | America/Bogota + 'CPT', # 1 | -0500 | America/Atikokan + # America/Chicago + # America/Coral_Harbour + # America/Fort_Wayne + # America/Indiana/Indianapolis + # America/Indiana/Knox + # America/Indiana/Marengo + # America/Indiana/Petersburg + # America/Indiana/Tell_City + # America/Indiana/Vevay + # America/Indiana/Vincennes + # America/Indiana/Winamac + # America/Indianapolis + # America/Kentucky/Louisville + # America/Kentucky/Monticello + # America/Knox_IN + # America/Louisville + # America/Menominee + # America/Rainy_River + # America/Winnipeg + # CST6CDT + # Canada/Central + # US/Central + # US/East-Indiana + # US/Indiana-Starke + 'CST', # 0 | -0600 | America/Atikokan + # America/Bahia_Banderas + # America/Belize + # America/Cambridge_Bay + # America/Cancun + # America/Chicago + # America/Chihuahua + # America/Coral_Harbour + # America/Costa_Rica + # America/Detroit + # America/El_Salvador + # America/Fort_Wayne + # America/Guatemala + # America/Hermosillo + # America/Indiana/Indianapolis + # America/Indiana/Knox + # America/Indiana/Marengo + # America/Indiana/Petersburg + # America/Indiana/Tell_City + # America/Indiana/Vevay + # America/Indiana/Vincennes + # America/Indiana/Winamac + # America/Indianapolis + # America/Iqaluit + # America/Kentucky/Louisville + # America/Kentucky/Monticello + # America/Knox_IN + # America/Louisville + # America/Managua + # America/Matamoros + # America/Mazatlan + # America/Menominee + # America/Merida + # America/Mexico_City + # America/Monterrey + # America/North_Dakota/Beulah + # America/North_Dakota/Center + # America/North_Dakota/New_Salem + # America/Ojinaga + # America/Pangnirtung + # America/Rainy_River + # America/Rankin_Inlet + # America/Regina + # America/Resolute + # America/Swift_Current + # America/Tegucigalpa + # America/Thunder_Bay + # America/Winnipeg + # CST6CDT + # Canada/Central + # Canada/East-Saskatchewan + # Canada/Saskatchewan + # Mexico/BajaSur + # Mexico/General + # US/Central + # US/East-Indiana + # US/Indiana-Starke + # US/Michigan + # 0 | -0500 | America/Havana + # Cuba + # 0 | +0800 | Asia/Chongqing + # Asia/Chungking + # Asia/Harbin + # Asia/Macao + # Asia/Macau + # Asia/Shanghai + # Asia/Taipei + # PRC + # ROC + 'CUT', # 0 | +0220 | Europe/Zaporozhye + 'CVST', # 1 | -0100 | Atlantic/Cape_Verde + 'CVT', # 0 | -0200 | Atlantic/Cape_Verde + # 0 | -0100 | Atlantic/Cape_Verde + 'CWT', # 1 | -0500 | America/Atikokan + # America/Chicago + # America/Coral_Harbour + # America/Fort_Wayne + # America/Indiana/Indianapolis + # America/Indiana/Knox + # America/Indiana/Marengo + # America/Indiana/Petersburg + # America/Indiana/Tell_City + # America/Indiana/Vevay + # America/Indiana/Vincennes + # America/Indiana/Winamac + # America/Indianapolis + # America/Kentucky/Louisville + # America/Kentucky/Monticello + # America/Knox_IN + # America/Louisville + # America/Menominee + # America/Mexico_City + # America/Rainy_River + # America/Winnipeg + # CST6CDT + # Canada/Central + # Mexico/General + # US/Central + # US/East-Indiana + # US/Indiana-Starke + 'CXT', # 0 | +0700 | Indian/Christmas + 'ChST', # 0 | +1000 | Pacific/Guam + # Pacific/Saipan + 'DACT', # 0 | +0600 | Asia/Dacca + # Asia/Dhaka + 'DAVT', # 0 | +0500 | Antarctica/Davis + # 0 | +0700 | Antarctica/Davis + 'DDUT', # 0 | +1000 | Antarctica/DumontDUrville + 'DMT', # 0 | -0135 | Eire + # Europe/Dublin + 'DUSST', # 1 | +0600 | Asia/Dushanbe + # 1 | +0700 | Asia/Dushanbe + 'DUST', # 0 | +0500 | Asia/Dushanbe + # 0 | +0600 | Asia/Dushanbe + 'EASST', # 1 | -0600 | Chile/EasterIsland + # Pacific/Easter + # 1 | -0500 | Chile/EasterIsland + # Pacific/Easter + 'EAST', # 0 | -0700 | Chile/EasterIsland + # Pacific/Easter + # 0 | -0600 | Chile/EasterIsland + # Pacific/Easter + # 1 | +0400 | Indian/Antananarivo + 'EAT', # 0 | +0300 | Africa/Addis_Ababa + # Africa/Asmara + # Africa/Asmera + # Africa/Dar_es_Salaam + # Africa/Djibouti + # Africa/Juba + # Africa/Kampala + # Africa/Khartoum + # Africa/Mogadishu + # Africa/Nairobi + # Indian/Antananarivo + # Indian/Comoro + # Indian/Mayotte + 'ECT', # 0 | -0500 | America/Guayaquil + # Pacific/Galapagos + 'EDDT', # 1 | -0300 | America/Iqaluit + 'EDT', # 1 | -0400 | America/Cancun + # America/Detroit + # America/Fort_Wayne + # America/Grand_Turk + # America/Indiana/Indianapolis + # America/Indiana/Marengo + # America/Indiana/Petersburg + # America/Indiana/Tell_City + # America/Indiana/Vevay + # America/Indiana/Vincennes + # America/Indiana/Winamac + # America/Indianapolis + # America/Iqaluit + # America/Jamaica + # America/Kentucky/Louisville + # America/Kentucky/Monticello + # America/Louisville + # America/Montreal + # America/Nassau + # America/New_York + # America/Nipigon + # America/Pangnirtung + # America/Port-au-Prince + # America/Santo_Domingo + # America/Thunder_Bay + # America/Toronto + # Canada/Eastern + # EST5EDT + # Jamaica + # US/East-Indiana + # US/Eastern + # US/Michigan + 'EEST', # 1 | +0300 | Africa/Cairo + # Asia/Amman + # Asia/Beirut + # Asia/Damascus + # Asia/Gaza + # Asia/Hebron + # Asia/Istanbul + # Asia/Nicosia + # EET + # Egypt + # Europe/Athens + # Europe/Bucharest + # Europe/Chisinau + # Europe/Helsinki + # Europe/Istanbul + # Europe/Kaliningrad + # Europe/Kiev + # Europe/Mariehamn + # Europe/Minsk + # Europe/Moscow + # Europe/Nicosia + # Europe/Riga + # Europe/Samara + # Europe/Simferopol + # Europe/Sofia + # Europe/Tallinn + # Europe/Tiraspol + # Europe/Uzhgorod + # Europe/Vilnius + # Europe/Warsaw + # Europe/Zaporozhye + # Poland + # Turkey + # W-SU + 'EET', # 0 | +0200 | Africa/Cairo + # Africa/Tripoli + # Asia/Amman + # Asia/Beirut + # Asia/Damascus + # Asia/Gaza + # Asia/Hebron + # Asia/Istanbul + # Asia/Nicosia + # EET + # Egypt + # Europe/Athens + # Europe/Bucharest + # Europe/Chisinau + # Europe/Helsinki + # Europe/Istanbul + # Europe/Kaliningrad + # Europe/Kiev + # Europe/Mariehamn + # Europe/Minsk + # Europe/Moscow + # Europe/Nicosia + # Europe/Riga + # Europe/Simferopol + # Europe/Sofia + # Europe/Tallinn + # Europe/Tiraspol + # Europe/Uzhgorod + # Europe/Vilnius + # Europe/Warsaw + # Europe/Zaporozhye + # Libya + # Poland + # Turkey + # W-SU + # 1 | +0300 | Asia/Gaza + # Asia/Hebron + 'EGST', # 1 | +0000 | America/Scoresbysund + 'EGT', # 0 | -0100 | America/Scoresbysund + 'EHDT', # 1 | -0530 | America/Santo_Domingo + 'EMT', # 0 | -0843 | Chile/EasterIsland + # Pacific/Easter + 'EPT', # 1 | -0400 | America/Detroit + # America/Iqaluit + # America/Montreal + # America/New_York + # America/Nipigon + # America/Thunder_Bay + # America/Toronto + # Canada/Eastern + # EST5EDT + # US/Eastern + # US/Michigan + 'EST', # 0 | -0500 | America/Antigua + # America/Atikokan + # America/Cambridge_Bay + # America/Cancun + # America/Cayman + # America/Chicago + # America/Coral_Harbour + # America/Detroit + # America/Fort_Wayne + # America/Grand_Turk + # America/Indiana/Indianapolis + # America/Indiana/Knox + # America/Indiana/Marengo + # America/Indiana/Petersburg + # America/Indiana/Tell_City + # America/Indiana/Vevay + # America/Indiana/Vincennes + # America/Indiana/Winamac + # America/Indianapolis + # America/Iqaluit + # America/Jamaica + # America/Kentucky/Louisville + # America/Kentucky/Monticello + # America/Knox_IN + # America/Louisville + # America/Managua + # America/Menominee + # America/Merida + # America/Moncton + # America/Montreal + # America/Nassau + # America/New_York + # America/Nipigon + # America/Panama + # America/Pangnirtung + # America/Port-au-Prince + # America/Rankin_Inlet + # America/Resolute + # America/Santo_Domingo + # America/Thunder_Bay + # America/Toronto + # Canada/Eastern + # EST + # EST5EDT + # Jamaica + # US/Central + # US/East-Indiana + # US/Eastern + # US/Indiana-Starke + # US/Michigan + 'EWT', # 1 | -0400 | America/Detroit + # America/Iqaluit + # America/Montreal + # America/New_York + # America/Nipigon + # America/Thunder_Bay + # America/Toronto + # Canada/Eastern + # EST5EDT + # US/Eastern + # US/Michigan + 'FET', # 0 | +0300 | Europe/Kaliningrad + # Europe/Minsk + 'FFMT', # 0 | -0556 | America/Martinique + 'FJST', # 1 | +1300 | Pacific/Fiji + 'FJT', # 0 | +1200 | Pacific/Fiji + 'FKST', # 0 | -0300 | Atlantic/Stanley + # 1 | -0300 | Atlantic/Stanley + # 1 | -0200 | Atlantic/Stanley + 'FKT', # 0 | -0400 | Atlantic/Stanley + # 0 | -0300 | Atlantic/Stanley + 'FMT', # 0 | -0252 | Atlantic/Madeira + 'FNST', # 1 | -0100 | America/Noronha + # Brazil/DeNoronha + 'FNT', # 0 | -0200 | America/Noronha + # Brazil/DeNoronha + 'FORT', # 0 | +0400 | Asia/Aqtau + # 0 | +0500 | Asia/Aqtau + 'FRUST', # 1 | +0600 | Asia/Bishkek + # 1 | +0700 | Asia/Bishkek + 'FRUT', # 0 | +0500 | Asia/Bishkek + # 0 | +0600 | Asia/Bishkek + 'GALT', # 0 | -0600 | Pacific/Galapagos + 'GAMT', # 0 | -0900 | Pacific/Gambier + 'GBGT', # 0 | -0415 | America/Guyana + 'GEST', # 1 | +0400 | Asia/Tbilisi + # 1 | +0500 | Asia/Tbilisi + 'GET', # 0 | +0300 | Asia/Tbilisi + # 0 | +0400 | Asia/Tbilisi + 'GFT', # 0 | -0400 | America/Cayenne + # 0 | -0300 | America/Cayenne + 'GHST', # 1 | +0020 | Africa/Accra + 'GILT', # 0 | +1200 | Pacific/Tarawa + 'GMT', # 0 | +0000 | Africa/Abidjan + # Africa/Accra + # Africa/Bamako + # Africa/Banjul + # Africa/Bissau + # Africa/Conakry + # Africa/Dakar + # Africa/Freetown + # Africa/Lome + # Africa/Monrovia + # Africa/Nouakchott + # Africa/Ouagadougou + # Africa/Sao_Tome + # Africa/Timbuktu + # America/Danmarkshavn + # Atlantic/Reykjavik + # Atlantic/St_Helena + # Eire + # Etc/GMT + # Etc/GMT+0 + # Etc/GMT-0 + # Etc/GMT0 + # Etc/Greenwich + # Europe/Belfast + # Europe/Dublin + # Europe/Gibraltar + # Europe/Guernsey + # Europe/Isle_of_Man + # Europe/Jersey + # Europe/London + # GB + # GB-Eire + # GMT + # GMT+0 + # GMT-0 + # GMT0 + # Greenwich + # Iceland + 'GMT+1', # 0 | -0100 | Etc/GMT+1 + 'GMT+10', # 0 | -1000 | Etc/GMT+10 + 'GMT+11', # 0 | -1100 | Etc/GMT+11 + 'GMT+12', # 0 | -1200 | Etc/GMT+12 + 'GMT+2', # 0 | -0200 | Etc/GMT+2 + 'GMT+3', # 0 | -0300 | Etc/GMT+3 + 'GMT+4', # 0 | -0400 | Etc/GMT+4 + 'GMT+5', # 0 | -0500 | Etc/GMT+5 + 'GMT+6', # 0 | -0600 | Etc/GMT+6 + 'GMT+7', # 0 | -0700 | Etc/GMT+7 + 'GMT+8', # 0 | -0800 | Etc/GMT+8 + 'GMT+9', # 0 | -0900 | Etc/GMT+9 + 'GMT-1', # 0 | +0100 | Etc/GMT-1 + 'GMT-10', # 0 | +1000 | Etc/GMT-10 + 'GMT-11', # 0 | +1100 | Etc/GMT-11 + 'GMT-12', # 0 | +1200 | Etc/GMT-12 + 'GMT-13', # 0 | +1300 | Etc/GMT-13 + 'GMT-14', # 0 | +1400 | Etc/GMT-14 + 'GMT-2', # 0 | +0200 | Etc/GMT-2 + 'GMT-3', # 0 | +0300 | Etc/GMT-3 + 'GMT-4', # 0 | +0400 | Etc/GMT-4 + 'GMT-5', # 0 | +0500 | Etc/GMT-5 + 'GMT-6', # 0 | +0600 | Etc/GMT-6 + 'GMT-7', # 0 | +0700 | Etc/GMT-7 + 'GMT-8', # 0 | +0800 | Etc/GMT-8 + 'GMT-9', # 0 | +0900 | Etc/GMT-9 + 'GST', # 0 | -0200 | Atlantic/South_Georgia + # 0 | +0400 | Asia/Bahrain + # Asia/Dubai + # Asia/Muscat + # Asia/Qatar + # 0 | +1000 | Pacific/Guam + 'GYT', # 0 | -0400 | America/Guyana + # 0 | -0415 | America/Guyana + # 0 | -0300 | America/Guyana + 'HADT', # 1 | -0900 | America/Adak + # America/Atka + # US/Aleutian + 'HAST', # 0 | -1000 | America/Adak + # America/Atka + # US/Aleutian + 'HDT', # 1 | -1030 | Pacific/Honolulu + # Pacific/Johnston + # US/Hawaii + 'HKST', # 1 | +0900 | Asia/Hong_Kong + # Hongkong + 'HKT', # 0 | +0800 | Asia/Hong_Kong + # Hongkong + 'HMT', # 0 | -0630 | America/Havana + # Cuba + # 0 | -0205 | Atlantic/Azores + # 0 | +0140 | Europe/Helsinki + # Europe/Mariehamn + # 0 | +0553 | Asia/Calcutta + # Asia/Dacca + # Asia/Dhaka + # Asia/Kolkata + 'HOVST', # 1 | +0800 | Asia/Hovd + 'HOVT', # 0 | +0600 | Asia/Hovd + # 0 | +0700 | Asia/Hovd + 'HST', # 0 | -1130 | Pacific/Honolulu + # Pacific/Johnston + # US/Hawaii + # 0 | -1000 | HST + # Pacific/Honolulu + # Pacific/Johnston + # US/Hawaii + 'ICT', # 0 | +0700 | Asia/Bangkok + # Asia/Ho_Chi_Minh + # Asia/Phnom_Penh + # Asia/Saigon + # Asia/Vientiane + # 0 | +0800 | Asia/Ho_Chi_Minh + # Asia/Phnom_Penh + # Asia/Saigon + # Asia/Vientiane + 'IDDT', # 1 | +0400 | Asia/Jerusalem + # Asia/Tel_Aviv + # Israel + 'IDT', # 1 | +0300 | Asia/Gaza + # Asia/Hebron + # Asia/Jerusalem + # Asia/Tel_Aviv + # Israel + 'IHST', # 1 | +0600 | Asia/Colombo + 'IMT', # 0 | +0157 | Asia/Istanbul + # Europe/Istanbul + # Europe/Sofia + # Turkey + # 0 | +0657 | Asia/Irkutsk + 'IOT', # 0 | +0500 | Indian/Chagos + # 0 | +0600 | Indian/Chagos + 'IRDT', # 1 | +0430 | Asia/Tehran + # Iran + # 1 | +0500 | Asia/Tehran + # Iran + 'IRKST', # 1 | +0800 | Asia/Irkutsk + # 1 | +0900 | Asia/Irkutsk + 'IRKT', # 0 | +0700 | Asia/Irkutsk + # 0 | +0800 | Asia/Chita + # Asia/Irkutsk + # 0 | +0900 | Asia/Irkutsk + 'IRST', # 0 | +0330 | Asia/Tehran + # Iran + # 0 | +0400 | Asia/Tehran + # Iran + 'ISST', # 1 | +0000 | Atlantic/Reykjavik + # Iceland + 'IST', # 0 | -0100 | Atlantic/Reykjavik + # Iceland + # 0 | +0100 | Eire + # Europe/Dublin + # 0 | +0200 | Asia/Gaza + # Asia/Hebron + # Asia/Jerusalem + # Asia/Tel_Aviv + # Israel + # 0 | +0530 | Asia/Calcutta + # Asia/Colombo + # Asia/Dacca + # Asia/Dhaka + # Asia/Karachi + # Asia/Kathmandu + # Asia/Katmandu + # Asia/Kolkata + # Asia/Thimbu + # Asia/Thimphu + # 1 | +0035 | Eire + # Europe/Dublin + # 1 | +0100 | Eire + # Europe/Dublin + # 1 | +0630 | Asia/Calcutta + # Asia/Colombo + # Asia/Karachi + # Asia/Kolkata + 'JAVT', # 0 | +0720 | Asia/Jakarta + 'JCST', # 0 | +0900 | Asia/Pyongyang + # Asia/Sakhalin + # Asia/Seoul + # Asia/Tokyo + # Japan + # ROK + 'JDT', # 1 | +1000 | Asia/Tokyo + # Japan + 'JMT', # 0 | +0221 | Asia/Jerusalem + # Asia/Tel_Aviv + # Israel + 'JST', # 0 | +0900 | Asia/Dili + # Asia/Hong_Kong + # Asia/Jakarta + # Asia/Kuala_Lumpur + # Asia/Kuching + # Asia/Makassar + # Asia/Manila + # Asia/Pontianak + # Asia/Pyongyang + # Asia/Rangoon + # Asia/Sakhalin + # Asia/Seoul + # Asia/Singapore + # Asia/Taipei + # Asia/Tokyo + # Asia/Ujung_Pandang + # Hongkong + # Japan + # Pacific/Nauru + # ROC + # ROK + # Singapore + 'JWST', # 0 | +0800 | Asia/Taipei + # ROC + 'KART', # 0 | +0500 | Asia/Karachi + 'KDT', # 1 | +0900 | Asia/Seoul + # ROK + # 1 | +1000 | Asia/Seoul + # ROK + 'KGST', # 1 | +0600 | Asia/Bishkek + 'KGT', # 0 | +0500 | Asia/Bishkek + # 0 | +0600 | Asia/Bishkek + 'KIZST', # 1 | +0600 | Asia/Qyzylorda + 'KIZT', # 0 | +0400 | Asia/Qyzylorda + # 0 | +0500 | Asia/Qyzylorda + # 0 | +0600 | Asia/Qyzylorda + 'KMT', # 0 | -0653 | America/Cayman + # America/Grand_Turk + # America/Jamaica + # Jamaica + # 0 | +0136 | Europe/Vilnius + # 0 | +0202 | Europe/Kiev + 'KOST', # 0 | +1100 | Pacific/Kosrae + # 0 | +1200 | Pacific/Kosrae + 'KRAST', # 1 | +0700 | Asia/Krasnoyarsk + # Asia/Novokuznetsk + # 1 | +0800 | Asia/Krasnoyarsk + # Asia/Novokuznetsk + 'KRAT', # 0 | +0600 | Asia/Krasnoyarsk + # Asia/Novokuznetsk + # 0 | +0700 | Asia/Krasnoyarsk + # Asia/Novokuznetsk + # 0 | +0800 | Asia/Krasnoyarsk + 'KST', # 0 | +0800 | Asia/Pyongyang + # Asia/Seoul + # ROK + # 0 | +0830 | Asia/Pyongyang + # Asia/Seoul + # ROK + # 0 | +0900 | Asia/Pyongyang + # Asia/Seoul + # ROK + 'KUYST', # 1 | +0500 | Europe/Samara + 'KUYT', # 0 | +0300 | Europe/Samara + # 0 | +0400 | Europe/Samara + 'KWAT', # 0 | -1200 | Kwajalein + # Pacific/Kwajalein + 'LHDT', # 1 | +1100 | Australia/LHI + # Australia/Lord_Howe + # 1 | +1130 | Australia/LHI + # Australia/Lord_Howe + 'LHST', # 0 | +1030 | Australia/LHI + # Australia/Lord_Howe + 'LINT', # 0 | -1120 | Pacific/Kiritimati + # 0 | -1000 | Pacific/Kiritimati + # 0 | +1400 | Pacific/Kiritimati + 'LKT', # 0 | +0600 | Asia/Colombo + # 0 | +0630 | Asia/Colombo + 'LMT', # 0 | -1211 | Pacific/Midway + # 0 | -1213 | America/Adak + # America/Atka + # US/Aleutian + # 0 | -1233 | Pacific/Apia + # 0 | -1235 | Pacific/Fakaofo + # 0 | -1236 | Pacific/Enderbury + # 0 | -1237 | Pacific/Pago_Pago + # Pacific/Samoa + # US/Samoa + # 0 | -1240 | Pacific/Niue + # 0 | -1258 | America/Nome + # 0 | -1121 | Pacific/Rarotonga + # 0 | -1129 | Pacific/Honolulu + # Pacific/Johnston + # US/Hawaii + # 0 | -1131 | Pacific/Kiritimati + # 0 | -1000 | America/Anchorage + # US/Alaska + # 0 | -1002 | Pacific/Tahiti + # 0 | -1041 | America/Yakutat + # 0 | -1042 | America/Dawson + # Pacific/Marquesas + # 0 | -1059 | America/Sitka + # 0 | -0900 | America/Whitehorse + # Canada/Yukon + # Pacific/Gambier + # 0 | -0902 | America/Juneau + # 0 | -0914 | America/Metlakatla + # 0 | -0920 | Pacific/Pitcairn + # 0 | -0948 | America/Vancouver + # Canada/Pacific + # 0 | -0959 | America/Dawson_Creek + # 0 | -0807 | America/Los_Angeles + # US/Pacific + # US/Pacific-New + # 0 | -0812 | America/Ensenada + # America/Tijuana + # Mexico/BajaNorte + # 0 | -0814 | America/Creston + # 0 | -0815 | America/Boise + # 0 | -0821 | America/Santa_Isabel + # 0 | -0826 | America/Edmonton + # Canada/Mountain + # 0 | -0832 | America/Phoenix + # US/Arizona + # 0 | -0836 | America/Hermosillo + # 0 | -0842 | Chile/EasterIsland + # Pacific/Easter + # 0 | -0849 | America/Swift_Current + # 0 | -0854 | America/Mazatlan + # Mexico/BajaSur + # 0 | -0856 | America/Chihuahua + # 0 | -0859 | America/Bahia_Banderas + # 0 | -0700 | America/Denver + # America/Shiprock + # Navajo + # US/Mountain + # 0 | -0701 | America/Regina + # Canada/East-Saskatchewan + # Canada/Saskatchewan + # 0 | -0702 | America/Ojinaga + # 0 | -0713 | America/North_Dakota/Beulah + # 0 | -0714 | America/North_Dakota/New_Salem + # 0 | -0715 | America/North_Dakota/Center + # 0 | -0719 | America/Monterrey + # 0 | -0720 | America/Matamoros + # 0 | -0723 | America/Mexico_City + # Mexico/General + # 0 | -0731 | America/Winnipeg + # Canada/Central + # 0 | -0742 | America/Rainy_River + # 0 | -0754 | America/Atikokan + # America/Coral_Harbour + # 0 | -0758 | America/Guatemala + # 0 | -0602 | America/Merida + # Pacific/Galapagos + # 0 | -0603 | America/El_Salvador + # America/Thunder_Bay + # 0 | -0607 | America/Belize + # America/Nipigon + # 0 | -0609 | America/Chicago + # US/Central + # 0 | -0610 | America/Indiana/Vincennes + # America/Menominee + # 0 | -0611 | America/Indiana/Petersburg + # America/Tegucigalpa + # 0 | -0613 | America/Cancun + # America/Indiana/Tell_City + # 0 | -0614 | America/Indiana/Knox + # America/Indiana/Winamac + # America/Knox_IN + # US/Indiana-Starke + # 0 | -0615 | America/Fort_Wayne + # America/Indiana/Indianapolis + # America/Indiana/Marengo + # America/Indianapolis + # America/Managua + # US/East-Indiana + # 0 | -0617 | America/Kentucky/Louisville + # America/Louisville + # 0 | -0620 | America/Indiana/Vevay + # 0 | -0621 | America/Kentucky/Monticello + # 0 | -0624 | America/Costa_Rica + # 0 | -0628 | America/Detroit + # US/Michigan + # 0 | -0631 | America/Havana + # Cuba + # 0 | -0634 | America/Cayman + # 0 | -0641 | America/Guayaquil + # 0 | -0642 | America/Panama + # America/Toronto + # Canada/Eastern + # 0 | -0651 | America/Lima + # America/Nassau + # 0 | -0652 | America/Lima + # 0 | -0653 | America/Jamaica + # Jamaica + # 0 | -0504 | America/Bogota + # America/New_York + # US/Eastern + # 0 | -0506 | America/Montreal + # 0 | -0511 | America/Port-au-Prince + # 0 | -0515 | America/Grand_Turk + # 0 | -0517 | America/Santiago + # Chile/Continental + # 0 | -0520 | America/Santo_Domingo + # 0 | -0521 | America/Eirunepe + # 0 | -0523 | America/Argentina/Rio_Gallegos + # 0 | -0524 | America/Aruba + # America/Curacao + # America/Kralendijk + # America/Lower_Princes + # 0 | -0525 | America/Argentina/Mendoza + # America/Mendoza + # America/Thule + # 0 | -0526 | America/Argentina/San_Juan + # 0 | -0527 | America/Argentina/Ushuaia + # America/La_Paz + # 0 | -0529 | America/Porto_Acre + # America/Rio_Branco + # Brazil/Acre + # 0 | -0532 | America/Caracas + # 0 | -0533 | America/Argentina/La_Rioja + # 0 | -0535 | America/Argentina/San_Luis + # 0 | -0536 | America/Puerto_Rico + # 0 | -0537 | America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Catamarca + # 0 | -0538 | America/Argentina/Salta + # 0 | -0539 | America/Argentina/Jujuy + # America/Argentina/Tucuman + # America/Jujuy + # 0 | -0541 | America/Moncton + # Atlantic/Bermuda + # 0 | -0543 | America/Argentina/Cordoba + # America/Cordoba + # America/Rosario + # 0 | -0544 | America/Porto_Velho + # 0 | -0546 | America/Halifax + # Canada/Atlantic + # 0 | -0553 | America/Antigua + # 0 | -0554 | America/Anguilla + # America/Dominica + # America/Grenada + # America/Guadeloupe + # America/Marigot + # America/Montserrat + # America/Port_of_Spain + # America/St_Barthelemy + # America/St_Kitts + # America/St_Lucia + # America/St_Thomas + # America/St_Vincent + # America/Tortola + # America/Virgin + # 0 | -0556 | America/Martinique + # 0 | -0557 | America/Boa_Vista + # 0 | -0558 | America/Goose_Bay + # 0 | -0400 | America/Glace_Bay + # America/Manaus + # Brazil/West + # 0 | -0402 | America/Barbados + # 0 | -0406 | America/Argentina/Buenos_Aires + # America/Buenos_Aires + # 0 | -0407 | America/Guyana + # 0 | -0409 | America/Asuncion + # Atlantic/Stanley + # 0 | -0412 | America/Blanc-Sablon + # 0 | -0415 | America/Miquelon + # America/Montevideo + # 0 | -0416 | America/Cuiaba + # 0 | -0419 | America/Paramaribo + # 0 | -0421 | America/Santarem + # 0 | -0422 | America/Campo_Grande + # 0 | -0429 | America/St_Johns + # Canada/Newfoundland + # 0 | -0431 | America/Cayenne + # 0 | -0433 | America/Godthab + # 0 | -0446 | America/Belem + # 0 | -0447 | America/Araguaina + # 0 | -0454 | America/Sao_Paulo + # Brazil/East + # 0 | -0326 | America/Bahia + # America/Fortaleza + # 0 | -0337 | America/Maceio + # 0 | -0340 | America/Recife + # 0 | -0350 | America/Noronha + # Brazil/DeNoronha + # 0 | -0217 | Atlantic/Azores + # 0 | -0226 | Atlantic/Cape_Verde + # 0 | -0232 | America/Scoresbysund + # 0 | -0233 | Atlantic/Reykjavik + # Iceland + # 0 | -0245 | America/Danmarkshavn + # 0 | -0252 | Atlantic/Madeira + # 0 | -0258 | Africa/Bissau + # Atlantic/Canary + # 0 | -0107 | Africa/El_Aaiun + # 0 | -0117 | Africa/Monrovia + # 0 | -0123 | Europe/Lisbon + # Portugal + # 0 | -0130 | Africa/Casablanca + # 0 | -0133 | Atlantic/Faeroe + # Atlantic/Faroe + # 0 | -0135 | Eire + # Europe/Dublin + # 0 | -0139 | Africa/Ceuta + # Europe/Gibraltar + # 0 | -0144 | Africa/Abidjan + # Africa/Bamako + # Africa/Banjul + # Africa/Conakry + # Africa/Dakar + # Africa/Freetown + # Africa/Lome + # Africa/Nouakchott + # Africa/Ouagadougou + # Africa/Sao_Tome + # Africa/Timbuktu + # Atlantic/St_Helena + # 0 | -0145 | Europe/Madrid + # 0 | -0159 | Africa/Accra + # Europe/Belfast + # Europe/Guernsey + # Europe/Isle_of_Man + # Europe/Jersey + # Europe/London + # GB + # GB-Eire + # 0 | +0006 | Europe/Andorra + # 0 | +0009 | Europe/Paris + # 0 | +0012 | Africa/Algiers + # 0 | +0014 | Africa/Bangui + # Africa/Brazzaville + # Africa/Douala + # Africa/Kinshasa + # Africa/Lagos + # Africa/Libreville + # Africa/Luanda + # Africa/Malabo + # Africa/Niamey + # Africa/Porto-Novo + # 0 | +0020 | Europe/Amsterdam + # 0 | +0025 | Europe/Luxembourg + # 0 | +0030 | Europe/Monaco + # 0 | +0041 | Africa/Tunis + # 0 | +0043 | Arctic/Longyearbyen + # Atlantic/Jan_Mayen + # Europe/Oslo + # 0 | +0053 | Africa/Tripoli + # Europe/Berlin + # Libya + # 0 | +0058 | Europe/Malta + # 0 | +0100 | Africa/Ndjamena + # 0 | +0105 | Europe/Vienna + # 0 | +0108 | Africa/Windhoek + # 0 | +0116 | Europe/Budapest + # 0 | +0119 | Europe/Tirane + # 0 | +0122 | Europe/Belgrade + # Europe/Kaliningrad + # Europe/Ljubljana + # Europe/Podgorica + # Europe/Sarajevo + # Europe/Skopje + # Europe/Zagreb + # 0 | +0124 | Europe/Warsaw + # Poland + # 0 | +0129 | Europe/Uzhgorod + # 0 | +0135 | Europe/Athens + # 0 | +0137 | Europe/Riga + # 0 | +0139 | Europe/Tallinn + # 0 | +0140 | Europe/Helsinki + # Europe/Mariehamn + # 0 | +0141 | Europe/Vilnius + # 0 | +0144 | Africa/Gaborone + # Europe/Bucharest + # 0 | +0150 | Africa/Maseru + # Europe/Minsk + # 0 | +0152 | Africa/Johannesburg + # 0 | +0153 | Africa/Lusaka + # 0 | +0155 | Europe/Chisinau + # Europe/Tiraspol + # 0 | +0156 | Asia/Istanbul + # Europe/Istanbul + # Turkey + # 0 | +0200 | Africa/Kigali + # 0 | +0202 | Europe/Kiev + # 0 | +0204 | Africa/Harare + # Africa/Mbabane + # 0 | +0205 | Africa/Cairo + # Egypt + # 0 | +0210 | Africa/Juba + # Africa/Kampala + # Africa/Khartoum + # Africa/Maputo + # 0 | +0213 | Asia/Nicosia + # Europe/Nicosia + # 0 | +0216 | Europe/Simferopol + # 0 | +0218 | Asia/Gaza + # 0 | +0220 | Africa/Blantyre + # Asia/Hebron + # 0 | +0221 | Asia/Jerusalem + # Asia/Tel_Aviv + # Europe/Zaporozhye + # Israel + # 0 | +0222 | Asia/Beirut + # 0 | +0224 | Asia/Amman + # 0 | +0225 | Asia/Damascus + # 0 | +0227 | Africa/Nairobi + # 0 | +0230 | Europe/Moscow + # W-SU + # 0 | +0235 | Africa/Addis_Ababa + # 0 | +0237 | Africa/Dar_es_Salaam + # 0 | +0253 | Africa/Djibouti + # Indian/Comoro + # 0 | +0258 | Asia/Baghdad + # Asia/Yerevan + # Europe/Volgograd + # 0 | +0259 | Asia/Tbilisi + # 0 | +0300 | Asia/Aden + # 0 | +0301 | Africa/Mogadishu + # Indian/Mayotte + # 0 | +0307 | Asia/Riyadh + # 0 | +0310 | Indian/Antananarivo + # 0 | +0312 | Asia/Kuwait + # 0 | +0319 | Asia/Baku + # 0 | +0320 | Europe/Samara + # 0 | +0321 | Asia/Aqtau + # 0 | +0322 | Asia/Bahrain + # 0 | +0325 | Asia/Oral + # 0 | +0326 | Asia/Qatar + # Asia/Tehran + # Iran + # 0 | +0341 | Asia/Dubai + # 0 | +0342 | Indian/Mahe + # Indian/Reunion + # 0 | +0349 | Asia/Aqtobe + # 0 | +0350 | Indian/Mauritius + # 0 | +0354 | Asia/Ashgabat + # Asia/Ashkhabad + # Asia/Muscat + # 0 | +0403 | Asia/Yekaterinburg + # 0 | +0422 | Asia/Qyzylorda + # 0 | +0428 | Asia/Karachi + # Asia/Samarkand + # 0 | +0435 | Asia/Dushanbe + # 0 | +0437 | Asia/Kabul + # Asia/Tashkent + # 0 | +0450 | Indian/Chagos + # 0 | +0454 | Asia/Omsk + # Indian/Maldives + # 0 | +0458 | Asia/Bishkek + # 0 | +0508 | Asia/Almaty + # 0 | +0519 | Asia/Colombo + # 0 | +0532 | Asia/Novosibirsk + # 0 | +0541 | Asia/Kathmandu + # Asia/Katmandu + # 0 | +0550 | Asia/Kashgar + # Asia/Urumqi + # 0 | +0553 | Asia/Calcutta + # Asia/Kolkata + # 0 | +0559 | Asia/Thimbu + # Asia/Thimphu + # 0 | +0602 | Asia/Dacca + # Asia/Dhaka + # 0 | +0607 | Asia/Hovd + # 0 | +0611 | Asia/Krasnoyarsk + # 0 | +0625 | Asia/Rangoon + # 0 | +0642 | Asia/Bangkok + # 0 | +0647 | Asia/Kuala_Lumpur + # 0 | +0650 | Asia/Vientiane + # 0 | +0655 | Asia/Singapore + # Singapore + # 0 | +0657 | Asia/Irkutsk + # 0 | +0700 | Asia/Phnom_Penh + # 0 | +0707 | Asia/Ho_Chi_Minh + # Asia/Jakarta + # Asia/Saigon + # 0 | +0708 | Asia/Ulaanbaatar + # Asia/Ulan_Bator + # 0 | +0717 | Asia/Pontianak + # 0 | +0721 | Asia/Kuching + # 0 | +0734 | Asia/Chita + # Asia/Macao + # Asia/Macau + # 0 | +0737 | Asia/Hong_Kong + # Hongkong + # 0 | +0738 | Asia/Choibalsan + # 0 | +0740 | Asia/Brunei + # 0 | +0743 | Australia/Perth + # Australia/West + # 0 | +0758 | Asia/Makassar + # Asia/Ujung_Pandang + # 0 | +0804 | Asia/Manila + # 0 | +0806 | Asia/Chongqing + # Asia/Chungking + # Asia/Harbin + # Asia/Shanghai + # Asia/Taipei + # PRC + # ROC + # 0 | +0822 | Asia/Dili + # 0 | +0823 | Asia/Pyongyang + # 0 | +0828 | Asia/Seoul + # ROK + # 0 | +0835 | Australia/Eucla + # 0 | +0839 | Asia/Yakutsk + # 0 | +0848 | Asia/Vladivostok + # 0 | +0902 | Asia/Khandyga + # 0 | +0923 | Asia/Jayapura + # 0 | +0931 | Asia/Sakhalin + # 0 | +0933 | Asia/Ust-Nera + # 0 | +0935 | Australia/Currie + # 0 | +0939 | Pacific/Guam + # 0 | +0940 | Australia/Melbourne + # Australia/Victoria + # 0 | +0943 | Pacific/Saipan + # 0 | +0949 | Australia/Hobart + # Australia/Tasmania + # 0 | +0956 | Australia/Lindeman + # 0 | +1003 | Asia/Magadan + # 0 | +1005 | Australia/ACT + # Australia/Canberra + # Australia/NSW + # Australia/Sydney + # 0 | +1012 | Australia/Brisbane + # Australia/Queensland + # 0 | +1015 | Asia/Srednekolymsk + # 0 | +1035 | Asia/Kamchatka + # 0 | +1036 | Australia/LHI + # Australia/Lord_Howe + # 0 | +1040 | Pacific/Guadalcanal + # 0 | +1052 | Pacific/Kosrae + # 0 | +1106 | Pacific/Noumea + # 0 | +1108 | Pacific/Nauru + # 0 | +1109 | Kwajalein + # Pacific/Kwajalein + # 0 | +1112 | Pacific/Norfolk + # 0 | +1113 | Pacific/Efate + # 0 | +1125 | Pacific/Majuro + # 0 | +1139 | Antarctica/McMurdo + # Antarctica/South_Pole + # NZ + # Pacific/Auckland + # 0 | +1150 | Asia/Anadyr + # 0 | +1156 | Pacific/Fiji + # 0 | +1214 | NZ-CHAT + # Pacific/Chatham + # 0 | +1219 | Pacific/Tongatapu + # 0 | +1233 | Pacific/Apia + # 0 | +1237 | Pacific/Pago_Pago + # Pacific/Samoa + # US/Samoa + 'LRT', # 0 | -0116 | Africa/Monrovia + 'LST', # 1 | +0237 | Europe/Riga + 'MADMT', # 1 | +0100 | Atlantic/Madeira + 'MADST', # 1 | +0000 | Atlantic/Madeira + 'MADT', # 0 | -0100 | Atlantic/Madeira + 'MAGST', # 1 | +1100 | Asia/Magadan + # Asia/Srednekolymsk + # Asia/Ust-Nera + # 1 | +1200 | Asia/Magadan + # Asia/Srednekolymsk + # Asia/Ust-Nera + 'MAGT', # 0 | +1000 | Asia/Magadan + # Asia/Srednekolymsk + # Asia/Ust-Nera + # 0 | +1100 | Asia/Magadan + # Asia/Srednekolymsk + # Asia/Ust-Nera + # 0 | +1200 | Asia/Magadan + # Asia/Srednekolymsk + # Asia/Ust-Nera + 'MALST', # 1 | +0720 | Asia/Kuala_Lumpur + # Asia/Singapore + # Singapore + 'MALT', # 0 | +0700 | Asia/Kuala_Lumpur + # Asia/Singapore + # Singapore + # 0 | +0720 | Asia/Kuala_Lumpur + # Asia/Singapore + # Singapore + # 0 | +0730 | Asia/Kuala_Lumpur + # Asia/Singapore + # Singapore + 'MART', # 0 | -1030 | Pacific/Marquesas + 'MAWT', # 0 | +0500 | Antarctica/Mawson + # 0 | +0600 | Antarctica/Mawson + 'MDDT', # 1 | -0500 | America/Cambridge_Bay + # America/Yellowknife + 'MDST', # 1 | +0431 | Europe/Moscow + # W-SU + 'MDT', # 1 | -0600 | America/Bahia_Banderas + # America/Boise + # America/Cambridge_Bay + # America/Chihuahua + # America/Denver + # America/Edmonton + # America/Hermosillo + # America/Inuvik + # America/Mazatlan + # America/North_Dakota/Beulah + # America/North_Dakota/Center + # America/North_Dakota/New_Salem + # America/Ojinaga + # America/Phoenix + # America/Regina + # America/Shiprock + # America/Swift_Current + # America/Yellowknife + # Canada/East-Saskatchewan + # Canada/Mountain + # Canada/Saskatchewan + # MST7MDT + # Mexico/BajaSur + # Navajo + # US/Arizona + # US/Mountain + 'MEST', # 1 | +0200 | MET + 'MET', # 0 | +0100 | MET + 'MHT', # 0 | +1100 | Kwajalein + # Pacific/Kwajalein + # Pacific/Majuro + # 0 | +1200 | Kwajalein + # Pacific/Kwajalein + # Pacific/Majuro + 'MIST', # 0 | +1100 | Antarctica/Macquarie + 'MMT', # 0 | -0615 | America/Managua + # 0 | -0415 | America/Montevideo + # 0 | -0117 | Africa/Monrovia + # 0 | +0150 | Europe/Minsk + # 0 | +0230 | Europe/Moscow + # W-SU + # 0 | +0231 | Europe/Moscow + # W-SU + # 0 | +0454 | Indian/Maldives + # 0 | +0520 | Asia/Colombo + # 0 | +0630 | Asia/Rangoon + # 0 | +0758 | Asia/Makassar + # Asia/Ujung_Pandang + 'MOST', # 1 | +0900 | Asia/Macao + # Asia/Macau + 'MOT', # 0 | +0800 | Asia/Macao + # Asia/Macau + 'MPT', # 0 | +0900 | Pacific/Saipan + # 0 | +1000 | Pacific/Saipan + # 1 | -0600 | America/Boise + # America/Cambridge_Bay + # America/Denver + # America/Edmonton + # America/North_Dakota/Beulah + # America/North_Dakota/Center + # America/North_Dakota/New_Salem + # America/Regina + # America/Shiprock + # America/Swift_Current + # America/Yellowknife + # Canada/East-Saskatchewan + # Canada/Mountain + # Canada/Saskatchewan + # MST7MDT + # Navajo + # US/Mountain + 'MSD', # 1 | +0400 | Europe/Chisinau + # Europe/Kaliningrad + # Europe/Kiev + # Europe/Minsk + # Europe/Moscow + # Europe/Riga + # Europe/Samara + # Europe/Simferopol + # Europe/Tallinn + # Europe/Tiraspol + # Europe/Uzhgorod + # Europe/Vilnius + # Europe/Zaporozhye + # W-SU + 'MSK', # 0 | +0300 | Europe/Chisinau + # Europe/Kaliningrad + # Europe/Kiev + # Europe/Minsk + # Europe/Moscow + # Europe/Riga + # Europe/Samara + # Europe/Simferopol + # Europe/Tallinn + # Europe/Tiraspol + # Europe/Uzhgorod + # Europe/Vilnius + # Europe/Volgograd + # Europe/Zaporozhye + # W-SU + # 0 | +0400 | Europe/Moscow + # Europe/Simferopol + # Europe/Volgograd + # W-SU + # 1 | +0400 | Europe/Volgograd + 'MSM', # 1 | +0500 | Europe/Moscow + # W-SU + 'MST', # 0 | -0700 | America/Bahia_Banderas + # America/Boise + # America/Cambridge_Bay + # America/Chihuahua + # America/Creston + # America/Dawson_Creek + # America/Denver + # America/Edmonton + # America/Ensenada + # America/Hermosillo + # America/Inuvik + # America/Mazatlan + # America/Mexico_City + # America/North_Dakota/Beulah + # America/North_Dakota/Center + # America/North_Dakota/New_Salem + # America/Ojinaga + # America/Phoenix + # America/Regina + # America/Santa_Isabel + # America/Shiprock + # America/Swift_Current + # America/Tijuana + # America/Yellowknife + # Canada/East-Saskatchewan + # Canada/Mountain + # Canada/Saskatchewan + # MST + # MST7MDT + # Mexico/BajaNorte + # Mexico/BajaSur + # Mexico/General + # Navajo + # US/Arizona + # US/Mountain + # 1 | +0331 | Europe/Moscow + # W-SU + 'MUST', # 1 | +0500 | Indian/Mauritius + 'MUT', # 0 | +0400 | Indian/Mauritius + 'MVT', # 0 | +0500 | Indian/Maldives + 'MWT', # 1 | -0600 | America/Boise + # America/Cambridge_Bay + # America/Denver + # America/Edmonton + # America/North_Dakota/Beulah + # America/North_Dakota/Center + # America/North_Dakota/New_Salem + # America/Phoenix + # America/Regina + # America/Shiprock + # America/Swift_Current + # America/Yellowknife + # Canada/East-Saskatchewan + # Canada/Mountain + # Canada/Saskatchewan + # MST7MDT + # Navajo + # US/Arizona + # US/Mountain + 'MYT', # 0 | +0800 | Asia/Kuala_Lumpur + # Asia/Kuching + 'NCST', # 1 | +1200 | Pacific/Noumea + 'NCT', # 0 | +1100 | Pacific/Noumea + 'NDDT', # 1 | -0230 | America/St_Johns + # Canada/Newfoundland + 'NDT', # 1 | -1000 | Pacific/Midway + # 1 | -0329 | America/Goose_Bay + # America/St_Johns + # Canada/Newfoundland + # 1 | -0330 | America/Goose_Bay + # America/St_Johns + # Canada/Newfoundland + 'NEGT', # 0 | -0430 | America/Paramaribo + 'NEST', # 1 | +0120 | Europe/Amsterdam + 'NET', # 0 | +0020 | Europe/Amsterdam + 'NFT', # 0 | +1130 | Pacific/Norfolk + 'NMT', # 0 | +0549 | Asia/Novokuznetsk + # 0 | +1112 | Pacific/Norfolk + 'NOVST', # 1 | +0700 | Asia/Novokuznetsk + # Asia/Novosibirsk + # 1 | +0800 | Asia/Novosibirsk + 'NOVT', # 0 | +0600 | Asia/Novokuznetsk + # Asia/Novosibirsk + # 0 | +0700 | Asia/Novokuznetsk + # Asia/Novosibirsk + 'NPT', # 0 | +0545 | Asia/Kathmandu + # Asia/Katmandu + # 1 | -1000 | America/Adak + # America/Atka + # America/Nome + # US/Aleutian + # 1 | -0330 | America/Goose_Bay + # America/St_Johns + # Canada/Newfoundland + 'NRT', # 0 | +1130 | Pacific/Nauru + # 0 | +1200 | Pacific/Nauru + 'NST', # 0 | -1100 | America/Adak + # America/Atka + # America/Nome + # Pacific/Midway + # Pacific/Pago_Pago + # Pacific/Samoa + # US/Aleutian + # US/Samoa + # 0 | -0429 | America/Goose_Bay + # America/St_Johns + # Canada/Newfoundland + # 0 | -0430 | America/Goose_Bay + # America/St_Johns + # Canada/Newfoundland + # 1 | +0120 | Europe/Amsterdam + 'NUT', # 0 | -1230 | Pacific/Niue + # 0 | -1240 | Pacific/Niue + # 0 | -1100 | Pacific/Niue + 'NWT', # 1 | -1000 | America/Adak + # America/Atka + # America/Nome + # US/Aleutian + # 1 | -0330 | America/Goose_Bay + # America/St_Johns + # Canada/Newfoundland + 'NZDT', # 1 | +1300 | Antarctica/McMurdo + # Antarctica/South_Pole + # NZ + # Pacific/Auckland + 'NZMT', # 0 | +1130 | Antarctica/McMurdo + # Antarctica/South_Pole + # NZ + # Pacific/Auckland + 'NZST', # 0 | +1200 | Antarctica/McMurdo + # Antarctica/South_Pole + # NZ + # Pacific/Auckland + # 1 | +1200 | Antarctica/McMurdo + # Antarctica/South_Pole + # NZ + # Pacific/Auckland + # 1 | +1230 | Antarctica/McMurdo + # Antarctica/South_Pole + # NZ + # Pacific/Auckland + 'OMSST', # 1 | +0600 | Asia/Omsk + # 1 | +0700 | Asia/Omsk + 'OMST', # 0 | +0500 | Asia/Omsk + # 0 | +0600 | Asia/Omsk + # 0 | +0700 | Asia/Omsk + 'ORAST', # 1 | +0500 | Asia/Oral + 'ORAT', # 0 | +0400 | Asia/Oral + # 0 | +0500 | Asia/Oral + 'PDDT', # 1 | -0600 | America/Inuvik + 'PDT', # 1 | -0700 | 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 + 'PEST', # 1 | -0400 | America/Lima + 'PET', # 0 | -0500 | America/Lima + 'PETST', # 1 | +1200 | Asia/Kamchatka + # 1 | +1300 | Asia/Kamchatka + 'PETT', # 0 | +1100 | Asia/Kamchatka + # 0 | +1200 | Asia/Kamchatka + 'PGT', # 0 | +1000 | Pacific/Port_Moresby + 'PHOT', # 0 | -1200 | Pacific/Enderbury + # 0 | -1100 | Pacific/Enderbury + # 0 | +1300 | Pacific/Enderbury + 'PHST', # 1 | +0900 | Asia/Manila + 'PHT', # 0 | +0800 | Asia/Manila + 'PKST', # 1 | +0600 | Asia/Karachi + 'PKT', # 0 | +0500 | Asia/Karachi + 'PMDT', # 1 | -0200 | America/Miquelon + 'PMST', # 0 | -0300 | America/Miquelon + 'PMT', # 0 | -0419 | America/Paramaribo + # 0 | +0009 | Africa/Algiers + # Africa/Tunis + # Europe/Monaco + # Europe/Paris + # 0 | +0058 | Europe/Bratislava + # Europe/Prague + # 0 | +0345 | Asia/Yekaterinburg + # 0 | +0717 | Asia/Pontianak + # 0 | +1000 | Antarctica/DumontDUrville + 'PNT', # 0 | -0930 | Pacific/Pitcairn + 'PONT', # 0 | +1100 | Pacific/Pohnpei + # Pacific/Ponape + 'PPMT', # 0 | -0511 | America/Port-au-Prince + 'PPT', # 1 | -0700 | America/Dawson_Creek + # America/Ensenada + # America/Juneau + # America/Los_Angeles + # America/Metlakatla + # America/Santa_Isabel + # America/Sitka + # America/Tijuana + # America/Vancouver + # Canada/Pacific + # Mexico/BajaNorte + # PST8PDT + # US/Pacific + # US/Pacific-New + 'PST', # 0 | -0800 | 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 + 'PWT', # 0 | +0900 | Pacific/Palau + # 1 | -0700 | America/Dawson_Creek + # America/Ensenada + # America/Juneau + # America/Los_Angeles + # America/Metlakatla + # America/Santa_Isabel + # America/Sitka + # America/Tijuana + # America/Vancouver + # Canada/Pacific + # Mexico/BajaNorte + # PST8PDT + # US/Pacific + # US/Pacific-New + 'PYST', # 1 | -0300 | America/Asuncion + 'PYT', # 0 | -0400 | America/Asuncion + # 0 | -0300 | America/Asuncion + 'QMT', # 0 | -0646 | America/Guayaquil + 'QYZST', # 1 | +0700 | Asia/Qyzylorda + 'QYZT', # 0 | +0500 | Asia/Qyzylorda + # 0 | +0600 | Asia/Qyzylorda + 'RET', # 0 | +0400 | Indian/Reunion + 'RMT', # 0 | -0232 | Atlantic/Reykjavik + # Iceland + # 0 | +0050 | Europe/Rome + # Europe/San_Marino + # Europe/Vatican + # 0 | +0137 | Europe/Riga + # 0 | +0625 | Asia/Rangoon + 'ROTT', # 0 | -0300 | Antarctica/Rothera + 'SAKST', # 1 | +1100 | Asia/Sakhalin + # 1 | +1200 | Asia/Sakhalin + 'SAKT', # 0 | +1000 | Asia/Sakhalin + # 0 | +1100 | Asia/Sakhalin + 'SAMST', # 1 | +0400 | Europe/Samara + # 1 | +0500 | Europe/Samara + # 1 | +0600 | Asia/Samarkand + 'SAMT', # 0 | +0300 | Europe/Samara + # 0 | +0400 | Asia/Samarkand + # Europe/Samara + # 0 | +0500 | Asia/Samarkand + 'SAST', # 0 | +0130 | Africa/Gaborone + # Africa/Johannesburg + # 0 | +0200 | Africa/Johannesburg + # Africa/Maseru + # Africa/Mbabane + # Africa/Windhoek + # 1 | +0300 | Africa/Johannesburg + # Africa/Maseru + # Africa/Windhoek + 'SBT', # 0 | +1100 | Pacific/Guadalcanal + 'SCT', # 0 | +0400 | Indian/Mahe + 'SDMT', # 0 | -0520 | America/Santo_Domingo + 'SDT', # 1 | -1000 | Pacific/Apia + 'SET', # 0 | +0100 | Europe/Stockholm + 'SGT', # 0 | +0730 | Asia/Singapore + # Singapore + # 0 | +0800 | Asia/Singapore + # Singapore + 'SHEST', # 1 | +0600 | Asia/Aqtau + 'SHET', # 0 | +0500 | Asia/Aqtau + # 0 | +0600 | Asia/Aqtau + 'SJMT', # 0 | -0624 | America/Costa_Rica + 'SMT', # 0 | -0517 | America/Santiago + # Chile/Continental + # 0 | -0409 | Atlantic/Stanley + # 0 | +0216 | Europe/Simferopol + # 0 | +0655 | Asia/Kuala_Lumpur + # Asia/Singapore + # Singapore + # 0 | +0706 | Asia/Ho_Chi_Minh + # Asia/Phnom_Penh + # Asia/Saigon + # Asia/Vientiane + 'SRET', # 0 | +1100 | Asia/Srednekolymsk + 'SRT', # 0 | -0430 | America/Paramaribo + # 0 | -0300 | America/Paramaribo + 'SST', # 0 | -1100 | Pacific/Apia + # Pacific/Midway + # Pacific/Pago_Pago + # Pacific/Samoa + # US/Samoa + 'STAT', # 0 | +0300 | Europe/Volgograd + # 0 | +0400 | Europe/Volgograd + 'SVEST', # 1 | +0500 | Asia/Yekaterinburg + # 1 | +0600 | Asia/Yekaterinburg + 'SVET', # 0 | +0400 | Asia/Yekaterinburg + # 0 | +0500 | Asia/Yekaterinburg + 'SWAT', # 0 | +0130 | Africa/Windhoek + 'SYOT', # 0 | +0300 | Antarctica/Syowa + 'TAHT', # 0 | -1000 | Pacific/Tahiti + 'TASST', # 1 | +0600 | Asia/Tashkent + # 1 | +0700 | Asia/Tashkent + 'TAST', # 0 | +0500 | Asia/Tashkent + # 0 | +0600 | Asia/Samarkand + # Asia/Tashkent + 'TBIST', # 1 | +0400 | Asia/Tbilisi + # 1 | +0500 | Asia/Tbilisi + 'TBIT', # 0 | +0300 | Asia/Tbilisi + # 0 | +0400 | Asia/Tbilisi + 'TBMT', # 0 | +0259 | Asia/Tbilisi + 'TFT', # 0 | +0500 | Indian/Kerguelen + 'TJT', # 0 | +0500 | Asia/Dushanbe + 'TKT', # 0 | -1100 | Pacific/Fakaofo + # 0 | +1300 | Pacific/Fakaofo + 'TLT', # 0 | +0800 | Asia/Dili + # 0 | +0900 | Asia/Dili + 'TMT', # 0 | +0139 | Europe/Tallinn + # 0 | +0326 | Asia/Tehran + # Iran + # 0 | +0400 | Asia/Ashgabat + # Asia/Ashkhabad + # 0 | +0500 | Asia/Ashgabat + # Asia/Ashkhabad + 'TOST', # 1 | +1400 | Pacific/Tongatapu + 'TOT', # 0 | +1220 | Pacific/Tongatapu + # 0 | +1300 | Pacific/Tongatapu + 'TRST', # 1 | +0400 | Asia/Istanbul + # Europe/Istanbul + # Turkey + 'TRT', # 0 | +0300 | Asia/Istanbul + # Europe/Istanbul + # Turkey + 'TSAT', # 0 | +0300 | Europe/Volgograd + 'TVT', # 0 | +1200 | Pacific/Funafuti + 'UCT', # 0 | +0000 | Etc/UCT + # UCT + 'ULAST', # 1 | +0900 | Asia/Ulaanbaatar + # Asia/Ulan_Bator + 'ULAT', # 0 | +0700 | Asia/Choibalsan + # Asia/Ulaanbaatar + # Asia/Ulan_Bator + # 0 | +0800 | Asia/Choibalsan + # Asia/Ulaanbaatar + # Asia/Ulan_Bator + 'URAST', # 1 | +0500 | Asia/Oral + # 1 | +0600 | Asia/Oral + 'URAT', # 0 | +0400 | Asia/Oral + # 0 | +0500 | Asia/Oral + # 0 | +0600 | Asia/Oral + 'UTC', # 0 | +0000 | Antarctica/Troll + # Etc/UTC + # Etc/Universal + # Etc/Zulu + # UTC + # Universal + # Zulu + 'UYHST', # 1 | -0300 | America/Montevideo + # 1 | -0330 | America/Montevideo + 'UYST', # 1 | -0200 | America/Montevideo + 'UYT', # 0 | -0430 | America/Montevideo + # 0 | -0300 | America/Montevideo + 'UZST', # 1 | +0600 | Asia/Samarkand + # Asia/Tashkent + 'UZT', # 0 | +0500 | Asia/Samarkand + # Asia/Tashkent + 'VET', # 0 | -0530 | America/Caracas + # 0 | -0400 | America/Caracas + 'VLAST', # 1 | +1000 | Asia/Vladivostok + # 1 | +1100 | Asia/Khandyga + # Asia/Vladivostok + 'VLAT', # 0 | +0900 | Asia/Vladivostok + # 0 | +1000 | Asia/Khandyga + # Asia/Ust-Nera + # Asia/Vladivostok + # 0 | +1100 | Asia/Khandyga + # Asia/Ust-Nera + # Asia/Vladivostok + 'VOLST', # 1 | +0400 | Europe/Volgograd + # 1 | +0500 | Europe/Volgograd + 'VOLT', # 0 | +0300 | Europe/Volgograd + # 0 | +0400 | Europe/Volgograd + 'VOST', # 0 | +0600 | Antarctica/Vostok + 'VUST', # 1 | +1200 | Pacific/Efate + 'VUT', # 0 | +1100 | Pacific/Efate + 'WAKT', # 0 | +1200 | Pacific/Wake + 'WARST', # 1 | -0300 | America/Argentina/Jujuy + # America/Argentina/Mendoza + # America/Argentina/San_Luis + # America/Jujuy + # America/Mendoza + 'WART', # 0 | -0400 | America/Argentina/Catamarca + # America/Argentina/ComodRivadavia + # America/Argentina/Cordoba + # America/Argentina/Jujuy + # America/Argentina/La_Rioja + # America/Argentina/Mendoza + # America/Argentina/Rio_Gallegos + # America/Argentina/Salta + # America/Argentina/San_Juan + # America/Argentina/San_Luis + # America/Argentina/Tucuman + # America/Argentina/Ushuaia + # America/Catamarca + # America/Cordoba + # America/Jujuy + # America/Mendoza + # America/Rosario + 'WAST', # 1 | +0200 | Africa/Ndjamena + # Africa/Windhoek + 'WAT', # 0 | -0100 | Africa/Bissau + # Africa/El_Aaiun + # 0 | +0100 | Africa/Bangui + # Africa/Brazzaville + # Africa/Douala + # Africa/Kinshasa + # Africa/Lagos + # Africa/Libreville + # Africa/Luanda + # Africa/Malabo + # Africa/Ndjamena + # Africa/Niamey + # Africa/Porto-Novo + # Africa/Windhoek + 'WEMT', # 1 | +0200 | Europe/Lisbon + # Europe/Madrid + # Europe/Monaco + # Europe/Paris + # Portugal + 'WEST', # 0 | +0100 | Europe/Paris + # 1 | +0100 | Africa/Algiers + # Africa/Casablanca + # Africa/Ceuta + # Africa/El_Aaiun + # Atlantic/Canary + # Atlantic/Faeroe + # Atlantic/Faroe + # Atlantic/Madeira + # Europe/Brussels + # Europe/Lisbon + # Europe/Luxembourg + # Europe/Madrid + # Europe/Monaco + # Europe/Paris + # Portugal + # WET + # 1 | +0200 | Europe/Luxembourg + 'WET', # 0 | +0000 | Africa/Algiers + # Africa/Casablanca + # Africa/Ceuta + # Africa/El_Aaiun + # Atlantic/Azores + # Atlantic/Canary + # Atlantic/Faeroe + # Atlantic/Faroe + # Atlantic/Madeira + # Europe/Andorra + # Europe/Brussels + # Europe/Lisbon + # Europe/Luxembourg + # Europe/Madrid + # Europe/Monaco + # Europe/Paris + # Portugal + # WET + # 0 | +0100 | Europe/Luxembourg + 'WFT', # 0 | +1200 | Pacific/Wallis + 'WGST', # 1 | -0200 | America/Danmarkshavn + # America/Godthab + 'WGT', # 0 | -0300 | America/Danmarkshavn + # America/Godthab + 'WIB', # 0 | +0700 | Asia/Jakarta + # Asia/Pontianak + # 0 | +0730 | Asia/Jakarta + # Asia/Pontianak + # 0 | +0800 | Asia/Jakarta + # Asia/Pontianak + 'WIT', # 0 | +0900 | Asia/Jayapura + 'WITA', # 0 | +0800 | Asia/Dili + # Asia/Makassar + # Asia/Pontianak + # Asia/Ujung_Pandang + 'WMT', # 0 | +0124 | Europe/Vilnius + # Europe/Warsaw + # Poland + 'WSDT', # 1 | +1400 | Pacific/Apia + 'WSST', # 0 | -1230 | Pacific/Apia + # 0 | +1300 | Pacific/Apia + 'XJT', # 0 | +0600 | Asia/Kashgar + # Asia/Urumqi + 'YAKST', # 1 | +0900 | Asia/Chita + # Asia/Khandyga + # Asia/Yakutsk + # 1 | +1000 | Asia/Chita + # Asia/Khandyga + # Asia/Yakutsk + 'YAKT', # 0 | +0800 | Asia/Chita + # Asia/Khandyga + # Asia/Ust-Nera + # Asia/Yakutsk + # 0 | +0900 | Asia/Chita + # Asia/Khandyga + # Asia/Ust-Nera + # Asia/Yakutsk + # 0 | +1000 | Asia/Chita + # Asia/Khandyga + # Asia/Yakutsk + 'YDDT', # 1 | -0700 | America/Dawson + # America/Whitehorse + # Canada/Yukon + 'YDT', # 1 | -0800 | America/Dawson + # America/Juneau + # America/Whitehorse + # America/Yakutat + # Canada/Yukon + 'YEKST', # 1 | +0600 | Asia/Yekaterinburg + 'YEKT', # 0 | +0500 | Asia/Yekaterinburg + # 0 | +0600 | Asia/Yekaterinburg + 'YERST', # 1 | +0400 | Asia/Yerevan + # 1 | +0500 | Asia/Yerevan + 'YERT', # 0 | +0300 | Asia/Yerevan + # 0 | +0400 | Asia/Yerevan + 'YPT', # 1 | -0800 | America/Dawson + # America/Whitehorse + # America/Yakutat + # Canada/Yukon + 'YST', # 0 | -0900 | America/Anchorage + # America/Dawson + # America/Juneau + # America/Nome + # America/Sitka + # America/Whitehorse + # America/Yakutat + # Canada/Yukon + # US/Alaska + 'YWT', # 1 | -0800 | America/Dawson + # America/Whitehorse + # America/Yakutat + # Canada/Yukon + 'zzz', # 0 | +0000 | America/Cambridge_Bay + # America/Inuvik + # America/Iqaluit + # America/Pangnirtung + # America/Rankin_Inlet + # America/Resolute + # America/Yellowknife + # Antarctica/Casey + # Antarctica/Davis + # Antarctica/DumontDUrville + # Antarctica/Macquarie + # Antarctica/Mawson + # Antarctica/Palmer + # Antarctica/Rothera + # Antarctica/Syowa + # Antarctica/Troll + # Antarctica/Vostok + # Indian/Kerguelen + ] + + class LocaleTime(object): """Stores and handles locale-specific information related to time. @@ -212,8 +2794,9 @@ 'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'), 'b': self.__seqToRE(self.locale_time.a_month[1:], 'b'), 'p': self.__seqToRE(self.locale_time.am_pm, 'p'), - 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone - for tz in tz_names), + 'Z': self.__seqToRE([tz for tz_names in self.locale_time.timezone + for tz in tz_names] + + _get_timezone_abbreviations(), 'Z'), '%': '%'}) base.__setitem__('W', base.__getitem__('U').replace('U', 'W')) @@ -307,8 +2890,9 @@ global _TimeRE_cache, _regex_cache with _cache_lock: - - if _getlang() != _TimeRE_cache.locale_time.lang: + if (_getlang() != _TimeRE_cache.locale_time.lang or + not any(tznames.intersection(time.tzname) + for tznames in _TimeRE_cache.locale_time.timezone)): _TimeRE_cache = TimeRE() _regex_cache.clear() if len(_regex_cache) > _CACHE_MAX_SIZE: @@ -494,18 +3078,53 @@ tt = _strptime(data_string, format)[0] return time.struct_time(tt[:time._STRUCT_TM_ITEMS]) -def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"): +def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y", + timezones=None): """Return a class cls instance based on the input string and the - format string.""" + format string. + + *timezones* is used by %Z directive + """ tt, fraction = _strptime(data_string, format) tzname, gmtoff = tt[-2:] + if tzname and timezones is None: # %Z matches non-empty string + warn("Default *timezones* parameter for " + "%s.strptime() will be True in Python 3.6. " + "Pass timezones=False to preserve the old behaviour" % ( + cls.__qualname__,), + category=DeprecationWarning, stacklevel=2) + args = tt[:6] + (fraction,) + tz = None if gmtoff is not None: tzdelta = datetime_timedelta(seconds=gmtoff) if tzname: tz = datetime_timezone(tzdelta, tzname) else: tz = datetime_timezone(tzdelta) + elif tzname and timezones: # find UTC offset based on tzname + try: + tz = timezones[tzname] # timezone or timedelta + except (TypeError, KeyError): + # either *timezones* is not a mapping or tzname is not in it; + # find UTC offset for UTC or the local timezone + abbr = tzname.lower() #NOTE: casefold() might match too much + if abbr in ('utc', 'gmt'): # UTC + tz = datetime_timedelta(0) + else: # get local UTC offset for the given time + #NOTE: mktime may fail during DST transitions (50%) + ts = time.mktime(tt[:6] + (-1,-1,-1)) + utc_dt = cls.fromtimestamp(ts, datetime_timezone.utc) + dt = utc_dt.astimezone() # local, may fail (in rare cases) + if dt.tzname().lower() == abbr: + tz = dt.utcoffset() + else: + raise ValueError("time data %r does not match format %r," + "timezones %r" % ( + data_string, format, timezones)) + if isinstance(tz, datetime_timedelta): + tz = datetime_timezone(tz, tzname) + + if tz is not None: args += (tz,) - return cls(*args) diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1588,10 +1588,20 @@ return self.isoformat(sep=' ') @classmethod - def strptime(cls, date_string, format): - 'string, format -> new datetime parsed from a string (like time.strptime()).' + def strptime(cls, string, format, *, timezones=None): + """Return new datetime parsed from a string (like time.strptime()). + + string + input date/time string + format + string that specifies a date/time format using %-directives + timezones + used by %Z directive. + + """ import _strptime - return _strptime._strptime_datetime(cls, date_string, format) + return _strptime._strptime_datetime(cls, string, format, + timezones=timezones) def utcoffset(self): """Return the timezone offset in minutes east of UTC (negative west of diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2,11 +2,12 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ - +import itertools import sys import pickle import random import unittest +import warnings from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod @@ -40,6 +41,12 @@ NAN = float("nan") +def _format_offset(offset): + # format utc offset as +HHMM, -HHMM + hours, minutes = divmod(offset, timedelta(minutes=60)) + return '{:+03d}{:02d}'.format(hours, minutes // timedelta(minutes=1)) + + ############################################################################# # module tests @@ -1887,19 +1894,19 @@ seconds = tzseconds hours, minutes = divmod(seconds//60, 60) dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname) - dt = strptime(dtstr, "%z %Z") + dt = strptime(dtstr, "%z %Z", timezones=False) self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds)) self.assertEqual(dt.tzname(), tzname) # Can produce inconsistent datetime dtstr, fmt = "+1234 UTC", "%z %Z" - dt = strptime(dtstr, fmt) + dt = strptime(dtstr, fmt, timezones=False) self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE) self.assertEqual(dt.tzname(), 'UTC') # yet will roundtrip self.assertEqual(dt.strftime(fmt), dtstr) # Produce naive datetime if no %z is provided - self.assertEqual(strptime("UTC", "%Z").tzinfo, None) + self.assertEqual(strptime("UTC", "%Z", timezones=False).tzinfo, None) with self.assertRaises(ValueError): strptime("-2400", "%z") with self.assertRaises(ValueError): strptime("-000", "%z") @@ -3399,6 +3406,76 @@ self.assertEqual(dt1.utcoffset(), dt2.utcoffset()) self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7) + def _test_strptime_timezones(self): + # test %Z and strptime()'s *timezones* parameter + for d, fmt, tzname, timezones in itertools.product( + # - test {summer time, winter time} + [self.theclass(2013, 6, 25), self.theclass(2013, 12, 25)], + # - parse {without %Z, with %Z, with %z, without %z} + ['', '%Z', '%Z%z', '%z'], + # - parse {UTC, CST, CDT} if it is not local timezone + # - parse {UTC, CST, CDT} if it is local timezone + ['UTC', 'CST', 'CDT'], + # - test timezones {None, False, True, tzname is in timezones, + # tzname is not in timezones} + [None, False, True, + {'UTC': timedelta(0)}, + {'CST': timedelta(hours=-5)}, + {'CDT': timedelta(hours=-4)}]): + fmt = '%Y-%m-%d %H:%M:%S' + fmt + offset = dict(UTC='+0000', CST='-0500', CDT='-0400')[tzname] + + # run tests + s = d.strftime(fmt) + tzname * ('%Z' in fmt) + offset * ('%z' in fmt) + for kwargs in [{}, {'timezones': timezones}]: + with self.subTest(s=s, fmt=fmt, kwargs=kwargs, d=d): + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + parsed = self.theclass.strptime(s, fmt, **kwargs) + except ValueError: # check error + # we are asked to get an aware datetime (%Z + # plus bool(timezones) is True) but there is + # not enough data: *timezones* mapping has no + # given tzname and it doesn't correspond to + # the local timezone name + self.assertIn('%Z', fmt) + tzs = kwargs.get('timezones') + self.assertTrue(tzs) + # either literally True or a mapping (test data) + self.assertTrue(tzs is True or tzname not in tzs) + # this works for test data + tt = _time.localtime(d.timestamp()) + localzone = getattr(tt, 'tm_zone') + self.assertNotEqual(tzname, localzone) + else: + if ('%z' in fmt or # check tzinfo + ('%Z' in fmt and kwargs.get('timezones'))): # aware + self.assertTrue(parsed.tzinfo is not None) + if '%Z' in fmt: # check tzname + self.assertEqual(parsed.tzname(), tzname) + # check utc offset + parsed_offset = _format_offset(parsed.utcoffset()) + self.assertEqual(parsed_offset, offset) + else: # naive + self.assertTrue(parsed.tzinfo is None) + # check datetime part + self.assertEqual(parsed.replace(tzinfo=None), d) + + @support.run_with_tz('UTC') + def test_strptime_timezones_utc(self): + self._test_strptime_timezones() + + @support.run_with_tz('CST+05CDT,M3.2.0,M11.1.0') + def test_strptime_timezones_cst(self): + self._test_strptime_timezones() + + def test_strptime_timezones_keyword_only(self): + # test that timezones is keyword-only + self.theclass.strptime('UTC', '%Z', timezones=True) # works + with self.assertRaises(TypeError): + self.theclass.strptime('UTC', '%Z', True) + # Pain to set up DST-aware tzinfo classes. def first_sunday_on_or_after(dt): diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -4232,24 +4232,75 @@ return result; } -/* Return new datetime from _strptime.strptime_datetime(). */ +/*[clinic input] + +@classmethod +datetime.datetime.strptime + + string: unicode + input date/time string + format: unicode + string that specifies a date/time format using %-directives + * + timezones: object = None + used by %Z directive. + +Return new datetime parsed from a string (like time.strptime()). +[clinic start generated code]*/ + +PyDoc_STRVAR(datetime_datetime_strptime__doc__, +"strptime($type, /, string, format, *, timezones=None)\n" +"--\n" +"\n" +"Return new datetime parsed from a string (like time.strptime()).\n" +"\n" +" string\n" +" input date/time string\n" +" format\n" +" string that specifies a date/time format using %-directives\n" +" timezones\n" +" used by %Z directive."); + +#define DATETIME_DATETIME_STRPTIME_METHODDEF \ + {"strptime", (PyCFunction)datetime_datetime_strptime, METH_VARARGS|METH_KEYWORDS|METH_CLASS, datetime_datetime_strptime__doc__}, + static PyObject * -datetime_strptime(PyObject *cls, PyObject *args) +datetime_datetime_strptime_impl(PyTypeObject *type, PyObject *string, PyObject *format, PyObject *timezones); + +static PyObject * +datetime_datetime_strptime(PyTypeObject *type, PyObject *args, PyObject *kwargs) { + PyObject *return_value = NULL; + static char *_keywords[] = {"string", "format", "timezones", NULL}; + PyObject *string; + PyObject *format; + PyObject *timezones = Py_None; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "UU|$O:strptime", _keywords, + &string, &format, &timezones)) + goto exit; + return_value = datetime_datetime_strptime_impl(type, string, format, timezones); + +exit: + return return_value; +} + +static PyObject * +datetime_datetime_strptime_impl(PyTypeObject *type, PyObject *string, PyObject *format, PyObject *timezones) +/*[clinic end generated code: output=724a52910415e9d0 input=5dad26250d56c2c3]*/ +{ + /* Return new datetime from _strptime.strptime_datetime(). */ static PyObject *module = NULL; - PyObject *string, *format; _Py_IDENTIFIER(_strptime_datetime); - if (!PyArg_ParseTuple(args, "UU:strptime", &string, &format)) - return NULL; - if (module == NULL) { module = PyImport_ImportModuleNoBlock("_strptime"); if (module == NULL) return NULL; } - return _PyObject_CallMethodId(module, &PyId__strptime_datetime, "OOO", - cls, string, format); + return _PyObject_CallMethodId(module, &PyId__strptime_datetime, "OOOO", + type, string, format, timezones); } /* Return new datetime from date/datetime and time arguments. */ @@ -5023,10 +5074,7 @@ PyDoc_STR("timestamp -> UTC datetime from a POSIX timestamp " "(like time.time()).")}, - {"strptime", (PyCFunction)datetime_strptime, - METH_VARARGS | METH_CLASS, - PyDoc_STR("string, format -> new datetime parsed from a string " - "(like time.strptime()).")}, + DATETIME_DATETIME_STRPTIME_METHODDEF {"combine", (PyCFunction)datetime_combine, METH_VARARGS | METH_KEYWORDS | METH_CLASS,