Update pytz to 2019.1

This commit is contained in:
JonnyWong16 2019-05-02 20:13:52 -07:00
parent eea1cf1d2c
commit e63606f195
44 changed files with 1514 additions and 738 deletions

View file

@ -1,19 +0,0 @@
Copyright (c) 2003-2018 Stuart Bishop <stuart@stuartbishop.net>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -1,587 +0,0 @@
pytz - World Timezone Definitions for Python
============================================
:Author: Stuart Bishop <stuart@stuartbishop.net>
Introduction
~~~~~~~~~~~~
pytz brings the Olson tz database into Python. This library allows
accurate and cross platform timezone calculations using Python 2.4
or higher. It also solves the issue of ambiguous times at the end
of daylight saving time, which you can read more about in the Python
Library Reference (``datetime.tzinfo``).
Almost all of the Olson timezones are supported.
.. note::
This library differs from the documented Python API for
tzinfo implementations; if you want to create local wallclock
times you need to use the ``localize()`` method documented in this
document. In addition, if you perform date arithmetic on local
times that cross DST boundaries, the result may be in an incorrect
timezone (ie. subtract 1 minute from 2002-10-27 1:00 EST and you get
2002-10-27 0:59 EST instead of the correct 2002-10-27 1:59 EDT). A
``normalize()`` method is provided to correct this. Unfortunately these
issues cannot be resolved without modifying the Python datetime
implementation (see PEP-431).
Installation
~~~~~~~~~~~~
This package can either be installed from a .egg file using setuptools,
or from the tarball using the standard Python distutils.
If you are installing from a tarball, run the following command as an
administrative user::
python setup.py install
If you are installing using setuptools, you don't even need to download
anything as the latest version will be downloaded for you
from the Python package index::
easy_install --upgrade pytz
If you already have the .egg file, you can use that too::
easy_install pytz-2008g-py2.6.egg
Example & Usage
~~~~~~~~~~~~~~~
Localized times and date arithmetic
-----------------------------------
>>> from datetime import datetime, timedelta
>>> from pytz import timezone
>>> import pytz
>>> utc = pytz.utc
>>> utc.zone
'UTC'
>>> eastern = timezone('US/Eastern')
>>> eastern.zone
'US/Eastern'
>>> amsterdam = timezone('Europe/Amsterdam')
>>> fmt = '%Y-%m-%d %H:%M:%S %Z%z'
This library only supports two ways of building a localized time. The
first is to use the ``localize()`` method provided by the pytz library.
This is used to localize a naive datetime (datetime with no timezone
information):
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500
The second way of building a localized time is by converting an existing
localized time using the standard ``astimezone()`` method:
>>> ams_dt = loc_dt.astimezone(amsterdam)
>>> ams_dt.strftime(fmt)
'2002-10-27 12:00:00 CET+0100'
Unfortunately using the tzinfo argument of the standard datetime
constructors ''does not work'' with pytz for many timezones.
>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) # /!\ Does not work this way!
'2002-10-27 12:00:00 LMT+0020'
It is safe for timezones without daylight saving transitions though, such
as UTC:
>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=pytz.utc).strftime(fmt) # /!\ Not recommended except for UTC
'2002-10-27 12:00:00 UTC+0000'
The preferred way of dealing with times is to always work in UTC,
converting to localtime only when generating output to be read
by humans.
>>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)
>>> loc_dt = utc_dt.astimezone(eastern)
>>> loc_dt.strftime(fmt)
'2002-10-27 01:00:00 EST-0500'
This library also allows you to do date arithmetic using local
times, although it is more complicated than working in UTC as you
need to use the ``normalize()`` method to handle daylight saving time
and other timezone transitions. In this example, ``loc_dt`` is set
to the instant when daylight saving time ends in the US/Eastern
timezone.
>>> before = loc_dt - timedelta(minutes=10)
>>> before.strftime(fmt)
'2002-10-27 00:50:00 EST-0500'
>>> eastern.normalize(before).strftime(fmt)
'2002-10-27 01:50:00 EDT-0400'
>>> after = eastern.normalize(before + timedelta(minutes=20))
>>> after.strftime(fmt)
'2002-10-27 01:10:00 EST-0500'
Creating local times is also tricky, and the reason why working with
local times is not recommended. Unfortunately, you cannot just pass
a ``tzinfo`` argument when constructing a datetime (see the next
section for more details)
>>> dt = datetime(2002, 10, 27, 1, 30, 0)
>>> dt1 = eastern.localize(dt, is_dst=True)
>>> dt1.strftime(fmt)
'2002-10-27 01:30:00 EDT-0400'
>>> dt2 = eastern.localize(dt, is_dst=False)
>>> dt2.strftime(fmt)
'2002-10-27 01:30:00 EST-0500'
Converting between timezones is more easily done, using the
standard astimezone method.
>>> utc_dt = utc.localize(datetime.utcfromtimestamp(1143408899))
>>> utc_dt.strftime(fmt)
'2006-03-26 21:34:59 UTC+0000'
>>> au_tz = timezone('Australia/Sydney')
>>> au_dt = utc_dt.astimezone(au_tz)
>>> au_dt.strftime(fmt)
'2006-03-27 08:34:59 AEDT+1100'
>>> utc_dt2 = au_dt.astimezone(utc)
>>> utc_dt2.strftime(fmt)
'2006-03-26 21:34:59 UTC+0000'
>>> utc_dt == utc_dt2
True
You can take shortcuts when dealing with the UTC side of timezone
conversions. ``normalize()`` and ``localize()`` are not really
necessary when there are no daylight saving time transitions to
deal with.
>>> utc_dt = datetime.utcfromtimestamp(1143408899).replace(tzinfo=utc)
>>> utc_dt.strftime(fmt)
'2006-03-26 21:34:59 UTC+0000'
>>> au_tz = timezone('Australia/Sydney')
>>> au_dt = au_tz.normalize(utc_dt.astimezone(au_tz))
>>> au_dt.strftime(fmt)
'2006-03-27 08:34:59 AEDT+1100'
>>> utc_dt2 = au_dt.astimezone(utc)
>>> utc_dt2.strftime(fmt)
'2006-03-26 21:34:59 UTC+0000'
``tzinfo`` API
--------------
The ``tzinfo`` instances returned by the ``timezone()`` function have
been extended to cope with ambiguous times by adding an ``is_dst``
parameter to the ``utcoffset()``, ``dst()`` && ``tzname()`` methods.
>>> tz = timezone('America/St_Johns')
>>> normal = datetime(2009, 9, 1)
>>> ambiguous = datetime(2009, 10, 31, 23, 30)
The ``is_dst`` parameter is ignored for most timestamps. It is only used
during DST transition ambiguous periods to resolve that ambiguity.
>>> tz.utcoffset(normal, is_dst=True)
datetime.timedelta(-1, 77400)
>>> tz.dst(normal, is_dst=True)
datetime.timedelta(0, 3600)
>>> tz.tzname(normal, is_dst=True)
'NDT'
>>> tz.utcoffset(ambiguous, is_dst=True)
datetime.timedelta(-1, 77400)
>>> tz.dst(ambiguous, is_dst=True)
datetime.timedelta(0, 3600)
>>> tz.tzname(ambiguous, is_dst=True)
'NDT'
>>> tz.utcoffset(normal, is_dst=False)
datetime.timedelta(-1, 77400)
>>> tz.dst(normal, is_dst=False)
datetime.timedelta(0, 3600)
>>> tz.tzname(normal, is_dst=False)
'NDT'
>>> tz.utcoffset(ambiguous, is_dst=False)
datetime.timedelta(-1, 73800)
>>> tz.dst(ambiguous, is_dst=False)
datetime.timedelta(0)
>>> tz.tzname(ambiguous, is_dst=False)
'NST'
If ``is_dst`` is not specified, ambiguous timestamps will raise
an ``pytz.exceptions.AmbiguousTimeError`` exception.
>>> tz.utcoffset(normal)
datetime.timedelta(-1, 77400)
>>> tz.dst(normal)
datetime.timedelta(0, 3600)
>>> tz.tzname(normal)
'NDT'
>>> import pytz.exceptions
>>> try:
... tz.utcoffset(ambiguous)
... except pytz.exceptions.AmbiguousTimeError:
... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)
pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00
>>> try:
... tz.dst(ambiguous)
... except pytz.exceptions.AmbiguousTimeError:
... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)
pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00
>>> try:
... tz.tzname(ambiguous)
... except pytz.exceptions.AmbiguousTimeError:
... print('pytz.exceptions.AmbiguousTimeError: %s' % ambiguous)
pytz.exceptions.AmbiguousTimeError: 2009-10-31 23:30:00
Problems with Localtime
~~~~~~~~~~~~~~~~~~~~~~~
The major problem we have to deal with is that certain datetimes
may occur twice in a year. For example, in the US/Eastern timezone
on the last Sunday morning in October, the following sequence
happens:
- 01:00 EDT occurs
- 1 hour later, instead of 2:00am the clock is turned back 1 hour
and 01:00 happens again (this time 01:00 EST)
In fact, every instant between 01:00 and 02:00 occurs twice. This means
that if you try and create a time in the 'US/Eastern' timezone
the standard datetime syntax, there is no way to specify if you meant
before of after the end-of-daylight-saving-time transition. Using the
pytz custom syntax, the best you can do is make an educated guess:
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 1, 30, 00))
>>> loc_dt.strftime(fmt)
'2002-10-27 01:30:00 EST-0500'
As you can see, the system has chosen one for you and there is a 50%
chance of it being out by one hour. For some applications, this does
not matter. However, if you are trying to schedule meetings with people
in different timezones or analyze log files it is not acceptable.
The best and simplest solution is to stick with using UTC. The pytz
package encourages using UTC for internal timezone representation by
including a special UTC implementation based on the standard Python
reference implementation in the Python documentation.
The UTC timezone unpickles to be the same instance, and pickles to a
smaller size than other pytz tzinfo instances. The UTC implementation
can be obtained as pytz.utc, pytz.UTC, or pytz.timezone('UTC').
>>> import pickle, pytz
>>> dt = datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc)
>>> naive = dt.replace(tzinfo=None)
>>> p = pickle.dumps(dt, 1)
>>> naive_p = pickle.dumps(naive, 1)
>>> len(p) - len(naive_p)
17
>>> new = pickle.loads(p)
>>> new == dt
True
>>> new is dt
False
>>> new.tzinfo is dt.tzinfo
True
>>> pytz.utc is pytz.UTC is pytz.timezone('UTC')
True
Note that some other timezones are commonly thought of as the same (GMT,
Greenwich, Universal, etc.). The definition of UTC is distinct from these
other timezones, and they are not equivalent. For this reason, they will
not compare the same in Python.
>>> utc == pytz.timezone('GMT')
False
See the section `What is UTC`_, below.
If you insist on working with local times, this library provides a
facility for constructing them unambiguously:
>>> loc_dt = datetime(2002, 10, 27, 1, 30, 00)
>>> est_dt = eastern.localize(loc_dt, is_dst=True)
>>> edt_dt = eastern.localize(loc_dt, is_dst=False)
>>> print(est_dt.strftime(fmt) + ' / ' + edt_dt.strftime(fmt))
2002-10-27 01:30:00 EDT-0400 / 2002-10-27 01:30:00 EST-0500
If you pass None as the is_dst flag to localize(), pytz will refuse to
guess and raise exceptions if you try to build ambiguous or non-existent
times.
For example, 1:30am on 27th Oct 2002 happened twice in the US/Eastern
timezone when the clocks where put back at the end of Daylight Saving
Time:
>>> dt = datetime(2002, 10, 27, 1, 30, 00)
>>> try:
... eastern.localize(dt, is_dst=None)
... except pytz.exceptions.AmbiguousTimeError:
... print('pytz.exceptions.AmbiguousTimeError: %s' % dt)
pytz.exceptions.AmbiguousTimeError: 2002-10-27 01:30:00
Similarly, 2:30am on 7th April 2002 never happened at all in the
US/Eastern timezone, as the clocks where put forward at 2:00am skipping
the entire hour:
>>> dt = datetime(2002, 4, 7, 2, 30, 00)
>>> try:
... eastern.localize(dt, is_dst=None)
... except pytz.exceptions.NonExistentTimeError:
... print('pytz.exceptions.NonExistentTimeError: %s' % dt)
pytz.exceptions.NonExistentTimeError: 2002-04-07 02:30:00
Both of these exceptions share a common base class to make error handling
easier:
>>> isinstance(pytz.AmbiguousTimeError(), pytz.InvalidTimeError)
True
>>> isinstance(pytz.NonExistentTimeError(), pytz.InvalidTimeError)
True
A special case is where countries change their timezone definitions
with no daylight savings time switch. For example, in 1915 Warsaw
switched from Warsaw time to Central European time with no daylight savings
transition. So at the stroke of midnight on August 5th 1915 the clocks
were wound back 24 minutes creating an ambiguous time period that cannot
be specified without referring to the timezone abbreviation or the
actual UTC offset. In this case midnight happened twice, neither time
during a daylight saving time period. pytz handles this transition by
treating the ambiguous period before the switch as daylight savings
time, and the ambiguous period after as standard time.
>>> warsaw = pytz.timezone('Europe/Warsaw')
>>> amb_dt1 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=True)
>>> amb_dt1.strftime(fmt)
'1915-08-04 23:59:59 WMT+0124'
>>> amb_dt2 = warsaw.localize(datetime(1915, 8, 4, 23, 59, 59), is_dst=False)
>>> amb_dt2.strftime(fmt)
'1915-08-04 23:59:59 CET+0100'
>>> switch_dt = warsaw.localize(datetime(1915, 8, 5, 00, 00, 00), is_dst=False)
>>> switch_dt.strftime(fmt)
'1915-08-05 00:00:00 CET+0100'
>>> str(switch_dt - amb_dt1)
'0:24:01'
>>> str(switch_dt - amb_dt2)
'0:00:01'
The best way of creating a time during an ambiguous time period is
by converting from another timezone such as UTC:
>>> utc_dt = datetime(1915, 8, 4, 22, 36, tzinfo=pytz.utc)
>>> utc_dt.astimezone(warsaw).strftime(fmt)
'1915-08-04 23:36:00 CET+0100'
The standard Python way of handling all these ambiguities is not to
handle them, such as demonstrated in this example using the US/Eastern
timezone definition from the Python documentation (Note that this
implementation only works for dates between 1987 and 2006 - it is
included for tests only!):
>>> from pytz.reference import Eastern # pytz.reference only for tests
>>> dt = datetime(2002, 10, 27, 0, 30, tzinfo=Eastern)
>>> str(dt)
'2002-10-27 00:30:00-04:00'
>>> str(dt + timedelta(hours=1))
'2002-10-27 01:30:00-05:00'
>>> str(dt + timedelta(hours=2))
'2002-10-27 02:30:00-05:00'
>>> str(dt + timedelta(hours=3))
'2002-10-27 03:30:00-05:00'
Notice the first two results? At first glance you might think they are
correct, but taking the UTC offset into account you find that they are
actually two hours appart instead of the 1 hour we asked for.
>>> from pytz.reference import UTC # pytz.reference only for tests
>>> str(dt.astimezone(UTC))
'2002-10-27 04:30:00+00:00'
>>> str((dt + timedelta(hours=1)).astimezone(UTC))
'2002-10-27 06:30:00+00:00'
Country Information
~~~~~~~~~~~~~~~~~~~
A mechanism is provided to access the timezones commonly in use
for a particular country, looked up using the ISO 3166 country code.
It returns a list of strings that can be used to retrieve the relevant
tzinfo instance using ``pytz.timezone()``:
>>> print(' '.join(pytz.country_timezones['nz']))
Pacific/Auckland Pacific/Chatham
The Olson database comes with a ISO 3166 country code to English country
name mapping that pytz exposes as a dictionary:
>>> print(pytz.country_names['nz'])
New Zealand
What is UTC
~~~~~~~~~~~
'UTC' is `Coordinated Universal Time`_. It is a successor to, but distinct
from, Greenwich Mean Time (GMT) and the various definitions of Universal
Time. UTC is now the worldwide standard for regulating clocks and time
measurement.
All other timezones are defined relative to UTC, and include offsets like
UTC+0800 - hours to add or subtract from UTC to derive the local time. No
daylight saving time occurs in UTC, making it a useful timezone to perform
date arithmetic without worrying about the confusion and ambiguities caused
by daylight saving time transitions, your country changing its timezone, or
mobile computers that roam through multiple timezones.
.. _Coordinated Universal Time: https://en.wikipedia.org/wiki/Coordinated_Universal_Time
Helpers
~~~~~~~
There are two lists of timezones provided.
``all_timezones`` is the exhaustive list of the timezone names that can
be used.
>>> from pytz import all_timezones
>>> len(all_timezones) >= 500
True
>>> 'Etc/Greenwich' in all_timezones
True
``common_timezones`` is a list of useful, current timezones. It doesn't
contain deprecated zones or historical zones, except for a few I've
deemed in common usage, such as US/Eastern (open a bug report if you
think other timezones are deserving of being included here). It is also
a sequence of strings.
>>> from pytz import common_timezones
>>> len(common_timezones) < len(all_timezones)
True
>>> 'Etc/Greenwich' in common_timezones
False
>>> 'Australia/Melbourne' in common_timezones
True
>>> 'US/Eastern' in common_timezones
True
>>> 'Canada/Eastern' in common_timezones
True
>>> 'Australia/Yancowinna' in all_timezones
True
>>> 'Australia/Yancowinna' in common_timezones
False
Both ``common_timezones`` and ``all_timezones`` are alphabetically
sorted:
>>> common_timezones_dupe = common_timezones[:]
>>> common_timezones_dupe.sort()
>>> common_timezones == common_timezones_dupe
True
>>> all_timezones_dupe = all_timezones[:]
>>> all_timezones_dupe.sort()
>>> all_timezones == all_timezones_dupe
True
``all_timezones`` and ``common_timezones`` are also available as sets.
>>> from pytz import all_timezones_set, common_timezones_set
>>> 'US/Eastern' in all_timezones_set
True
>>> 'US/Eastern' in common_timezones_set
True
>>> 'Australia/Victoria' in common_timezones_set
False
You can also retrieve lists of timezones used by particular countries
using the ``country_timezones()`` function. It requires an ISO-3166
two letter country code.
>>> from pytz import country_timezones
>>> print(' '.join(country_timezones('ch')))
Europe/Zurich
>>> print(' '.join(country_timezones('CH')))
Europe/Zurich
Internationalization - i18n/l10n
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pytz is an interface to the IANA database, which uses ASCII names. The `Unicode Consortium's Unicode Locales (CLDR) <http://cldr.unicode.org>`_
project provides translations. Thomas Khyn's
`l18n <https://pypi.org/project/l18n/>`_ package can be used to access
these translations from Python.
License
~~~~~~~
MIT license.
This code is also available as part of Zope 3 under the Zope Public
License, Version 2.1 (ZPL).
I'm happy to relicense this code if necessary for inclusion in other
open source projects.
Latest Versions
~~~~~~~~~~~~~~~
This package will be updated after releases of the Olson timezone
database. The latest version can be downloaded from the `Python Package
Index <https://pypi.org/project/pytz/>`_. The code that is used
to generate this distribution is hosted on launchpad.net and available
using git::
git clone https://git.launchpad.net/pytz
A mirror on github is also available at https://github.com/stub42/pytz
Announcements of new releases are made on
`Launchpad <https://launchpad.net/pytz>`_, and the
`Atom feed <http://feeds.launchpad.net/pytz/announcements.atom>`_
hosted there.
Bugs, Feature Requests & Patches
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bugs can be reported using `Launchpad <https://bugs.launchpad.net/pytz>`__.
Issues & Limitations
~~~~~~~~~~~~~~~~~~~~
- Offsets from UTC are rounded to the nearest whole minute, so timezones
such as Europe/Amsterdam pre 1937 will be up to 30 seconds out. This
is a limitation of the Python datetime library.
- If you think a timezone definition is incorrect, I probably can't fix
it. pytz is a direct translation of the Olson timezone database, and
changes to the timezone definitions need to be made to this source.
If you find errors they should be reported to the time zone mailing
list, linked from http://www.iana.org/time-zones.
Further Reading
~~~~~~~~~~~~~~~
More info than you want to know about timezones:
http://www.twinsun.com/tz/tz-link.htm
Contact
~~~~~~~
Stuart Bishop <stuart@stuartbishop.net>

View file

@ -16,14 +16,14 @@ from pytz.exceptions import AmbiguousTimeError
from pytz.exceptions import InvalidTimeError
from pytz.exceptions import NonExistentTimeError
from pytz.exceptions import UnknownTimeZoneError
from pytz.lazy import LazyDict, LazyList, LazySet
from pytz.lazy import LazyDict, LazyList, LazySet # noqa
from pytz.tzinfo import unpickler, BaseTzInfo
from pytz.tzfile import build_tzinfo
# The IANA (nee Olson) database is updated several times a year.
OLSON_VERSION = '2018f'
VERSION = '2018.6' # pip compatible version number.
OLSON_VERSION = '2019a'
VERSION = '2019.1' # pip compatible version number.
__version__ = VERSION
OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling
@ -157,6 +157,9 @@ def timezone(zone):
Unknown
'''
if zone is None:
raise UnknownTimeZoneError(None)
if zone.upper() == 'UTC':
return utc
@ -166,9 +169,9 @@ def timezone(zone):
# All valid timezones are ASCII
raise UnknownTimeZoneError(zone)
zone = _unmunge_zone(zone)
zone = _case_insensitive_zone_lookup(_unmunge_zone(zone))
if zone not in _tzinfo_cache:
if zone in all_timezones_set:
if zone in all_timezones_set: # noqa
fp = open_resource(zone)
try:
_tzinfo_cache[zone] = build_tzinfo(zone, fp)
@ -185,6 +188,11 @@ def _unmunge_zone(zone):
return zone.replace('_plus_', '+').replace('_minus_', '-')
def _case_insensitive_zone_lookup(zone):
"""case-insensitively matching timezone, else return zone unchanged"""
return _all_timezones_lower_to_standard.get(zone.lower()) or zone # noqa
ZERO = datetime.timedelta(0)
HOUR = datetime.timedelta(hours=1)
@ -272,6 +280,8 @@ def _UTC():
False
"""
return utc
_UTC.__safe_for_unpickling__ = True
@ -282,6 +292,8 @@ def _p(*args):
by shortening the path.
"""
return unpickler(*args)
_p.__safe_for_unpickling__ = True
@ -330,7 +342,7 @@ class _CountryTimezoneDict(LazyDict):
if line.startswith('#'):
continue
code, coordinates, zone = line.split(None, 4)[:3]
if zone not in all_timezones_set:
if zone not in all_timezones_set: # noqa
continue
try:
data[code].append(zone)
@ -340,6 +352,7 @@ class _CountryTimezoneDict(LazyDict):
finally:
zone_tab.close()
country_timezones = _CountryTimezoneDict()
@ -363,6 +376,7 @@ class _CountryNameDict(LazyDict):
finally:
zone_tab.close()
country_names = _CountryNameDict()
@ -474,6 +488,7 @@ def FixedOffset(offset, _tzinfos={}):
return info
FixedOffset.__safe_for_unpickling__ = True
@ -483,6 +498,7 @@ def _test():
import pytz
return doctest.testmod(pytz)
if __name__ == '__main__':
_test()
all_timezones = \
@ -787,6 +803,7 @@ all_timezones = \
'Asia/Pontianak',
'Asia/Pyongyang',
'Asia/Qatar',
'Asia/Qostanay',
'Asia/Qyzylorda',
'Asia/Rangoon',
'Asia/Riyadh',
@ -1081,6 +1098,7 @@ all_timezones = LazyList(
tz for tz in all_timezones if resource_exists(tz))
all_timezones_set = LazySet(all_timezones)
_all_timezones_lower_to_standard = dict((tz.lower(), tz) for tz in all_timezones)
common_timezones = \
['Africa/Abidjan',
'Africa/Accra',
@ -1351,6 +1369,7 @@ common_timezones = \
'Asia/Pontianak',
'Asia/Pyongyang',
'Asia/Qatar',
'Asia/Qostanay',
'Asia/Qyzylorda',
'Asia/Riyadh',
'Asia/Sakhalin',

View file

@ -0,0 +1,34 @@
# -*- coding: ascii -*-
from doctest import DocFileSuite
import unittest
import os.path
import sys
THIS_DIR = os.path.dirname(__file__)
README = os.path.join(THIS_DIR, os.pardir, os.pardir, 'README.txt')
class DocumentationTestCase(unittest.TestCase):
def test_readme_encoding(self):
'''Confirm the README.txt is pure ASCII.'''
f = open(README, 'rb')
try:
f.read().decode('ASCII')
finally:
f.close()
def test_suite():
"For the Z3 test runner"
return unittest.TestSuite((
DocumentationTestCase('test_readme_encoding'),
DocFileSuite(os.path.join(os.pardir, os.pardir, 'README.txt'))))
if __name__ == '__main__':
sys.path.insert(
0, os.path.abspath(os.path.join(THIS_DIR, os.pardir, os.pardir))
)
unittest.main(defaultTest='test_suite')

315
lib/pytz/tests/test_lazy.py Normal file
View file

@ -0,0 +1,315 @@
from operator import (
eq, ge, gt, le, lt, ne, add, concat, not_, sub, and_, or_, xor
)
import os.path
import sys
import unittest
import warnings
if __name__ == '__main__':
# Only munge path if invoked as a script. Testrunners should have setup
# the paths already
sys.path.insert(0, os.path.abspath(os.path.join(os.pardir, os.pardir)))
from pytz.lazy import LazyList, LazySet
class LazyListTestCase(unittest.TestCase):
initial_data = [3, 2, 1]
def setUp(self):
self.base = [3, 2, 1]
self.lesser = [2, 1, 0]
self.greater = [4, 3, 2]
self.lazy = LazyList(iter(list(self.base)))
def test_unary_ops(self):
unary_ops = [str, repr, len, bool, not_]
try:
unary_ops.append(unicode)
except NameError:
pass # unicode no longer exists in Python 3.
for op in unary_ops:
self.assertEqual(
op(self.lazy),
op(self.base), str(op))
def test_binary_ops(self):
binary_ops = [eq, ge, gt, le, lt, ne, add, concat]
try:
binary_ops.append(cmp)
except NameError:
pass # cmp no longer exists in Python 3.
for op in binary_ops:
self.assertEqual(
op(self.lazy, self.lazy),
op(self.base, self.base), str(op))
for other in [self.base, self.lesser, self.greater]:
self.assertEqual(
op(self.lazy, other),
op(self.base, other), '%s %s' % (op, other))
self.assertEqual(
op(other, self.lazy),
op(other, self.base), '%s %s' % (op, other))
# Multiplication
self.assertEqual(self.lazy * 3, self.base * 3)
self.assertEqual(3 * self.lazy, 3 * self.base)
# Contains
self.assertTrue(2 in self.lazy)
self.assertFalse(42 in self.lazy)
def test_iadd(self):
self.lazy += [1]
self.base += [1]
self.assertEqual(self.lazy, self.base)
def test_bool(self):
self.assertTrue(bool(self.lazy))
self.assertFalse(bool(LazyList()))
self.assertFalse(bool(LazyList(iter([]))))
def test_hash(self):
self.assertRaises(TypeError, hash, self.lazy)
def test_isinstance(self):
self.assertTrue(isinstance(self.lazy, list))
self.assertFalse(isinstance(self.lazy, tuple))
def test_callable(self):
try:
callable
except NameError:
return # No longer exists with Python 3.
self.assertFalse(callable(self.lazy))
def test_append(self):
self.base.append('extra')
self.lazy.append('extra')
self.assertEqual(self.lazy, self.base)
def test_count(self):
self.assertEqual(self.lazy.count(2), 1)
def test_index(self):
self.assertEqual(self.lazy.index(2), 1)
def test_extend(self):
self.base.extend([6, 7])
self.lazy.extend([6, 7])
self.assertEqual(self.lazy, self.base)
def test_insert(self):
self.base.insert(0, 'ping')
self.lazy.insert(0, 'ping')
self.assertEqual(self.lazy, self.base)
def test_pop(self):
self.assertEqual(self.lazy.pop(), self.base.pop())
self.assertEqual(self.lazy, self.base)
def test_remove(self):
self.base.remove(2)
self.lazy.remove(2)
self.assertEqual(self.lazy, self.base)
def test_reverse(self):
self.base.reverse()
self.lazy.reverse()
self.assertEqual(self.lazy, self.base)
def test_reversed(self):
self.assertEqual(list(reversed(self.lazy)), list(reversed(self.base)))
def test_sort(self):
self.base.sort()
self.assertNotEqual(self.lazy, self.base, 'Test data already sorted')
self.lazy.sort()
self.assertEqual(self.lazy, self.base)
def test_sorted(self):
self.assertEqual(sorted(self.lazy), sorted(self.base))
def test_getitem(self):
for idx in range(-len(self.base), len(self.base)):
self.assertEqual(self.lazy[idx], self.base[idx])
def test_setitem(self):
for idx in range(-len(self.base), len(self.base)):
self.base[idx] = idx + 1000
self.assertNotEqual(self.lazy, self.base)
self.lazy[idx] = idx + 1000
self.assertEqual(self.lazy, self.base)
def test_delitem(self):
del self.base[0]
self.assertNotEqual(self.lazy, self.base)
del self.lazy[0]
self.assertEqual(self.lazy, self.base)
del self.base[-2]
self.assertNotEqual(self.lazy, self.base)
del self.lazy[-2]
self.assertEqual(self.lazy, self.base)
def test_iter(self):
self.assertEqual(list(iter(self.lazy)), list(iter(self.base)))
def test_getslice(self):
for i in range(-len(self.base), len(self.base)):
for j in range(-len(self.base), len(self.base)):
for step in [-1, 1]:
self.assertEqual(self.lazy[i:j:step], self.base[i:j:step])
def test_setslice(self):
for i in range(-len(self.base), len(self.base)):
for j in range(-len(self.base), len(self.base)):
for step in [-1, 1]:
replacement = range(0, len(self.base[i:j:step]))
self.base[i:j:step] = replacement
self.lazy[i:j:step] = replacement
self.assertEqual(self.lazy, self.base)
def test_delslice(self):
del self.base[0:1]
del self.lazy[0:1]
self.assertEqual(self.lazy, self.base)
del self.base[-1:1:-1]
del self.lazy[-1:1:-1]
self.assertEqual(self.lazy, self.base)
class LazySetTestCase(unittest.TestCase):
initial_data = set([3, 2, 1])
def setUp(self):
self.base = set([3, 2, 1])
self.lazy = LazySet(iter(set(self.base)))
def test_unary_ops(self):
# These ops just need to work.
unary_ops = [str, repr]
try:
unary_ops.append(unicode)
except NameError:
pass # unicode no longer exists in Python 3.
for op in unary_ops:
op(self.lazy) # These ops just need to work.
# These ops should return identical values as a real set.
unary_ops = [len, bool, not_]
for op in unary_ops:
self.assertEqual(
op(self.lazy),
op(self.base), '%s(lazy) == %r' % (op, op(self.lazy)))
def test_binary_ops(self):
binary_ops = [eq, ge, gt, le, lt, ne, sub, and_, or_, xor]
try:
binary_ops.append(cmp)
except NameError:
pass # cmp no longer exists in Python 3.
for op in binary_ops:
self.assertEqual(
op(self.lazy, self.lazy),
op(self.base, self.base), str(op))
self.assertEqual(
op(self.lazy, self.base),
op(self.base, self.base), str(op))
self.assertEqual(
op(self.base, self.lazy),
op(self.base, self.base), str(op))
# Contains
self.assertTrue(2 in self.lazy)
self.assertFalse(42 in self.lazy)
def test_iops(self):
try:
iops = [isub, iand, ior, ixor]
except NameError:
return # Don't exist in older Python versions.
for op in iops:
# Mutating operators, so make fresh copies.
lazy = LazySet(self.base)
base = self.base.copy()
op(lazy, set([1]))
op(base, set([1]))
self.assertEqual(lazy, base, str(op))
def test_bool(self):
self.assertTrue(bool(self.lazy))
self.assertFalse(bool(LazySet()))
self.assertFalse(bool(LazySet(iter([]))))
def test_hash(self):
self.assertRaises(TypeError, hash, self.lazy)
def test_isinstance(self):
self.assertTrue(isinstance(self.lazy, set))
def test_callable(self):
try:
callable
except NameError:
return # No longer exists with Python 3.
self.assertFalse(callable(self.lazy))
def test_add(self):
self.base.add('extra')
self.lazy.add('extra')
self.assertEqual(self.lazy, self.base)
def test_copy(self):
self.assertEqual(self.lazy.copy(), self.base)
def test_method_ops(self):
ops = [
'difference', 'intersection', 'isdisjoint',
'issubset', 'issuperset', 'symmetric_difference', 'union',
'difference_update', 'intersection_update',
'symmetric_difference_update', 'update']
for op in ops:
if not hasattr(set, op):
continue # Not in this version of Python.
# Make a copy, as some of the ops are mutating.
lazy = LazySet(set(self.base))
base = set(self.base)
self.assertEqual(
getattr(lazy, op)(set([1])),
getattr(base, op)(set([1])), op)
self.assertEqual(lazy, base, op)
def test_discard(self):
self.base.discard(1)
self.assertNotEqual(self.lazy, self.base)
self.lazy.discard(1)
self.assertEqual(self.lazy, self.base)
def test_pop(self):
self.assertEqual(self.lazy.pop(), self.base.pop())
self.assertEqual(self.lazy, self.base)
def test_remove(self):
self.base.remove(2)
self.lazy.remove(2)
self.assertEqual(self.lazy, self.base)
def test_clear(self):
self.lazy.clear()
self.assertEqual(self.lazy, set())
if __name__ == '__main__':
warnings.simplefilter("error") # Warnings should be fatal in tests.
unittest.main()

View file

@ -0,0 +1,863 @@
# -*- coding: ascii -*-
import doctest
import sys
import os
import os.path
import unittest
try:
import cPickle as pickle
except ImportError:
import pickle
from datetime import (
datetime,
timedelta
)
import warnings
if __name__ == '__main__':
# Only munge path if invoked as a script. Testrunners should have setup
# the paths already
sys.path.insert(0, os.path.abspath(os.path.join(os.pardir, os.pardir)))
import pytz # noqa
from pytz import reference # noqa
from pytz.tzfile import _byte_string # noqa
from pytz.tzinfo import DstTzInfo, StaticTzInfo # noqa
# I test for expected version to ensure the correct version of pytz is
# actually being tested.
EXPECTED_VERSION = '2019.1'
EXPECTED_OLSON_VERSION = '2019a'
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
NOTIME = timedelta(0)
# GMT is a tzinfo.StaticTzInfo--the class we primarily want to test--while
# UTC is reference implementation. They both have the same timezone meaning.
UTC = pytz.timezone('UTC')
GMT = pytz.timezone('GMT')
assert isinstance(GMT, StaticTzInfo), 'GMT is no longer a StaticTzInfo'
def prettydt(dt):
"""datetime as a string using a known format.
We don't use strftime as it doesn't handle years earlier than 1900
per http://bugs.python.org/issue1777412
"""
if dt.utcoffset() >= timedelta(0):
offset = '+%s' % (dt.utcoffset(),)
else:
offset = '-%s' % (-1 * dt.utcoffset(),)
return '%04d-%02d-%02d %02d:%02d:%02d %s %s' % (
dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.tzname(), offset)
if sys.version_info[0] > 2:
# Python 3.x doesn't have unicode(), making writing code
# for Python 2.3 and Python 3.x a pain.
unicode = str
class BasicTest(unittest.TestCase):
def testVersion(self):
# Ensuring the correct version of pytz has been loaded
self.assertEqual(
EXPECTED_VERSION, pytz.__version__,
'Incorrect pytz version loaded. Import path is stuffed '
'or this test needs updating. (Wanted %s, got %s)'
% (EXPECTED_VERSION, pytz.__version__)
)
self.assertEqual(
EXPECTED_OLSON_VERSION, pytz.OLSON_VERSION,
'Incorrect pytz version loaded. Import path is stuffed '
'or this test needs updating. (Wanted %s, got %s)'
% (EXPECTED_OLSON_VERSION, pytz.OLSON_VERSION)
)
def testGMT(self):
now = datetime.now(tz=GMT)
self.assertTrue(now.utcoffset() == NOTIME)
self.assertTrue(now.dst() == NOTIME)
self.assertTrue(now.timetuple() == now.utctimetuple())
self.assertTrue(now == now.replace(tzinfo=UTC))
def testReferenceUTC(self):
now = datetime.now(tz=UTC)
self.assertTrue(now.utcoffset() == NOTIME)
self.assertTrue(now.dst() == NOTIME)
self.assertTrue(now.timetuple() == now.utctimetuple())
def testUnknownOffsets(self):
# This tzinfo behavior is required to make
# datetime.time.{utcoffset, dst, tzname} work as documented.
dst_tz = pytz.timezone('US/Eastern')
# This information is not known when we don't have a date,
# so return None per API.
self.assertTrue(dst_tz.utcoffset(None) is None)
self.assertTrue(dst_tz.dst(None) is None)
# We don't know the abbreviation, but this is still a valid
# tzname per the Python documentation.
self.assertEqual(dst_tz.tzname(None), 'US/Eastern')
def clearCache(self):
pytz._tzinfo_cache.clear()
def testUnicodeTimezone(self):
# We need to ensure that cold lookups work for both Unicode
# and traditional strings, and that the desired singleton is
# returned.
self.clearCache()
eastern = pytz.timezone(unicode('US/Eastern'))
self.assertTrue(eastern is pytz.timezone('US/Eastern'))
self.clearCache()
eastern = pytz.timezone('US/Eastern')
self.assertTrue(eastern is pytz.timezone(unicode('US/Eastern')))
def testStaticTzInfo(self):
# Ensure that static timezones are correctly detected,
# per lp:1602807
static = pytz.timezone('Etc/GMT-4')
self.assertTrue(isinstance(static, StaticTzInfo))
class PicklingTest(unittest.TestCase):
def _roundtrip_tzinfo(self, tz):
p = pickle.dumps(tz)
unpickled_tz = pickle.loads(p)
self.assertTrue(tz is unpickled_tz, '%s did not roundtrip' % tz.zone)
def _roundtrip_datetime(self, dt):
# Ensure that the tzinfo attached to a datetime instance
# is identical to the one returned. This is important for
# DST timezones, as some state is stored in the tzinfo.
tz = dt.tzinfo
p = pickle.dumps(dt)
unpickled_dt = pickle.loads(p)
unpickled_tz = unpickled_dt.tzinfo
self.assertTrue(tz is unpickled_tz, '%s did not roundtrip' % tz.zone)
def testDst(self):
tz = pytz.timezone('Europe/Amsterdam')
dt = datetime(2004, 2, 1, 0, 0, 0)
for localized_tz in tz._tzinfos.values():
self._roundtrip_tzinfo(localized_tz)
self._roundtrip_datetime(dt.replace(tzinfo=localized_tz))
def testRoundtrip(self):
for zone in pytz.all_timezones:
tz = pytz.timezone(zone)
self._roundtrip_tzinfo(tz)
def testDatabaseFixes(self):
# Hack the pickle to make it refer to a timezone abbreviation
# that does not match anything. The unpickler should be able
# to repair this case
tz = pytz.timezone('Australia/Melbourne')
p = pickle.dumps(tz)
tzname = tz._tzname
hacked_p = p.replace(
_byte_string(tzname),
_byte_string('?' * len(tzname))
)
self.assertNotEqual(p, hacked_p)
unpickled_tz = pickle.loads(hacked_p)
self.assertTrue(tz is unpickled_tz)
# Simulate a database correction. In this case, the incorrect
# data will continue to be used.
p = pickle.dumps(tz)
new_utcoffset = tz._utcoffset.seconds + 42
# Python 3 introduced a new pickle protocol where numbers are stored in
# hexadecimal representation. Here we extract the pickle
# representation of the number for the current Python version.
old_pickle_pattern = pickle.dumps(tz._utcoffset.seconds)[3:-1]
new_pickle_pattern = pickle.dumps(new_utcoffset)[3:-1]
hacked_p = p.replace(old_pickle_pattern, new_pickle_pattern)
self.assertNotEqual(p, hacked_p)
unpickled_tz = pickle.loads(hacked_p)
self.assertEqual(unpickled_tz._utcoffset.seconds, new_utcoffset)
self.assertTrue(tz is not unpickled_tz)
def testOldPickles(self):
# Ensure that applications serializing pytz instances as pickles
# have no troubles upgrading to a new pytz release. These pickles
# where created with pytz2006j
east1 = pickle.loads(
_byte_string(
"cpytz\n_p\np1\n(S'US/Eastern'\np2\nI-18000\n"
"I0\nS'EST'\np3\ntRp4\n."
)
)
east2 = pytz.timezone('US/Eastern').localize(
datetime(2006, 1, 1)).tzinfo
self.assertTrue(east1 is east2)
# Confirm changes in name munging between 2006j and 2007c cause
# no problems.
pap1 = pickle.loads(_byte_string(
"cpytz\n_p\np1\n(S'America/Port_minus_au_minus_Prince'"
"\np2\nI-17340\nI0\nS'PPMT'\np3\ntRp4\n."))
pap2 = pytz.timezone('America/Port-au-Prince').localize(
datetime(1910, 1, 1)).tzinfo
self.assertTrue(pap1 is pap2)
gmt1 = pickle.loads(_byte_string(
"cpytz\n_p\np1\n(S'Etc/GMT_plus_10'\np2\ntRp3\n."))
gmt2 = pytz.timezone('Etc/GMT+10')
self.assertTrue(gmt1 is gmt2)
class USEasternDSTStartTestCase(unittest.TestCase):
tzinfo = pytz.timezone('US/Eastern')
# 24 hours before DST changeover
transition_time = datetime(2002, 4, 7, 7, 0, 0, tzinfo=UTC)
# Increase for 'flexible' DST transitions due to 1 minute granularity
# of Python's datetime library
instant = timedelta(seconds=1)
# before transition
before = {
'tzname': 'EST',
'utcoffset': timedelta(hours=-5),
'dst': timedelta(hours=0),
}
# after transition
after = {
'tzname': 'EDT',
'utcoffset': timedelta(hours=-4),
'dst': timedelta(hours=1),
}
def _test_tzname(self, utc_dt, wanted):
tzname = wanted['tzname']
dt = utc_dt.astimezone(self.tzinfo)
self.assertEqual(
dt.tzname(), tzname,
'Expected %s as tzname for %s. Got %s' % (
tzname, str(utc_dt), dt.tzname()
)
)
def _test_utcoffset(self, utc_dt, wanted):
utcoffset = wanted['utcoffset']
dt = utc_dt.astimezone(self.tzinfo)
self.assertEqual(
dt.utcoffset(), wanted['utcoffset'],
'Expected %s as utcoffset for %s. Got %s' % (
utcoffset, utc_dt, dt.utcoffset()
)
)
def _test_dst(self, utc_dt, wanted):
dst = wanted['dst']
dt = utc_dt.astimezone(self.tzinfo)
self.assertEqual(
dt.dst(), dst,
'Expected %s as dst for %s. Got %s' % (dst, utc_dt, dt.dst())
)
def test_arithmetic(self):
utc_dt = self.transition_time
for days in range(-420, 720, 20):
delta = timedelta(days=days)
# Make sure we can get back where we started
dt = utc_dt.astimezone(self.tzinfo)
dt2 = dt + delta
dt2 = dt2 - delta
self.assertEqual(dt, dt2)
# Make sure arithmetic crossing DST boundaries ends
# up in the correct timezone after normalization
utc_plus_delta = (utc_dt + delta).astimezone(self.tzinfo)
local_plus_delta = self.tzinfo.normalize(dt + delta)
self.assertEqual(
prettydt(utc_plus_delta), prettydt(local_plus_delta),
'Incorrect result for delta==%d days. Wanted %r. Got %r' % (
days, prettydt(utc_plus_delta), prettydt(local_plus_delta),
)
)
def _test_all(self, utc_dt, wanted):
self._test_utcoffset(utc_dt, wanted)
self._test_tzname(utc_dt, wanted)
self._test_dst(utc_dt, wanted)
def testDayBefore(self):
self._test_all(
self.transition_time - timedelta(days=1), self.before
)
def testTwoHoursBefore(self):
self._test_all(
self.transition_time - timedelta(hours=2), self.before
)
def testHourBefore(self):
self._test_all(
self.transition_time - timedelta(hours=1), self.before
)
def testInstantBefore(self):
self._test_all(
self.transition_time - self.instant, self.before
)
def testTransition(self):
self._test_all(
self.transition_time, self.after
)
def testInstantAfter(self):
self._test_all(
self.transition_time + self.instant, self.after
)
def testHourAfter(self):
self._test_all(
self.transition_time + timedelta(hours=1), self.after
)
def testTwoHoursAfter(self):
self._test_all(
self.transition_time + timedelta(hours=1), self.after
)
def testDayAfter(self):
self._test_all(
self.transition_time + timedelta(days=1), self.after
)
class USEasternDSTEndTestCase(USEasternDSTStartTestCase):
tzinfo = pytz.timezone('US/Eastern')
transition_time = datetime(2002, 10, 27, 6, 0, 0, tzinfo=UTC)
before = {
'tzname': 'EDT',
'utcoffset': timedelta(hours=-4),
'dst': timedelta(hours=1),
}
after = {
'tzname': 'EST',
'utcoffset': timedelta(hours=-5),
'dst': timedelta(hours=0),
}
class USEasternEPTStartTestCase(USEasternDSTStartTestCase):
transition_time = datetime(1945, 8, 14, 23, 0, 0, tzinfo=UTC)
before = {
'tzname': 'EWT',
'utcoffset': timedelta(hours=-4),
'dst': timedelta(hours=1),
}
after = {
'tzname': 'EPT',
'utcoffset': timedelta(hours=-4),
'dst': timedelta(hours=1),
}
class USEasternEPTEndTestCase(USEasternDSTStartTestCase):
transition_time = datetime(1945, 9, 30, 6, 0, 0, tzinfo=UTC)
before = {
'tzname': 'EPT',
'utcoffset': timedelta(hours=-4),
'dst': timedelta(hours=1),
}
after = {
'tzname': 'EST',
'utcoffset': timedelta(hours=-5),
'dst': timedelta(hours=0),
}
class WarsawWMTEndTestCase(USEasternDSTStartTestCase):
# In 1915, Warsaw changed from Warsaw to Central European time.
# This involved the clocks being set backwards, causing a end-of-DST
# like situation without DST being involved.
tzinfo = pytz.timezone('Europe/Warsaw')
transition_time = datetime(1915, 8, 4, 22, 36, 0, tzinfo=UTC)
before = {
'tzname': 'WMT',
'utcoffset': timedelta(hours=1, minutes=24),
'dst': timedelta(0),
}
after = {
'tzname': 'CET',
'utcoffset': timedelta(hours=1),
'dst': timedelta(0),
}
class VilniusWMTEndTestCase(USEasternDSTStartTestCase):
# At the end of 1916, Vilnius changed timezones putting its clock
# forward by 11 minutes 35 seconds. Neither timezone was in DST mode.
tzinfo = pytz.timezone('Europe/Vilnius')
instant = timedelta(seconds=31)
transition_time = datetime(1916, 12, 31, 22, 36, 00, tzinfo=UTC)
before = {
'tzname': 'WMT',
'utcoffset': timedelta(hours=1, minutes=24),
'dst': timedelta(0),
}
after = {
'tzname': 'KMT',
'utcoffset': timedelta(hours=1, minutes=36), # Really 1:35:36
'dst': timedelta(0),
}
class VilniusCESTStartTestCase(USEasternDSTStartTestCase):
# In 1941, Vilnius changed from MSG to CEST, switching to summer
# time while simultaneously reducing its UTC offset by two hours,
# causing the clocks to go backwards for this summer time
# switchover.
tzinfo = pytz.timezone('Europe/Vilnius')
transition_time = datetime(1941, 6, 23, 21, 00, 00, tzinfo=UTC)
before = {
'tzname': 'MSK',
'utcoffset': timedelta(hours=3),
'dst': timedelta(0),
}
after = {
'tzname': 'CEST',
'utcoffset': timedelta(hours=2),
'dst': timedelta(hours=1),
}
class LondonHistoryStartTestCase(USEasternDSTStartTestCase):
# The first known timezone transition in London was in 1847 when
# clocks where synchronized to GMT. However, we currently only
# understand v1 format tzfile(5) files which does handle years
# this far in the past, so our earliest known transition is in
# 1916.
tzinfo = pytz.timezone('Europe/London')
# transition_time = datetime(1847, 12, 1, 1, 15, 00, tzinfo=UTC)
# before = {
# 'tzname': 'LMT',
# 'utcoffset': timedelta(minutes=-75),
# 'dst': timedelta(0),
# }
# after = {
# 'tzname': 'GMT',
# 'utcoffset': timedelta(0),
# 'dst': timedelta(0),
# }
transition_time = datetime(1916, 5, 21, 2, 00, 00, tzinfo=UTC)
before = {
'tzname': 'GMT',
'utcoffset': timedelta(0),
'dst': timedelta(0),
}
after = {
'tzname': 'BST',
'utcoffset': timedelta(hours=1),
'dst': timedelta(hours=1),
}
class LondonHistoryEndTestCase(USEasternDSTStartTestCase):
# Timezone switchovers are projected into the future, even
# though no official statements exist or could be believed even
# if they did exist. We currently only check the last known
# transition in 2037, as we are still using v1 format tzfile(5)
# files.
tzinfo = pytz.timezone('Europe/London')
# transition_time = datetime(2499, 10, 25, 1, 0, 0, tzinfo=UTC)
transition_time = datetime(2037, 10, 25, 1, 0, 0, tzinfo=UTC)
before = {
'tzname': 'BST',
'utcoffset': timedelta(hours=1),
'dst': timedelta(hours=1),
}
after = {
'tzname': 'GMT',
'utcoffset': timedelta(0),
'dst': timedelta(0),
}
class NoumeaHistoryStartTestCase(USEasternDSTStartTestCase):
# Noumea adopted a whole hour offset in 1912. Previously
# it was 11 hours, 5 minutes and 48 seconds off UTC. However,
# due to limitations of the Python datetime library, we need
# to round that to 11 hours 6 minutes.
tzinfo = pytz.timezone('Pacific/Noumea')
transition_time = datetime(1912, 1, 12, 12, 54, 12, tzinfo=UTC)
before = {
'tzname': 'LMT',
'utcoffset': timedelta(hours=11, minutes=6),
'dst': timedelta(0),
}
after = {
'tzname': '+11', # pre-2017a, NCT
'utcoffset': timedelta(hours=11),
'dst': timedelta(0),
}
class NoumeaDSTEndTestCase(USEasternDSTStartTestCase):
# Noumea dropped DST in 1997.
tzinfo = pytz.timezone('Pacific/Noumea')
transition_time = datetime(1997, 3, 1, 15, 00, 00, tzinfo=UTC)
before = {
'tzname': '+12', # pre-2017a, NCST
'utcoffset': timedelta(hours=12),
'dst': timedelta(hours=1),
}
after = {
'tzname': '+11', # pre-2017a, NCT
'utcoffset': timedelta(hours=11),
'dst': timedelta(0),
}
class NoumeaNoMoreDSTTestCase(NoumeaDSTEndTestCase):
# Noumea dropped DST in 1997. Here we test that it stops occuring.
transition_time = (
NoumeaDSTEndTestCase.transition_time + timedelta(days=365 * 10))
before = NoumeaDSTEndTestCase.after
after = NoumeaDSTEndTestCase.after
class TahitiTestCase(USEasternDSTStartTestCase):
# Tahiti has had a single transition in its history.
tzinfo = pytz.timezone('Pacific/Tahiti')
transition_time = datetime(1912, 10, 1, 9, 58, 16, tzinfo=UTC)
before = {
'tzname': 'LMT',
'utcoffset': timedelta(hours=-9, minutes=-58),
'dst': timedelta(0),
}
after = {
'tzname': '-10', # pre-2017a, TAHT
'utcoffset': timedelta(hours=-10),
'dst': timedelta(0),
}
class SamoaInternationalDateLineChange(USEasternDSTStartTestCase):
# At the end of 2011, Samoa will switch from being east of the
# international dateline to the west. There will be no Dec 30th
# 2011 and it will switch from UTC-10 to UTC+14.
tzinfo = pytz.timezone('Pacific/Apia')
transition_time = datetime(2011, 12, 30, 10, 0, 0, tzinfo=UTC)
before = {
'tzname': '-10', # pre-2017a, SDT
'utcoffset': timedelta(hours=-10),
'dst': timedelta(hours=1),
}
after = {
'tzname': '+14', # pre-2017a, WSDT
'utcoffset': timedelta(hours=14),
'dst': timedelta(hours=1),
}
class ReferenceUSEasternDSTStartTestCase(USEasternDSTStartTestCase):
tzinfo = reference.Eastern
def test_arithmetic(self):
# Reference implementation cannot handle this
pass
class ReferenceUSEasternDSTEndTestCase(USEasternDSTEndTestCase):
tzinfo = reference.Eastern
def testHourBefore(self):
# Python's datetime library has a bug, where the hour before
# a daylight saving transition is one hour out. For example,
# at the end of US/Eastern daylight saving time, 01:00 EST
# occurs twice (once at 05:00 UTC and once at 06:00 UTC),
# whereas the first should actually be 01:00 EDT.
# Note that this bug is by design - by accepting this ambiguity
# for one hour one hour per year, an is_dst flag on datetime.time
# became unnecessary.
self._test_all(self.transition_time - timedelta(hours=1), self.after)
def testInstantBefore(self):
self._test_all(self.transition_time - timedelta(seconds=1), self.after)
def test_arithmetic(self):
# Reference implementation cannot handle this
pass
class LocalTestCase(unittest.TestCase):
def testLocalize(self):
loc_tz = pytz.timezone('Europe/Amsterdam')
loc_time = loc_tz.localize(datetime(1930, 5, 10, 0, 0, 0))
# Actually +00:19:32, but Python datetime rounds this
self.assertEqual(loc_time.strftime('%Z%z'), 'AMT+0020')
loc_time = loc_tz.localize(datetime(1930, 5, 20, 0, 0, 0))
# Actually +00:19:32, but Python datetime rounds this
self.assertEqual(loc_time.strftime('%Z%z'), 'NST+0120')
loc_time = loc_tz.localize(datetime(1940, 5, 10, 0, 0, 0))
# pre-2017a, abbreviation was NCT
self.assertEqual(loc_time.strftime('%Z%z'), '+0020+0020')
loc_time = loc_tz.localize(datetime(1940, 5, 20, 0, 0, 0))
self.assertEqual(loc_time.strftime('%Z%z'), 'CEST+0200')
loc_time = loc_tz.localize(datetime(2004, 2, 1, 0, 0, 0))
self.assertEqual(loc_time.strftime('%Z%z'), 'CET+0100')
loc_time = loc_tz.localize(datetime(2004, 4, 1, 0, 0, 0))
self.assertEqual(loc_time.strftime('%Z%z'), 'CEST+0200')
loc_time = loc_tz.localize(datetime(1943, 3, 29, 1, 59, 59))
self.assertEqual(loc_time.strftime('%Z%z'), 'CET+0100')
# Switch to US
loc_tz = pytz.timezone('US/Eastern')
# End of DST ambiguity check
loc_time = loc_tz.localize(datetime(1918, 10, 27, 1, 59, 59), is_dst=1)
self.assertEqual(loc_time.strftime('%Z%z'), 'EDT-0400')
loc_time = loc_tz.localize(datetime(1918, 10, 27, 1, 59, 59), is_dst=0)
self.assertEqual(loc_time.strftime('%Z%z'), 'EST-0500')
self.assertRaises(
pytz.AmbiguousTimeError,
loc_tz.localize, datetime(1918, 10, 27, 1, 59, 59), is_dst=None
)
# Start of DST non-existent times
loc_time = loc_tz.localize(datetime(1918, 3, 31, 2, 0, 0), is_dst=0)
self.assertEqual(loc_time.strftime('%Z%z'), 'EST-0500')
loc_time = loc_tz.localize(datetime(1918, 3, 31, 2, 0, 0), is_dst=1)
self.assertEqual(loc_time.strftime('%Z%z'), 'EDT-0400')
self.assertRaises(
pytz.NonExistentTimeError,
loc_tz.localize, datetime(1918, 3, 31, 2, 0, 0), is_dst=None
)
# Weird changes - war time and peace time both is_dst==True
loc_time = loc_tz.localize(datetime(1942, 2, 9, 3, 0, 0))
self.assertEqual(loc_time.strftime('%Z%z'), 'EWT-0400')
loc_time = loc_tz.localize(datetime(1945, 8, 14, 19, 0, 0))
self.assertEqual(loc_time.strftime('%Z%z'), 'EPT-0400')
loc_time = loc_tz.localize(datetime(1945, 9, 30, 1, 0, 0), is_dst=1)
self.assertEqual(loc_time.strftime('%Z%z'), 'EPT-0400')
loc_time = loc_tz.localize(datetime(1945, 9, 30, 1, 0, 0), is_dst=0)
self.assertEqual(loc_time.strftime('%Z%z'), 'EST-0500')
# Weird changes - ambiguous time (end-of-DST like) but is_dst==False
for zonename, ambiguous_naive, expected in [
('Europe/Warsaw', datetime(1915, 8, 4, 23, 59, 59),
['1915-08-04 23:59:59 WMT+0124',
'1915-08-04 23:59:59 CET+0100']),
('Europe/Moscow', datetime(2014, 10, 26, 1, 30),
['2014-10-26 01:30:00 MSK+0400',
'2014-10-26 01:30:00 MSK+0300'])]:
loc_tz = pytz.timezone(zonename)
self.assertRaises(
pytz.AmbiguousTimeError,
loc_tz.localize, ambiguous_naive, is_dst=None
)
# Also test non-boolean is_dst in the weird case
for dst in [True, timedelta(1), False, timedelta(0)]:
loc_time = loc_tz.localize(ambiguous_naive, is_dst=dst)
self.assertEqual(loc_time.strftime(fmt), expected[not dst])
def testNormalize(self):
tz = pytz.timezone('US/Eastern')
dt = datetime(2004, 4, 4, 7, 0, 0, tzinfo=UTC).astimezone(tz)
dt2 = dt - timedelta(minutes=10)
self.assertEqual(
dt2.strftime('%Y-%m-%d %H:%M:%S %Z%z'),
'2004-04-04 02:50:00 EDT-0400'
)
dt2 = tz.normalize(dt2)
self.assertEqual(
dt2.strftime('%Y-%m-%d %H:%M:%S %Z%z'),
'2004-04-04 01:50:00 EST-0500'
)
def testPartialMinuteOffsets(self):
# utcoffset in Amsterdam was not a whole minute until 1937
# However, we fudge this by rounding them, as the Python
# datetime library
tz = pytz.timezone('Europe/Amsterdam')
utc_dt = datetime(1914, 1, 1, 13, 40, 28, tzinfo=UTC) # correct
utc_dt = utc_dt.replace(second=0) # But we need to fudge it
loc_dt = utc_dt.astimezone(tz)
self.assertEqual(
loc_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'),
'1914-01-01 14:00:00 AMT+0020'
)
# And get back...
utc_dt = loc_dt.astimezone(UTC)
self.assertEqual(
utc_dt.strftime('%Y-%m-%d %H:%M:%S %Z%z'),
'1914-01-01 13:40:00 UTC+0000'
)
def no_testCreateLocaltime(self):
# It would be nice if this worked, but it doesn't.
tz = pytz.timezone('Europe/Amsterdam')
dt = datetime(2004, 10, 31, 2, 0, 0, tzinfo=tz)
self.assertEqual(
dt.strftime(fmt),
'2004-10-31 02:00:00 CET+0100'
)
class CommonTimezonesTestCase(unittest.TestCase):
def test_bratislava(self):
# Bratislava is the default timezone for Slovakia, but our
# heuristics where not adding it to common_timezones. Ideally,
# common_timezones should be populated from zone.tab at runtime,
# but I'm hesitant to pay the startup cost as loading the list
# on demand whilst remaining backwards compatible seems
# difficult.
self.assertTrue('Europe/Bratislava' in pytz.common_timezones)
self.assertTrue('Europe/Bratislava' in pytz.common_timezones_set)
def test_us_eastern(self):
self.assertTrue('US/Eastern' in pytz.common_timezones)
self.assertTrue('US/Eastern' in pytz.common_timezones_set)
def test_belfast(self):
# Belfast uses London time.
self.assertTrue('Europe/Belfast' in pytz.all_timezones_set)
self.assertFalse('Europe/Belfast' in pytz.common_timezones)
self.assertFalse('Europe/Belfast' in pytz.common_timezones_set)
class ZoneCaseInsensitivityTestCase(unittest.TestCase):
def test_lower_case_timezone_constructor_arg(self):
for tz in pytz.all_timezones_set:
from_lower = pytz.timezone(tz.lower())
from_passed = pytz.timezone(tz)
self.assertEqual(from_lower,
from_passed,
"arg '%s' and arg '%s' produce different "
"timezone objects" % (
from_lower, from_passed))
class BaseTzInfoTestCase:
'''Ensure UTC, StaticTzInfo and DstTzInfo work consistently.
These tests are run for each type of tzinfo.
'''
tz = None # override
tz_class = None # override
def test_expectedclass(self):
self.assertTrue(isinstance(self.tz, self.tz_class))
def test_fromutc(self):
# naive datetime.
dt1 = datetime(2011, 10, 31)
# localized datetime, same timezone.
dt2 = self.tz.localize(dt1)
# Both should give the same results. Note that the standard
# Python tzinfo.fromutc() only supports the second.
for dt in [dt1, dt2]:
loc_dt = self.tz.fromutc(dt)
loc_dt2 = pytz.utc.localize(dt1).astimezone(self.tz)
self.assertEqual(loc_dt, loc_dt2)
# localized datetime, different timezone.
new_tz = pytz.timezone('Europe/Paris')
self.assertTrue(self.tz is not new_tz)
dt3 = new_tz.localize(dt1)
self.assertRaises(ValueError, self.tz.fromutc, dt3)
def test_normalize(self):
other_tz = pytz.timezone('Europe/Paris')
self.assertTrue(self.tz is not other_tz)
dt = datetime(2012, 3, 26, 12, 0)
other_dt = other_tz.localize(dt)
local_dt = self.tz.normalize(other_dt)
self.assertTrue(local_dt.tzinfo is not other_dt.tzinfo)
self.assertNotEqual(
local_dt.replace(tzinfo=None), other_dt.replace(tzinfo=None))
def test_astimezone(self):
other_tz = pytz.timezone('Europe/Paris')
self.assertTrue(self.tz is not other_tz)
dt = datetime(2012, 3, 26, 12, 0)
other_dt = other_tz.localize(dt)
local_dt = other_dt.astimezone(self.tz)
self.assertTrue(local_dt.tzinfo is not other_dt.tzinfo)
self.assertNotEqual(
local_dt.replace(tzinfo=None), other_dt.replace(tzinfo=None))
class OptimizedUTCTestCase(unittest.TestCase, BaseTzInfoTestCase):
tz = pytz.utc
tz_class = tz.__class__
class LegacyUTCTestCase(unittest.TestCase, BaseTzInfoTestCase):
# Deprecated timezone, but useful for comparison tests.
tz = pytz.timezone('Etc/UTC')
tz_class = StaticTzInfo
class StaticTzInfoTestCase(unittest.TestCase, BaseTzInfoTestCase):
tz = pytz.timezone('GMT')
tz_class = StaticTzInfo
class DstTzInfoTestCase(unittest.TestCase, BaseTzInfoTestCase):
tz = pytz.timezone('Australia/Melbourne')
tz_class = DstTzInfo
def test_suite():
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite('pytz'))
suite.addTest(doctest.DocTestSuite('pytz.tzinfo'))
import test_tzinfo
suite.addTest(unittest.defaultTestLoader.loadTestsFromModule(test_tzinfo))
return suite
if __name__ == '__main__':
warnings.simplefilter("error") # Warnings should be fatal in tests.
unittest.main(defaultTest='test_suite')

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -9,8 +9,8 @@
# All text uses UTF-8 encoding. The columns of the table are as follows:
#
# 1. ISO 3166-1 alpha-2 country code, current as of
# ISO 3166-1 N905 (2016-11-15). See: Updates on ISO 3166-1
# http://isotc.iso.org/livelink/livelink/Open/16944257
# ISO 3166-1 N976 (2018-11-06). See: Updates on ISO 3166-1
# https://isotc.iso.org/livelink/livelink/Open/16944257
# 2. The usual English name for the coded region,
# chosen so that alphabetic sorting of subsets produces helpful lists.
# This is not the same as the English name in the ISO 3166 tables.
@ -166,7 +166,7 @@ ME Montenegro
MF St Martin (French)
MG Madagascar
MH Marshall Islands
MK Macedonia
MK North Macedonia
ML Mali
MM Myanmar (Burma)
MN Mongolia
@ -235,7 +235,7 @@ ST Sao Tome & Principe
SV El Salvador
SX St Maarten (Dutch)
SY Syria
SZ Swaziland
SZ Eswatini (Swaziland)
TC Turks & Caicos Is
TD Chad
TF French Southern & Antarctic Lands

View file

@ -19,9 +19,12 @@
# See: Levine J. Coordinated Universal Time and the leap second.
# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995
# <https://ieeexplore.ieee.org/document/7909995>.
# There were no leap seconds before 1972, because the official mechanism
# accounting for the discrepancy between atomic time and the earth's rotation
# did not exist.
# did not exist. The first ("1 Jan 1972") data line in leap-seconds.list
# does not denote a leap second; it denotes the start of the current definition
# of UTC.
# The correction (+ or -) is made at the given time, so lines
# will typically look like:
@ -60,7 +63,7 @@ Leap 2016 Dec 31 23:59:60 + S
# POSIX timestamps for the data in this file:
#updated 1467936000
#expires 1561680000
#expires 1577491200
# Updated through IERS Bulletin C56
# File expires on: 28 June 2019
# Updated through IERS Bulletin C57
# File expires on: 28 December 2019

View file

@ -143,67 +143,96 @@ R MU 2008 o - O lastSun 2 1 -
R MU 2009 o - Mar lastSun 2 0 -
Z Indian/Mauritius 3:50 - LMT 1907
4 MU +04/+05
R M 1939 o - S 12 0 1 S
R M 1939 o - S 12 0 1 -
R M 1939 o - N 19 0 0 -
R M 1940 o - F 25 0 1 S
R M 1940 o - F 25 0 1 -
R M 1945 o - N 18 0 0 -
R M 1950 o - Jun 11 0 1 S
R M 1950 o - Jun 11 0 1 -
R M 1950 o - O 29 0 0 -
R M 1967 o - Jun 3 12 1 S
R M 1967 o - Jun 3 12 1 -
R M 1967 o - O 1 0 0 -
R M 1974 o - Jun 24 0 1 S
R M 1974 o - Jun 24 0 1 -
R M 1974 o - S 1 0 0 -
R M 1976 1977 - May 1 0 1 S
R M 1976 1977 - May 1 0 1 -
R M 1976 o - Au 1 0 0 -
R M 1977 o - S 28 0 0 -
R M 1978 o - Jun 1 0 1 S
R M 1978 o - Jun 1 0 1 -
R M 1978 o - Au 4 0 0 -
R M 2008 o - Jun 1 0 1 S
R M 2008 o - Jun 1 0 1 -
R M 2008 o - S 1 0 0 -
R M 2009 o - Jun 1 0 1 S
R M 2009 o - Jun 1 0 1 -
R M 2009 o - Au 21 0 0 -
R M 2010 o - May 2 0 1 S
R M 2010 o - May 2 0 1 -
R M 2010 o - Au 8 0 0 -
R M 2011 o - Ap 3 0 1 S
R M 2011 o - Ap 3 0 1 -
R M 2011 o - Jul 31 0 0 -
R M 2012 2013 - Ap lastSun 2 1 S
R M 2012 2013 - Ap lastSun 2 1 -
R M 2012 o - Jul 20 3 0 -
R M 2012 o - Au 20 2 1 S
R M 2012 o - Au 20 2 1 -
R M 2012 o - S 30 3 0 -
R M 2013 o - Jul 7 3 0 -
R M 2013 o - Au 10 2 1 S
R M 2013 ma - O lastSun 3 0 -
R M 2014 2021 - Mar lastSun 2 1 S
R M 2013 o - Au 10 2 1 -
R M 2013 2018 - O lastSun 3 0 -
R M 2014 2018 - Mar lastSun 2 1 -
R M 2014 o - Jun 28 3 0 -
R M 2014 o - Au 2 2 1 S
R M 2014 o - Au 2 2 1 -
R M 2015 o - Jun 14 3 0 -
R M 2015 o - Jul 19 2 1 S
R M 2015 o - Jul 19 2 1 -
R M 2016 o - Jun 5 3 0 -
R M 2016 o - Jul 10 2 1 S
R M 2016 o - Jul 10 2 1 -
R M 2017 o - May 21 3 0 -
R M 2017 o - Jul 2 2 1 S
R M 2017 o - Jul 2 2 1 -
R M 2018 o - May 13 3 0 -
R M 2018 o - Jun 17 2 1 S
R M 2019 o - May 5 3 0 -
R M 2019 o - Jun 9 2 1 S
R M 2020 o - Ap 19 3 0 -
R M 2020 o - May 24 2 1 S
R M 2021 o - Ap 11 3 0 -
R M 2021 o - May 16 2 1 S
R M 2022 o - May 8 2 1 S
R M 2023 o - Ap 23 2 1 S
R M 2024 o - Ap 14 2 1 S
R M 2025 o - Ap 6 2 1 S
R M 2026 ma - Mar lastSun 2 1 S
R M 2036 o - O 19 3 0 -
R M 2037 o - O 4 3 0 -
R M 2018 o - Jun 17 2 1 -
R M 2019 o - May 5 3 -1 -
R M 2019 o - Jun 9 2 0 -
R M 2020 o - Ap 19 3 -1 -
R M 2020 o - May 24 2 0 -
R M 2021 o - Ap 11 3 -1 -
R M 2021 o - May 16 2 0 -
R M 2022 o - Mar 27 3 -1 -
R M 2022 o - May 8 2 0 -
R M 2023 o - Mar 19 3 -1 -
R M 2023 o - Ap 23 2 0 -
R M 2024 o - Mar 10 3 -1 -
R M 2024 o - Ap 14 2 0 -
R M 2025 o - F 23 3 -1 -
R M 2025 o - Ap 6 2 0 -
R M 2026 o - F 15 3 -1 -
R M 2026 o - Mar 22 2 0 -
R M 2027 o - F 7 3 -1 -
R M 2027 o - Mar 14 2 0 -
R M 2028 o - Ja 23 3 -1 -
R M 2028 o - F 27 2 0 -
R M 2029 o - Ja 14 3 -1 -
R M 2029 o - F 18 2 0 -
R M 2029 o - D 30 3 -1 -
R M 2030 o - F 10 2 0 -
R M 2030 o - D 22 3 -1 -
R M 2031 o - Ja 26 2 0 -
R M 2031 o - D 14 3 -1 -
R M 2032 o - Ja 18 2 0 -
R M 2032 o - N 28 3 -1 -
R M 2033 o - Ja 9 2 0 -
R M 2033 o - N 20 3 -1 -
R M 2033 o - D 25 2 0 -
R M 2034 o - N 5 3 -1 -
R M 2034 o - D 17 2 0 -
R M 2035 o - O 28 3 -1 -
R M 2035 o - D 2 2 0 -
R M 2036 o - O 19 3 -1 -
R M 2036 o - N 23 2 0 -
R M 2037 o - O 4 3 -1 -
R M 2037 o - N 15 2 0 -
Z Africa/Casablanca -0:30:20 - LMT 1913 O 26
0 M WE%sT 1984 Mar 16
1 - CET 1986
0 M WE%sT
0 M +00/+01 1984 Mar 16
1 - +01 1986
0 M +00/+01 2018 O 28 3
1 M +01/+00
Z Africa/El_Aaiun -0:52:48 - LMT 1934
-1 - -01 1976 Ap 14
0 M WE%sT
0 M +00/+01 2018 O 28 3
1 M +01/+00
Z Africa/Maputo 2:10:20 - LMT 1903 Mar
2 - CAT
Li Africa/Maputo Africa/Blantyre
@ -238,7 +267,8 @@ Z Indian/Reunion 3:41:52 - LMT 1911 Jun
Z Africa/Sao_Tome 0:26:56 - LMT 1884
-0:36:45 - LMT 1912 Ja 1 0u
0 - GMT 2018 Ja 1 1
1 - WAT
1 - WAT 2019 Ja 1 2
0 - GMT
Z Indian/Mahe 3:41:48 - LMT 1906 Jun
4 - +04
R SA 1942 1943 - S Sun>=15 2 1 -
@ -386,17 +416,14 @@ Z Asia/Shanghai 8:5:43 - LMT 1901
8 CN C%sT
Z Asia/Urumqi 5:50:20 - LMT 1928
6 - +06
R HK 1941 o - Ap 1 3:30 1 S
R HK 1941 o - S 30 3:30 0 -
R HK 1946 o - Ap 20 3:30 1 S
R HK 1946 o - D 1 3:30 0 -
R HK 1947 o - Ap 13 3:30 1 S
R HK 1947 o - D 30 3:30 0 -
R HK 1948 o - May 2 3:30 1 S
R HK 1948 1951 - O lastSun 3:30 0 -
R HK 1952 o - O 25 3:30 0 -
R HK 1952 1953 - N Sun>=1 3:30 0 -
R HK 1949 1953 - Ap Sun>=1 3:30 1 S
R HK 1953 o - N 1 3:30 0 -
R HK 1954 1964 - Mar Sun>=18 3:30 1 S
R HK 1954 o - O 31 3:30 0 -
R HK 1955 1964 - N Sun>=1 3:30 0 -
@ -405,9 +432,11 @@ R HK 1965 1976 - O Sun>=16 3:30 0 -
R HK 1973 o - D 30 3:30 1 S
R HK 1979 o - May Sun>=8 3:30 1 S
R HK 1979 o - O Sun>=16 3:30 0 -
Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30
8 HK HK%sT 1941 D 25
9 - JST 1945 S 15
Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42
8 - HKT 1941 Jun 15 3:30
8 1 HKST 1941 O 1 4
8:30 - HKT 1941 D 25
9 - JST 1945 S 16
8 HK HK%sT
R f 1946 o - May 15 0 1 D
R f 1946 o - O 1 0 0 S
@ -528,55 +557,107 @@ Z Asia/Jayapura 9:22:48 - LMT 1932 N
9 - +09 1944 S
9:30 - +0930 1964
9 - WIT
R i 1978 1980 - Mar 21 0 1 -
R i 1978 o - O 21 0 0 -
R i 1979 o - S 19 0 0 -
R i 1980 o - S 23 0 0 -
R i 1991 o - May 3 0 1 -
R i 1992 1995 - Mar 22 0 1 -
R i 1991 1995 - S 22 0 0 -
R i 1996 o - Mar 21 0 1 -
R i 1996 o - S 21 0 0 -
R i 1997 1999 - Mar 22 0 1 -
R i 1997 1999 - S 22 0 0 -
R i 2000 o - Mar 21 0 1 -
R i 2000 o - S 21 0 0 -
R i 2001 2003 - Mar 22 0 1 -
R i 2001 2003 - S 22 0 0 -
R i 2004 o - Mar 21 0 1 -
R i 2004 o - S 21 0 0 -
R i 2005 o - Mar 22 0 1 -
R i 2005 o - S 22 0 0 -
R i 2008 o - Mar 21 0 1 -
R i 2008 o - S 21 0 0 -
R i 2009 2011 - Mar 22 0 1 -
R i 2009 2011 - S 22 0 0 -
R i 2012 o - Mar 21 0 1 -
R i 2012 o - S 21 0 0 -
R i 2013 2015 - Mar 22 0 1 -
R i 2013 2015 - S 22 0 0 -
R i 2016 o - Mar 21 0 1 -
R i 2016 o - S 21 0 0 -
R i 2017 2019 - Mar 22 0 1 -
R i 2017 2019 - S 22 0 0 -
R i 2020 o - Mar 21 0 1 -
R i 2020 o - S 21 0 0 -
R i 2021 2023 - Mar 22 0 1 -
R i 2021 2023 - S 22 0 0 -
R i 2024 o - Mar 21 0 1 -
R i 2024 o - S 21 0 0 -
R i 2025 2027 - Mar 22 0 1 -
R i 2025 2027 - S 22 0 0 -
R i 2028 2029 - Mar 21 0 1 -
R i 2028 2029 - S 21 0 0 -
R i 2030 2031 - Mar 22 0 1 -
R i 2030 2031 - S 22 0 0 -
R i 2032 2033 - Mar 21 0 1 -
R i 2032 2033 - S 21 0 0 -
R i 2034 2035 - Mar 22 0 1 -
R i 2034 2035 - S 22 0 0 -
R i 2036 ma - Mar 21 0 1 -
R i 2036 ma - S 21 0 0 -
R i 1978 1980 - Mar 20 24 1 -
R i 1978 o - O 20 24 0 -
R i 1979 o - S 18 24 0 -
R i 1980 o - S 22 24 0 -
R i 1991 o - May 2 24 1 -
R i 1992 1995 - Mar 21 24 1 -
R i 1991 1995 - S 21 24 0 -
R i 1996 o - Mar 20 24 1 -
R i 1996 o - S 20 24 0 -
R i 1997 1999 - Mar 21 24 1 -
R i 1997 1999 - S 21 24 0 -
R i 2000 o - Mar 20 24 1 -
R i 2000 o - S 20 24 0 -
R i 2001 2003 - Mar 21 24 1 -
R i 2001 2003 - S 21 24 0 -
R i 2004 o - Mar 20 24 1 -
R i 2004 o - S 20 24 0 -
R i 2005 o - Mar 21 24 1 -
R i 2005 o - S 21 24 0 -
R i 2008 o - Mar 20 24 1 -
R i 2008 o - S 20 24 0 -
R i 2009 2011 - Mar 21 24 1 -
R i 2009 2011 - S 21 24 0 -
R i 2012 o - Mar 20 24 1 -
R i 2012 o - S 20 24 0 -
R i 2013 2015 - Mar 21 24 1 -
R i 2013 2015 - S 21 24 0 -
R i 2016 o - Mar 20 24 1 -
R i 2016 o - S 20 24 0 -
R i 2017 2019 - Mar 21 24 1 -
R i 2017 2019 - S 21 24 0 -
R i 2020 o - Mar 20 24 1 -
R i 2020 o - S 20 24 0 -
R i 2021 2023 - Mar 21 24 1 -
R i 2021 2023 - S 21 24 0 -
R i 2024 o - Mar 20 24 1 -
R i 2024 o - S 20 24 0 -
R i 2025 2027 - Mar 21 24 1 -
R i 2025 2027 - S 21 24 0 -
R i 2028 2029 - Mar 20 24 1 -
R i 2028 2029 - S 20 24 0 -
R i 2030 2031 - Mar 21 24 1 -
R i 2030 2031 - S 21 24 0 -
R i 2032 2033 - Mar 20 24 1 -
R i 2032 2033 - S 20 24 0 -
R i 2034 2035 - Mar 21 24 1 -
R i 2034 2035 - S 21 24 0 -
R i 2036 2037 - Mar 20 24 1 -
R i 2036 2037 - S 20 24 0 -
R i 2038 2039 - Mar 21 24 1 -
R i 2038 2039 - S 21 24 0 -
R i 2040 2041 - Mar 20 24 1 -
R i 2040 2041 - S 20 24 0 -
R i 2042 2043 - Mar 21 24 1 -
R i 2042 2043 - S 21 24 0 -
R i 2044 2045 - Mar 20 24 1 -
R i 2044 2045 - S 20 24 0 -
R i 2046 2047 - Mar 21 24 1 -
R i 2046 2047 - S 21 24 0 -
R i 2048 2049 - Mar 20 24 1 -
R i 2048 2049 - S 20 24 0 -
R i 2050 2051 - Mar 21 24 1 -
R i 2050 2051 - S 21 24 0 -
R i 2052 2053 - Mar 20 24 1 -
R i 2052 2053 - S 20 24 0 -
R i 2054 2055 - Mar 21 24 1 -
R i 2054 2055 - S 21 24 0 -
R i 2056 2057 - Mar 20 24 1 -
R i 2056 2057 - S 20 24 0 -
R i 2058 2059 - Mar 21 24 1 -
R i 2058 2059 - S 21 24 0 -
R i 2060 2062 - Mar 20 24 1 -
R i 2060 2062 - S 20 24 0 -
R i 2063 o - Mar 21 24 1 -
R i 2063 o - S 21 24 0 -
R i 2064 2066 - Mar 20 24 1 -
R i 2064 2066 - S 20 24 0 -
R i 2067 o - Mar 21 24 1 -
R i 2067 o - S 21 24 0 -
R i 2068 2070 - Mar 20 24 1 -
R i 2068 2070 - S 20 24 0 -
R i 2071 o - Mar 21 24 1 -
R i 2071 o - S 21 24 0 -
R i 2072 2074 - Mar 20 24 1 -
R i 2072 2074 - S 20 24 0 -
R i 2075 o - Mar 21 24 1 -
R i 2075 o - S 21 24 0 -
R i 2076 2078 - Mar 20 24 1 -
R i 2076 2078 - S 20 24 0 -
R i 2079 o - Mar 21 24 1 -
R i 2079 o - S 21 24 0 -
R i 2080 2082 - Mar 20 24 1 -
R i 2080 2082 - S 20 24 0 -
R i 2083 o - Mar 21 24 1 -
R i 2083 o - S 21 24 0 -
R i 2084 2086 - Mar 20 24 1 -
R i 2084 2086 - S 20 24 0 -
R i 2087 o - Mar 21 24 1 -
R i 2087 o - S 21 24 0 -
R i 2088 ma - Mar 20 24 1 -
R i 2088 ma - S 20 24 0 -
Z Asia/Tehran 3:25:44 - LMT 1916
3:25:44 - TMT 1946
3:30 - +0330 1977 N
@ -626,6 +707,10 @@ R Z 1974 o - Jul 7 0 1 D
R Z 1974 o - O 13 0 0 S
R Z 1975 o - Ap 20 0 1 D
R Z 1975 o - Au 31 0 0 S
R Z 1980 o - Au 2 0 1 D
R Z 1980 o - S 13 1 0 S
R Z 1984 o - May 5 0 1 D
R Z 1984 o - Au 25 1 0 S
R Z 1985 o - Ap 14 0 1 D
R Z 1985 o - S 15 0 0 S
R Z 1986 o - May 18 0 1 D
@ -738,6 +823,16 @@ Z Asia/Qyzylorda 4:21:52 - LMT 1924 May 2
5 R +05/+06 1992 Ja 19 2s
6 R +06/+07 1992 Mar 29 2s
5 R +05/+06 2004 O 31 2s
6 - +06 2018 D 21
5 - +05
Z Asia/Qostanay 4:14:28 - LMT 1924 May 2
4 - +04 1930 Jun 21
5 - +05 1981 Ap
5 1 +06 1981 O
6 - +06 1982 Ap
5 R +05/+06 1991 Mar 31 2s
4 R +04/+05 1992 Ja 19 2s
5 R +05/+06 2004 O 31 2s
6 - +06
Z Asia/Aqtobe 3:48:40 - LMT 1924 May 2
4 - +04 1930 Jun 21
@ -787,17 +882,17 @@ Z Asia/Bishkek 4:58:24 - LMT 1924 May 2
5 KG +05/+06 2005 Au 12
6 - +06
R KR 1948 o - Jun 1 0 1 D
R KR 1948 o - S 13 0 0 S
R KR 1948 o - S 12 24 0 S
R KR 1949 o - Ap 3 0 1 D
R KR 1949 1951 - S Sun>=8 0 0 S
R KR 1949 1951 - S Sat>=7 24 0 S
R KR 1950 o - Ap 1 0 1 D
R KR 1951 o - May 6 0 1 D
R KR 1955 o - May 5 0 1 D
R KR 1955 o - S 9 0 0 S
R KR 1955 o - S 8 24 0 S
R KR 1956 o - May 20 0 1 D
R KR 1956 o - S 30 0 0 S
R KR 1956 o - S 29 24 0 S
R KR 1957 1960 - May Sun>=1 0 1 D
R KR 1957 1960 - S Sun>=18 0 0 S
R KR 1957 1960 - S Sat>=17 24 0 S
R KR 1987 1988 - May Sun>=8 2 1 D
R KR 1987 1988 - O Sun>=8 3 0 S
Z Asia/Seoul 8:27:52 - LMT 1908 Ap
@ -912,7 +1007,7 @@ R P 2012 o - S 21 1 0 -
R P 2013 o - S F>=21 0 0 -
R P 2014 2015 - O F>=21 0 0 -
R P 2015 o - Mar lastF 24 1 S
R P 2016 ma - Mar Sat>=22 1 1 S
R P 2016 ma - Mar Sat>=24 1 1 S
R P 2016 ma - O lastSat 1 0 -
Z Asia/Gaza 2:17:52 - LMT 1900 O
2 Z EET/EEST 1948 May 15
@ -1228,9 +1323,25 @@ Z Pacific/Marquesas -9:18 - LMT 1912 O
-9:30 - -0930
Z Pacific/Tahiti -9:58:16 - LMT 1912 O
-10 - -10
R Gu 1959 o - Jun 27 2 1 D
R Gu 1961 o - Ja 29 2 0 S
R Gu 1967 o - S 1 2 1 D
R Gu 1969 o - Ja 26 0:1 0 S
R Gu 1969 o - Jun 22 2 1 D
R Gu 1969 o - Au 31 2 0 S
R Gu 1970 1971 - Ap lastSun 2 1 D
R Gu 1970 1971 - S Sun>=1 2 0 S
R Gu 1973 o - D 16 2 1 D
R Gu 1974 o - F 24 2 0 S
R Gu 1976 o - May 26 2 1 D
R Gu 1976 o - Au 22 2:1 0 S
R Gu 1977 o - Ap 24 2 1 D
R Gu 1977 o - Au 28 2 0 S
Z Pacific/Guam -14:21 - LMT 1844 D 31
9:39 - LMT 1901
10 - GST 2000 D 23
10 - GST 1941 D 10
9 - +09 1944 Jul 31
10 Gu G%sT 2000 D 23
10 - ChST
Li Pacific/Guam Pacific/Saipan
Z Pacific/Tarawa 11:32:4 - LMT 1901
@ -1244,24 +1355,49 @@ Z Pacific/Kiritimati -10:29:20 - LMT 1901
-10 - -10 1994 D 31
14 - +14
Z Pacific/Majuro 11:24:48 - LMT 1901
11 - +11 1914 O
9 - +09 1919 F
11 - +11 1937
10 - +10 1941 Ap
9 - +09 1944 Ja 30
11 - +11 1969 O
12 - +12
Z Pacific/Kwajalein 11:9:20 - LMT 1901
11 - +11 1937
10 - +10 1941 Ap
9 - +09 1944 F 6
11 - +11 1969 O
-12 - -12 1993 Au 20
-12 - -12 1993 Au 20 24
12 - +12
Z Pacific/Chuuk 10:7:8 - LMT 1901
Z Pacific/Chuuk -13:52:52 - LMT 1844 D 31
10:7:8 - LMT 1901
10 - +10 1914 O
9 - +09 1919 F
10 - +10 1941 Ap
9 - +09 1945 Au
10 - +10
Z Pacific/Pohnpei 10:32:52 - LMT 1901
Z Pacific/Pohnpei -13:27:8 - LMT 1844 D 31
10:32:52 - LMT 1901
11 - +11 1914 O
9 - +09 1919 F
11 - +11 1937
10 - +10 1941 Ap
9 - +09 1945 Au
11 - +11
Z Pacific/Kosrae 10:51:56 - LMT 1901
Z Pacific/Kosrae -13:8:4 - LMT 1844 D 31
10:51:56 - LMT 1901
11 - +11 1914 O
9 - +09 1919 F
11 - +11 1937
10 - +10 1941 Ap
9 - +09 1945 Au
11 - +11 1969 O
12 - +12 1999
11 - +11
Z Pacific/Nauru 11:7:40 - LMT 1921 Ja 15
11:30 - +1130 1942 Mar 15
9 - +09 1944 Au 15
11:30 - +1130 1979 May
11:30 - +1130 1942 Au 29
9 - +09 1945 S 8
11:30 - +1130 1979 F 10 2
12 - +12
R NC 1977 1978 - D Sun>=1 0 1 -
R NC 1978 1979 - F 27 0 0 -
@ -1317,7 +1453,8 @@ Z Pacific/Norfolk 11:11:52 - LMT 1901
11:30 1 +1230 1975 Mar 2 2
11:30 - +1130 2015 O 4 2
11 - +11
Z Pacific/Palau 8:57:56 - LMT 1901
Z Pacific/Palau -15:2:4 - LMT 1844 D 31
8:57:56 - LMT 1901
9 - +09
Z Pacific/Port_Moresby 9:48:40 - LMT 1880
9:48:32 - PMMT 1895
@ -2433,6 +2570,15 @@ R s 1976 1977 - S lastSun 1 0 -
R s 1977 o - Ap 2 23 1 S
R s 1978 o - Ap 2 2s 1 S
R s 1978 o - O 1 2s 0 -
R Sp 1967 o - Jun 3 12 1 S
R Sp 1967 o - O 1 0 0 -
R Sp 1974 o - Jun 24 0 1 S
R Sp 1974 o - S 1 0 0 -
R Sp 1976 1977 - May 1 0 1 S
R Sp 1976 o - Au 1 0 0 -
R Sp 1977 o - S 28 0 0 -
R Sp 1978 o - Jun 1 0 1 S
R Sp 1978 o - Au 4 0 0 -
Z Europe/Madrid -0:14:44 - LMT 1900 D 31 23:45:16
0 s WE%sT 1940 Mar 16 23
1 s CE%sT 1979
@ -2443,7 +2589,7 @@ Z Africa/Ceuta -0:21:16 - LMT 1900 D 31 23:38:44
0 - WET 1924
0 s WE%sT 1929
0 - WET 1967
0 M WE%sT 1984 Mar 16
0 Sp WE%sT 1984 Mar 16
1 - CET 1986
1 E CE%sT
Z Atlantic/Canary -1:1:36 - LMT 1922 Mar
@ -2658,6 +2804,8 @@ Z America/Metlakatla 15:13:42 - LMT 1867 O 19 15:44:55
-8 - PST 1969
-8 u P%sT 1983 O 30 2
-8 - PST 2015 N 1 2
-9 u AK%sT 2018 N 4 2
-8 - PST 2019 Ja 20 2
-9 u AK%sT
Z America/Yakutat 14:41:5 - LMT 1867 O 19 15:12:18
-9:18:55 - LMT 1900 Au 20 12
@ -2695,9 +2843,7 @@ Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35
Z Pacific/Honolulu -10:31:26 - LMT 1896 Ja 13 12
-10:30 - HST 1933 Ap 30 2
-10:30 1 HDT 1933 May 21 12
-10:30 - HST 1942 F 9 2
-10:30 1 HDT 1945 S 30 2
-10:30 - HST 1947 Jun 8 2
-10:30 u H%sT 1947 Jun 8 2
-10 - HST
Z America/Phoenix -7:28:18 - LMT 1883 N 18 11:31:42
-7 u M%sT 1944 Ja 1 0:1
@ -4026,7 +4172,6 @@ Z America/Caracas -4:27:44 - LMT 1890
-4 - -04
Z Etc/GMT 0 - GMT
Z Etc/UTC 0 - UTC
Z Etc/UCT 0 - UCT
Li Etc/GMT GMT
Li Etc/UTC Etc/Universal
Li Etc/UTC Etc/Zulu
@ -4128,6 +4273,7 @@ Li Pacific/Easter Chile/EasterIsland
Li America/Havana Cuba
Li Africa/Cairo Egypt
Li Europe/Dublin Eire
Li Etc/UTC Etc/UCT
Li Europe/London Europe/Belfast
Li Europe/Chisinau Europe/Tiraspol
Li Europe/London GB
@ -4162,7 +4308,7 @@ Li Asia/Taipei ROC
Li Asia/Seoul ROK
Li Asia/Singapore Singapore
Li Europe/Istanbul Turkey
Li Etc/UCT UCT
Li Etc/UTC UCT
Li America/Anchorage US/Alaska
Li America/Adak US/Aleutian
Li America/Phoenix US/Arizona

View file

@ -239,6 +239,7 @@ KW +2920+04759 Asia/Kuwait
KY +1918-08123 America/Cayman
KZ +4315+07657 Asia/Almaty Kazakhstan (most areas)
KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda
KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay
KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe
KZ +4431+05016 Asia/Aqtau Mangghystau/Mankistau
KZ +4707+05156 Asia/Atyrau Atyrau/Atirau/Gur'yev
@ -332,9 +333,9 @@ RS +4450+02030 Europe/Belgrade
RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad
RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area
RU +4457+03406 Europe/Simferopol MSK+00 - Crimea
RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd
RU +5836+04939 Europe/Kirov MSK+00 - Kirov
RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan
RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd
RU +5134+04602 Europe/Saratov MSK+01 - Saratov
RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk
RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia

View file

@ -211,6 +211,7 @@ KP +3901+12545 Asia/Pyongyang
KR +3733+12658 Asia/Seoul
KZ +4315+07657 Asia/Almaty Kazakhstan (most areas)
KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda
KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay
KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe
KZ +4431+05016 Asia/Aqtau Mangghystaū/Mankistau
KZ +4707+05156 Asia/Atyrau Atyraū/Atirau/Gur'yev
@ -289,9 +290,9 @@ RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade
RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad
RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area
RU +4457+03406 Europe/Simferopol MSK+00 - Crimea
RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd
RU +5836+04939 Europe/Kirov MSK+00 - Kirov
RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan
RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd
RU +5134+04602 Europe/Saratov MSK+01 - Saratov
RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk
RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia