mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-30 16:33:37 +00:00
Change improve getting the local timezone information.
This commit is contained in:
parent
5c3ec65813
commit
7c68ac2439
613 changed files with 5769 additions and 4 deletions
|
@ -174,6 +174,7 @@
|
||||||
* Fix ensure downloaded eps are not shown on episode view
|
* Fix ensure downloaded eps are not shown on episode view
|
||||||
* Fix allow propers to pp when show marked upgrade once
|
* Fix allow propers to pp when show marked upgrade once
|
||||||
* Fix never set episodes without airdate to wanted
|
* Fix never set episodes without airdate to wanted
|
||||||
|
* Change improve getting the local timezone information
|
||||||
|
|
||||||
[develop changelog]
|
[develop changelog]
|
||||||
* Change send nzb data to NZBGet for Anizb instead of url
|
* Change send nzb data to NZBGet for Anizb instead of url
|
||||||
|
|
1526
lib/pytz/__init__.py
Normal file
1526
lib/pytz/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
48
lib/pytz/exceptions.py
Normal file
48
lib/pytz/exceptions.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'''
|
||||||
|
Custom exceptions raised by pytz.
|
||||||
|
'''
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'UnknownTimeZoneError', 'InvalidTimeError', 'AmbiguousTimeError',
|
||||||
|
'NonExistentTimeError',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownTimeZoneError(KeyError):
|
||||||
|
'''Exception raised when pytz is passed an unknown timezone.
|
||||||
|
|
||||||
|
>>> isinstance(UnknownTimeZoneError(), LookupError)
|
||||||
|
True
|
||||||
|
|
||||||
|
This class is actually a subclass of KeyError to provide backwards
|
||||||
|
compatibility with code relying on the undocumented behavior of earlier
|
||||||
|
pytz releases.
|
||||||
|
|
||||||
|
>>> isinstance(UnknownTimeZoneError(), KeyError)
|
||||||
|
True
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidTimeError(Exception):
|
||||||
|
'''Base class for invalid time exceptions.'''
|
||||||
|
|
||||||
|
|
||||||
|
class AmbiguousTimeError(InvalidTimeError):
|
||||||
|
'''Exception raised when attempting to create an ambiguous wallclock time.
|
||||||
|
|
||||||
|
At the end of a DST transition period, a particular wallclock time will
|
||||||
|
occur twice (once before the clocks are set back, once after). Both
|
||||||
|
possibilities may be correct, unless further information is supplied.
|
||||||
|
|
||||||
|
See DstTzInfo.normalize() for more info
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class NonExistentTimeError(InvalidTimeError):
|
||||||
|
'''Exception raised when attempting to create a wallclock time that
|
||||||
|
cannot exist.
|
||||||
|
|
||||||
|
At the start of a DST transition period, the wallclock time jumps forward.
|
||||||
|
The instants jumped over never occur.
|
||||||
|
'''
|
168
lib/pytz/lazy.py
Normal file
168
lib/pytz/lazy.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
from threading import RLock
|
||||||
|
try:
|
||||||
|
from UserDict import DictMixin
|
||||||
|
except ImportError:
|
||||||
|
from collections import Mapping as DictMixin
|
||||||
|
|
||||||
|
|
||||||
|
# With lazy loading, we might end up with multiple threads triggering
|
||||||
|
# it at the same time. We need a lock.
|
||||||
|
_fill_lock = RLock()
|
||||||
|
|
||||||
|
|
||||||
|
class LazyDict(DictMixin):
|
||||||
|
"""Dictionary populated on first use."""
|
||||||
|
data = None
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if self.data is None:
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if self.data is None:
|
||||||
|
self._fill()
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return self.data[key.upper()]
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
if self.data is None:
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if self.data is None:
|
||||||
|
self._fill()
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return key in self.data
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
if self.data is None:
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if self.data is None:
|
||||||
|
self._fill()
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return iter(self.data)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
if self.data is None:
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if self.data is None:
|
||||||
|
self._fill()
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return len(self.data)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
if self.data is None:
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if self.data is None:
|
||||||
|
self._fill()
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return self.data.keys()
|
||||||
|
|
||||||
|
|
||||||
|
class LazyList(list):
|
||||||
|
"""List populated on first use."""
|
||||||
|
|
||||||
|
_props = [
|
||||||
|
'__str__', '__repr__', '__unicode__',
|
||||||
|
'__hash__', '__sizeof__', '__cmp__',
|
||||||
|
'__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
|
||||||
|
'append', 'count', 'index', 'extend', 'insert', 'pop', 'remove',
|
||||||
|
'reverse', 'sort', '__add__', '__radd__', '__iadd__', '__mul__',
|
||||||
|
'__rmul__', '__imul__', '__contains__', '__len__', '__nonzero__',
|
||||||
|
'__getitem__', '__setitem__', '__delitem__', '__iter__',
|
||||||
|
'__reversed__', '__getslice__', '__setslice__', '__delslice__']
|
||||||
|
|
||||||
|
def __new__(cls, fill_iter=None):
|
||||||
|
|
||||||
|
if fill_iter is None:
|
||||||
|
return list()
|
||||||
|
|
||||||
|
# We need a new class as we will be dynamically messing with its
|
||||||
|
# methods.
|
||||||
|
class LazyList(list):
|
||||||
|
pass
|
||||||
|
|
||||||
|
fill_iter = [fill_iter]
|
||||||
|
|
||||||
|
def lazy(name):
|
||||||
|
def _lazy(self, *args, **kw):
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if len(fill_iter) > 0:
|
||||||
|
list.extend(self, fill_iter.pop())
|
||||||
|
for method_name in cls._props:
|
||||||
|
delattr(LazyList, method_name)
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return getattr(list, name)(self, *args, **kw)
|
||||||
|
return _lazy
|
||||||
|
|
||||||
|
for name in cls._props:
|
||||||
|
setattr(LazyList, name, lazy(name))
|
||||||
|
|
||||||
|
new_list = LazyList()
|
||||||
|
return new_list
|
||||||
|
|
||||||
|
# Not all versions of Python declare the same magic methods.
|
||||||
|
# Filter out properties that don't exist in this version of Python
|
||||||
|
# from the list.
|
||||||
|
LazyList._props = [prop for prop in LazyList._props if hasattr(list, prop)]
|
||||||
|
|
||||||
|
|
||||||
|
class LazySet(set):
|
||||||
|
"""Set populated on first use."""
|
||||||
|
|
||||||
|
_props = (
|
||||||
|
'__str__', '__repr__', '__unicode__',
|
||||||
|
'__hash__', '__sizeof__', '__cmp__',
|
||||||
|
'__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
|
||||||
|
'__contains__', '__len__', '__nonzero__',
|
||||||
|
'__getitem__', '__setitem__', '__delitem__', '__iter__',
|
||||||
|
'__sub__', '__and__', '__xor__', '__or__',
|
||||||
|
'__rsub__', '__rand__', '__rxor__', '__ror__',
|
||||||
|
'__isub__', '__iand__', '__ixor__', '__ior__',
|
||||||
|
'add', 'clear', 'copy', 'difference', 'difference_update',
|
||||||
|
'discard', 'intersection', 'intersection_update', 'isdisjoint',
|
||||||
|
'issubset', 'issuperset', 'pop', 'remove',
|
||||||
|
'symmetric_difference', 'symmetric_difference_update',
|
||||||
|
'union', 'update')
|
||||||
|
|
||||||
|
def __new__(cls, fill_iter=None):
|
||||||
|
|
||||||
|
if fill_iter is None:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
class LazySet(set):
|
||||||
|
pass
|
||||||
|
|
||||||
|
fill_iter = [fill_iter]
|
||||||
|
|
||||||
|
def lazy(name):
|
||||||
|
def _lazy(self, *args, **kw):
|
||||||
|
_fill_lock.acquire()
|
||||||
|
try:
|
||||||
|
if len(fill_iter) > 0:
|
||||||
|
for i in fill_iter.pop():
|
||||||
|
set.add(self, i)
|
||||||
|
for method_name in cls._props:
|
||||||
|
delattr(LazySet, method_name)
|
||||||
|
finally:
|
||||||
|
_fill_lock.release()
|
||||||
|
return getattr(set, name)(self, *args, **kw)
|
||||||
|
return _lazy
|
||||||
|
|
||||||
|
for name in cls._props:
|
||||||
|
setattr(LazySet, name, lazy(name))
|
||||||
|
|
||||||
|
new_set = LazySet()
|
||||||
|
return new_set
|
||||||
|
|
||||||
|
# Not all versions of Python declare the same magic methods.
|
||||||
|
# Filter out properties that don't exist in this version of Python
|
||||||
|
# from the list.
|
||||||
|
LazySet._props = [prop for prop in LazySet._props if hasattr(set, prop)]
|
127
lib/pytz/reference.py
Normal file
127
lib/pytz/reference.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
'''
|
||||||
|
Reference tzinfo implementations from the Python docs.
|
||||||
|
Used for testing against as they are only correct for the years
|
||||||
|
1987 to 2006. Do not use these for real code.
|
||||||
|
'''
|
||||||
|
|
||||||
|
from datetime import tzinfo, timedelta, datetime
|
||||||
|
from pytz import utc, UTC, HOUR, ZERO
|
||||||
|
|
||||||
|
# A class building tzinfo objects for fixed-offset time zones.
|
||||||
|
# Note that FixedOffset(0, "UTC") is a different way to build a
|
||||||
|
# UTC tzinfo object.
|
||||||
|
|
||||||
|
class FixedOffset(tzinfo):
|
||||||
|
"""Fixed offset in minutes east from UTC."""
|
||||||
|
|
||||||
|
def __init__(self, offset, name):
|
||||||
|
self.__offset = timedelta(minutes = offset)
|
||||||
|
self.__name = name
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
return self.__offset
|
||||||
|
|
||||||
|
def tzname(self, dt):
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
# A class capturing the platform's idea of local time.
|
||||||
|
|
||||||
|
import time as _time
|
||||||
|
|
||||||
|
STDOFFSET = timedelta(seconds = -_time.timezone)
|
||||||
|
if _time.daylight:
|
||||||
|
DSTOFFSET = timedelta(seconds = -_time.altzone)
|
||||||
|
else:
|
||||||
|
DSTOFFSET = STDOFFSET
|
||||||
|
|
||||||
|
DSTDIFF = DSTOFFSET - STDOFFSET
|
||||||
|
|
||||||
|
class LocalTimezone(tzinfo):
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
if self._isdst(dt):
|
||||||
|
return DSTOFFSET
|
||||||
|
else:
|
||||||
|
return STDOFFSET
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
if self._isdst(dt):
|
||||||
|
return DSTDIFF
|
||||||
|
else:
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
def tzname(self, dt):
|
||||||
|
return _time.tzname[self._isdst(dt)]
|
||||||
|
|
||||||
|
def _isdst(self, dt):
|
||||||
|
tt = (dt.year, dt.month, dt.day,
|
||||||
|
dt.hour, dt.minute, dt.second,
|
||||||
|
dt.weekday(), 0, -1)
|
||||||
|
stamp = _time.mktime(tt)
|
||||||
|
tt = _time.localtime(stamp)
|
||||||
|
return tt.tm_isdst > 0
|
||||||
|
|
||||||
|
Local = LocalTimezone()
|
||||||
|
|
||||||
|
# A complete implementation of current DST rules for major US time zones.
|
||||||
|
|
||||||
|
def first_sunday_on_or_after(dt):
|
||||||
|
days_to_go = 6 - dt.weekday()
|
||||||
|
if days_to_go:
|
||||||
|
dt += timedelta(days_to_go)
|
||||||
|
return dt
|
||||||
|
|
||||||
|
# In the US, DST starts at 2am (standard time) on the first Sunday in April.
|
||||||
|
DSTSTART = datetime(1, 4, 1, 2)
|
||||||
|
# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
|
||||||
|
# which is the first Sunday on or after Oct 25.
|
||||||
|
DSTEND = datetime(1, 10, 25, 1)
|
||||||
|
|
||||||
|
class USTimeZone(tzinfo):
|
||||||
|
|
||||||
|
def __init__(self, hours, reprname, stdname, dstname):
|
||||||
|
self.stdoffset = timedelta(hours=hours)
|
||||||
|
self.reprname = reprname
|
||||||
|
self.stdname = stdname
|
||||||
|
self.dstname = dstname
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.reprname
|
||||||
|
|
||||||
|
def tzname(self, dt):
|
||||||
|
if self.dst(dt):
|
||||||
|
return self.dstname
|
||||||
|
else:
|
||||||
|
return self.stdname
|
||||||
|
|
||||||
|
def utcoffset(self, dt):
|
||||||
|
return self.stdoffset + self.dst(dt)
|
||||||
|
|
||||||
|
def dst(self, dt):
|
||||||
|
if dt is None or dt.tzinfo is None:
|
||||||
|
# An exception may be sensible here, in one or both cases.
|
||||||
|
# It depends on how you want to treat them. The default
|
||||||
|
# fromutc() implementation (called by the default astimezone()
|
||||||
|
# implementation) passes a datetime with dt.tzinfo is self.
|
||||||
|
return ZERO
|
||||||
|
assert dt.tzinfo is self
|
||||||
|
|
||||||
|
# Find first Sunday in April & the last in October.
|
||||||
|
start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
|
||||||
|
end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
|
||||||
|
|
||||||
|
# Can't compare naive to aware objects, so strip the timezone from
|
||||||
|
# dt first.
|
||||||
|
if start <= dt.replace(tzinfo=None) < end:
|
||||||
|
return HOUR
|
||||||
|
else:
|
||||||
|
return ZERO
|
||||||
|
|
||||||
|
Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
|
||||||
|
Central = USTimeZone(-6, "Central", "CST", "CDT")
|
||||||
|
Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
|
||||||
|
Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
|
||||||
|
|
34
lib/pytz/tests/test_docs.py
Normal file
34
lib/pytz/tests/test_docs.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
|
||||||
|
from doctest import DocFileSuite
|
||||||
|
import unittest, os.path, 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')
|
||||||
|
|
||||||
|
|
313
lib/pytz/tests/test_lazy.py
Normal file
313
lib/pytz/tests/test_lazy.py
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
from operator import *
|
||||||
|
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(self.lazy, op)(set([1])),
|
||||||
|
getattr(self.base, op)(set([1])), op)
|
||||||
|
self.assertEqual(self.lazy, self.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()
|
843
lib/pytz/tests/test_tzinfo.py
Normal file
843
lib/pytz/tests/test_tzinfo.py
Normal file
|
@ -0,0 +1,843 @@
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
|
||||||
|
import sys, os, os.path
|
||||||
|
import unittest, doctest
|
||||||
|
try:
|
||||||
|
import cPickle as pickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle
|
||||||
|
from datetime import datetime, time, timedelta, tzinfo
|
||||||
|
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
|
||||||
|
from pytz import reference
|
||||||
|
from pytz.tzfile import _byte_string
|
||||||
|
from pytz.tzinfo import DstTzInfo, StaticTzInfo
|
||||||
|
|
||||||
|
# I test for expected version to ensure the correct version of pytz is
|
||||||
|
# actually being tested.
|
||||||
|
EXPECTED_VERSION='2016.6.1'
|
||||||
|
EXPECTED_OLSON_VERSION='2016f'
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
unicode
|
||||||
|
except NameError:
|
||||||
|
# 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):
|
||||||
|
dt = datetime(2004, 2, 1, 0, 0, 0)
|
||||||
|
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': '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': 'NCST',
|
||||||
|
'utcoffset': timedelta(hours=12),
|
||||||
|
'dst': timedelta(hours=1),
|
||||||
|
}
|
||||||
|
after = {
|
||||||
|
'tzname': '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': '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': 'SDT',
|
||||||
|
'utcoffset': timedelta(hours=-10),
|
||||||
|
'dst': timedelta(hours=1),
|
||||||
|
}
|
||||||
|
after = {
|
||||||
|
'tzname': '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))
|
||||||
|
self.assertEqual(loc_time.strftime('%Z%z'), 'NET+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')
|
||||||
|
|
||||||
|
tz = pytz.timezone('Europe/Amsterdam')
|
||||||
|
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 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')
|
137
lib/pytz/tzfile.py
Normal file
137
lib/pytz/tzfile.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
'''
|
||||||
|
$Id: tzfile.py,v 1.8 2004/06/03 00:15:24 zenzen Exp $
|
||||||
|
'''
|
||||||
|
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from io import StringIO
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from struct import unpack, calcsize
|
||||||
|
|
||||||
|
from pytz.tzinfo import StaticTzInfo, DstTzInfo, memorized_ttinfo
|
||||||
|
from pytz.tzinfo import memorized_datetime, memorized_timedelta
|
||||||
|
|
||||||
|
def _byte_string(s):
|
||||||
|
"""Cast a string or byte string to an ASCII byte string."""
|
||||||
|
return s.encode('ASCII')
|
||||||
|
|
||||||
|
_NULL = _byte_string('\0')
|
||||||
|
|
||||||
|
def _std_string(s):
|
||||||
|
"""Cast a string or byte string to an ASCII string."""
|
||||||
|
return str(s.decode('ASCII'))
|
||||||
|
|
||||||
|
def build_tzinfo(zone, fp):
|
||||||
|
head_fmt = '>4s c 15x 6l'
|
||||||
|
head_size = calcsize(head_fmt)
|
||||||
|
(magic, format, ttisgmtcnt, ttisstdcnt,leapcnt, timecnt,
|
||||||
|
typecnt, charcnt) = unpack(head_fmt, fp.read(head_size))
|
||||||
|
|
||||||
|
# Make sure it is a tzfile(5) file
|
||||||
|
assert magic == _byte_string('TZif'), 'Got magic %s' % repr(magic)
|
||||||
|
|
||||||
|
# Read out the transition times, localtime indices and ttinfo structures.
|
||||||
|
data_fmt = '>%(timecnt)dl %(timecnt)dB %(ttinfo)s %(charcnt)ds' % dict(
|
||||||
|
timecnt=timecnt, ttinfo='lBB'*typecnt, charcnt=charcnt)
|
||||||
|
data_size = calcsize(data_fmt)
|
||||||
|
data = unpack(data_fmt, fp.read(data_size))
|
||||||
|
|
||||||
|
# make sure we unpacked the right number of values
|
||||||
|
assert len(data) == 2 * timecnt + 3 * typecnt + 1
|
||||||
|
transitions = [memorized_datetime(trans)
|
||||||
|
for trans in data[:timecnt]]
|
||||||
|
lindexes = list(data[timecnt:2 * timecnt])
|
||||||
|
ttinfo_raw = data[2 * timecnt:-1]
|
||||||
|
tznames_raw = data[-1]
|
||||||
|
del data
|
||||||
|
|
||||||
|
# Process ttinfo into separate structs
|
||||||
|
ttinfo = []
|
||||||
|
tznames = {}
|
||||||
|
i = 0
|
||||||
|
while i < len(ttinfo_raw):
|
||||||
|
# have we looked up this timezone name yet?
|
||||||
|
tzname_offset = ttinfo_raw[i+2]
|
||||||
|
if tzname_offset not in tznames:
|
||||||
|
nul = tznames_raw.find(_NULL, tzname_offset)
|
||||||
|
if nul < 0:
|
||||||
|
nul = len(tznames_raw)
|
||||||
|
tznames[tzname_offset] = _std_string(
|
||||||
|
tznames_raw[tzname_offset:nul])
|
||||||
|
ttinfo.append((ttinfo_raw[i],
|
||||||
|
bool(ttinfo_raw[i+1]),
|
||||||
|
tznames[tzname_offset]))
|
||||||
|
i += 3
|
||||||
|
|
||||||
|
# Now build the timezone object
|
||||||
|
if len(ttinfo) ==1 or len(transitions) == 0:
|
||||||
|
ttinfo[0][0], ttinfo[0][2]
|
||||||
|
cls = type(zone, (StaticTzInfo,), dict(
|
||||||
|
zone=zone,
|
||||||
|
_utcoffset=memorized_timedelta(ttinfo[0][0]),
|
||||||
|
_tzname=ttinfo[0][2]))
|
||||||
|
else:
|
||||||
|
# Early dates use the first standard time ttinfo
|
||||||
|
i = 0
|
||||||
|
while ttinfo[i][1]:
|
||||||
|
i += 1
|
||||||
|
if ttinfo[i] == ttinfo[lindexes[0]]:
|
||||||
|
transitions[0] = datetime.min
|
||||||
|
else:
|
||||||
|
transitions.insert(0, datetime.min)
|
||||||
|
lindexes.insert(0, i)
|
||||||
|
|
||||||
|
# calculate transition info
|
||||||
|
transition_info = []
|
||||||
|
for i in range(len(transitions)):
|
||||||
|
inf = ttinfo[lindexes[i]]
|
||||||
|
utcoffset = inf[0]
|
||||||
|
if not inf[1]:
|
||||||
|
dst = 0
|
||||||
|
else:
|
||||||
|
for j in range(i-1, -1, -1):
|
||||||
|
prev_inf = ttinfo[lindexes[j]]
|
||||||
|
if not prev_inf[1]:
|
||||||
|
break
|
||||||
|
dst = inf[0] - prev_inf[0] # dst offset
|
||||||
|
|
||||||
|
# Bad dst? Look further. DST > 24 hours happens when
|
||||||
|
# a timzone has moved across the international dateline.
|
||||||
|
if dst <= 0 or dst > 3600*3:
|
||||||
|
for j in range(i+1, len(transitions)):
|
||||||
|
stdinf = ttinfo[lindexes[j]]
|
||||||
|
if not stdinf[1]:
|
||||||
|
dst = inf[0] - stdinf[0]
|
||||||
|
if dst > 0:
|
||||||
|
break # Found a useful std time.
|
||||||
|
|
||||||
|
tzname = inf[2]
|
||||||
|
|
||||||
|
# Round utcoffset and dst to the nearest minute or the
|
||||||
|
# datetime library will complain. Conversions to these timezones
|
||||||
|
# might be up to plus or minus 30 seconds out, but it is
|
||||||
|
# the best we can do.
|
||||||
|
utcoffset = int((utcoffset + 30) // 60) * 60
|
||||||
|
dst = int((dst + 30) // 60) * 60
|
||||||
|
transition_info.append(memorized_ttinfo(utcoffset, dst, tzname))
|
||||||
|
|
||||||
|
cls = type(zone, (DstTzInfo,), dict(
|
||||||
|
zone=zone,
|
||||||
|
_utc_transition_times=transitions,
|
||||||
|
_transition_info=transition_info))
|
||||||
|
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import os.path
|
||||||
|
from pprint import pprint
|
||||||
|
base = os.path.join(os.path.dirname(__file__), 'zoneinfo')
|
||||||
|
tz = build_tzinfo('Australia/Melbourne',
|
||||||
|
open(os.path.join(base,'Australia','Melbourne'), 'rb'))
|
||||||
|
tz = build_tzinfo('US/Eastern',
|
||||||
|
open(os.path.join(base,'US','Eastern'), 'rb'))
|
||||||
|
pprint(tz._utc_transition_times)
|
||||||
|
#print tz.asPython(4)
|
||||||
|
#print tz.transitions_mapping
|
564
lib/pytz/tzinfo.py
Normal file
564
lib/pytz/tzinfo.py
Normal file
|
@ -0,0 +1,564 @@
|
||||||
|
'''Base classes and helpers for building zone specific tzinfo classes'''
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, tzinfo
|
||||||
|
from bisect import bisect_right
|
||||||
|
try:
|
||||||
|
set
|
||||||
|
except NameError:
|
||||||
|
from sets import Set as set
|
||||||
|
|
||||||
|
import pytz
|
||||||
|
from pytz.exceptions import AmbiguousTimeError, NonExistentTimeError
|
||||||
|
|
||||||
|
__all__ = []
|
||||||
|
|
||||||
|
_timedelta_cache = {}
|
||||||
|
def memorized_timedelta(seconds):
|
||||||
|
'''Create only one instance of each distinct timedelta'''
|
||||||
|
try:
|
||||||
|
return _timedelta_cache[seconds]
|
||||||
|
except KeyError:
|
||||||
|
delta = timedelta(seconds=seconds)
|
||||||
|
_timedelta_cache[seconds] = delta
|
||||||
|
return delta
|
||||||
|
|
||||||
|
_epoch = datetime.utcfromtimestamp(0)
|
||||||
|
_datetime_cache = {0: _epoch}
|
||||||
|
def memorized_datetime(seconds):
|
||||||
|
'''Create only one instance of each distinct datetime'''
|
||||||
|
try:
|
||||||
|
return _datetime_cache[seconds]
|
||||||
|
except KeyError:
|
||||||
|
# NB. We can't just do datetime.utcfromtimestamp(seconds) as this
|
||||||
|
# fails with negative values under Windows (Bug #90096)
|
||||||
|
dt = _epoch + timedelta(seconds=seconds)
|
||||||
|
_datetime_cache[seconds] = dt
|
||||||
|
return dt
|
||||||
|
|
||||||
|
_ttinfo_cache = {}
|
||||||
|
def memorized_ttinfo(*args):
|
||||||
|
'''Create only one instance of each distinct tuple'''
|
||||||
|
try:
|
||||||
|
return _ttinfo_cache[args]
|
||||||
|
except KeyError:
|
||||||
|
ttinfo = (
|
||||||
|
memorized_timedelta(args[0]),
|
||||||
|
memorized_timedelta(args[1]),
|
||||||
|
args[2]
|
||||||
|
)
|
||||||
|
_ttinfo_cache[args] = ttinfo
|
||||||
|
return ttinfo
|
||||||
|
|
||||||
|
_notime = memorized_timedelta(0)
|
||||||
|
|
||||||
|
def _to_seconds(td):
|
||||||
|
'''Convert a timedelta to seconds'''
|
||||||
|
return td.seconds + td.days * 24 * 60 * 60
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTzInfo(tzinfo):
|
||||||
|
# Overridden in subclass
|
||||||
|
_utcoffset = None
|
||||||
|
_tzname = None
|
||||||
|
zone = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.zone
|
||||||
|
|
||||||
|
|
||||||
|
class StaticTzInfo(BaseTzInfo):
|
||||||
|
'''A timezone that has a constant offset from UTC
|
||||||
|
|
||||||
|
These timezones are rare, as most locations have changed their
|
||||||
|
offset at some point in their history
|
||||||
|
'''
|
||||||
|
def fromutc(self, dt):
|
||||||
|
'''See datetime.tzinfo.fromutc'''
|
||||||
|
if dt.tzinfo is not None and dt.tzinfo is not self:
|
||||||
|
raise ValueError('fromutc: dt.tzinfo is not self')
|
||||||
|
return (dt + self._utcoffset).replace(tzinfo=self)
|
||||||
|
|
||||||
|
def utcoffset(self, dt, is_dst=None):
|
||||||
|
'''See datetime.tzinfo.utcoffset
|
||||||
|
|
||||||
|
is_dst is ignored for StaticTzInfo, and exists only to
|
||||||
|
retain compatibility with DstTzInfo.
|
||||||
|
'''
|
||||||
|
return self._utcoffset
|
||||||
|
|
||||||
|
def dst(self, dt, is_dst=None):
|
||||||
|
'''See datetime.tzinfo.dst
|
||||||
|
|
||||||
|
is_dst is ignored for StaticTzInfo, and exists only to
|
||||||
|
retain compatibility with DstTzInfo.
|
||||||
|
'''
|
||||||
|
return _notime
|
||||||
|
|
||||||
|
def tzname(self, dt, is_dst=None):
|
||||||
|
'''See datetime.tzinfo.tzname
|
||||||
|
|
||||||
|
is_dst is ignored for StaticTzInfo, and exists only to
|
||||||
|
retain compatibility with DstTzInfo.
|
||||||
|
'''
|
||||||
|
return self._tzname
|
||||||
|
|
||||||
|
def localize(self, dt, is_dst=False):
|
||||||
|
'''Convert naive time to local time'''
|
||||||
|
if dt.tzinfo is not None:
|
||||||
|
raise ValueError('Not naive datetime (tzinfo is already set)')
|
||||||
|
return dt.replace(tzinfo=self)
|
||||||
|
|
||||||
|
def normalize(self, dt, is_dst=False):
|
||||||
|
'''Correct the timezone information on the given datetime.
|
||||||
|
|
||||||
|
This is normally a no-op, as StaticTzInfo timezones never have
|
||||||
|
ambiguous cases to correct:
|
||||||
|
|
||||||
|
>>> from pytz import timezone
|
||||||
|
>>> gmt = timezone('GMT')
|
||||||
|
>>> isinstance(gmt, StaticTzInfo)
|
||||||
|
True
|
||||||
|
>>> dt = datetime(2011, 5, 8, 1, 2, 3, tzinfo=gmt)
|
||||||
|
>>> gmt.normalize(dt) is dt
|
||||||
|
True
|
||||||
|
|
||||||
|
The supported method of converting between timezones is to use
|
||||||
|
datetime.astimezone(). Currently normalize() also works:
|
||||||
|
|
||||||
|
>>> la = timezone('America/Los_Angeles')
|
||||||
|
>>> dt = la.localize(datetime(2011, 5, 7, 1, 2, 3))
|
||||||
|
>>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
|
||||||
|
>>> gmt.normalize(dt).strftime(fmt)
|
||||||
|
'2011-05-07 08:02:03 GMT (+0000)'
|
||||||
|
'''
|
||||||
|
if dt.tzinfo is self:
|
||||||
|
return dt
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
raise ValueError('Naive time - no tzinfo set')
|
||||||
|
return dt.astimezone(self)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<StaticTzInfo %r>' % (self.zone,)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
# Special pickle to zone remains a singleton and to cope with
|
||||||
|
# database changes.
|
||||||
|
return pytz._p, (self.zone,)
|
||||||
|
|
||||||
|
|
||||||
|
class DstTzInfo(BaseTzInfo):
|
||||||
|
'''A timezone that has a variable offset from UTC
|
||||||
|
|
||||||
|
The offset might change if daylight saving time comes into effect,
|
||||||
|
or at a point in history when the region decides to change their
|
||||||
|
timezone definition.
|
||||||
|
'''
|
||||||
|
# Overridden in subclass
|
||||||
|
_utc_transition_times = None # Sorted list of DST transition times in UTC
|
||||||
|
_transition_info = None # [(utcoffset, dstoffset, tzname)] corresponding
|
||||||
|
# to _utc_transition_times entries
|
||||||
|
zone = None
|
||||||
|
|
||||||
|
# Set in __init__
|
||||||
|
_tzinfos = None
|
||||||
|
_dst = None # DST offset
|
||||||
|
|
||||||
|
def __init__(self, _inf=None, _tzinfos=None):
|
||||||
|
if _inf:
|
||||||
|
self._tzinfos = _tzinfos
|
||||||
|
self._utcoffset, self._dst, self._tzname = _inf
|
||||||
|
else:
|
||||||
|
_tzinfos = {}
|
||||||
|
self._tzinfos = _tzinfos
|
||||||
|
self._utcoffset, self._dst, self._tzname = self._transition_info[0]
|
||||||
|
_tzinfos[self._transition_info[0]] = self
|
||||||
|
for inf in self._transition_info[1:]:
|
||||||
|
if inf not in _tzinfos:
|
||||||
|
_tzinfos[inf] = self.__class__(inf, _tzinfos)
|
||||||
|
|
||||||
|
def fromutc(self, dt):
|
||||||
|
'''See datetime.tzinfo.fromutc'''
|
||||||
|
if (dt.tzinfo is not None
|
||||||
|
and getattr(dt.tzinfo, '_tzinfos', None) is not self._tzinfos):
|
||||||
|
raise ValueError('fromutc: dt.tzinfo is not self')
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
idx = max(0, bisect_right(self._utc_transition_times, dt) - 1)
|
||||||
|
inf = self._transition_info[idx]
|
||||||
|
return (dt + inf[0]).replace(tzinfo=self._tzinfos[inf])
|
||||||
|
|
||||||
|
def normalize(self, dt):
|
||||||
|
'''Correct the timezone information on the given datetime
|
||||||
|
|
||||||
|
If date arithmetic crosses DST boundaries, the tzinfo
|
||||||
|
is not magically adjusted. This method normalizes the
|
||||||
|
tzinfo to the correct one.
|
||||||
|
|
||||||
|
To test, first we need to do some setup
|
||||||
|
|
||||||
|
>>> from pytz import timezone
|
||||||
|
>>> utc = timezone('UTC')
|
||||||
|
>>> eastern = timezone('US/Eastern')
|
||||||
|
>>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
|
||||||
|
|
||||||
|
We next create a datetime right on an end-of-DST transition point,
|
||||||
|
the instant when the wallclocks are wound back one hour.
|
||||||
|
|
||||||
|
>>> 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)'
|
||||||
|
|
||||||
|
Now, if we subtract a few minutes from it, note that the timezone
|
||||||
|
information has not changed.
|
||||||
|
|
||||||
|
>>> before = loc_dt - timedelta(minutes=10)
|
||||||
|
>>> before.strftime(fmt)
|
||||||
|
'2002-10-27 00:50:00 EST (-0500)'
|
||||||
|
|
||||||
|
But we can fix that by calling the normalize method
|
||||||
|
|
||||||
|
>>> before = eastern.normalize(before)
|
||||||
|
>>> before.strftime(fmt)
|
||||||
|
'2002-10-27 01:50:00 EDT (-0400)'
|
||||||
|
|
||||||
|
The supported method of converting between timezones is to use
|
||||||
|
datetime.astimezone(). Currently, normalize() also works:
|
||||||
|
|
||||||
|
>>> th = timezone('Asia/Bangkok')
|
||||||
|
>>> am = timezone('Europe/Amsterdam')
|
||||||
|
>>> dt = th.localize(datetime(2011, 5, 7, 1, 2, 3))
|
||||||
|
>>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
|
||||||
|
>>> am.normalize(dt).strftime(fmt)
|
||||||
|
'2011-05-06 20:02:03 CEST (+0200)'
|
||||||
|
'''
|
||||||
|
if dt.tzinfo is None:
|
||||||
|
raise ValueError('Naive time - no tzinfo set')
|
||||||
|
|
||||||
|
# Convert dt in localtime to UTC
|
||||||
|
offset = dt.tzinfo._utcoffset
|
||||||
|
dt = dt.replace(tzinfo=None)
|
||||||
|
dt = dt - offset
|
||||||
|
# convert it back, and return it
|
||||||
|
return self.fromutc(dt)
|
||||||
|
|
||||||
|
def localize(self, dt, is_dst=False):
|
||||||
|
'''Convert naive time to local time.
|
||||||
|
|
||||||
|
This method should be used to construct localtimes, rather
|
||||||
|
than passing a tzinfo argument to a datetime constructor.
|
||||||
|
|
||||||
|
is_dst is used to determine the correct timezone in the ambigous
|
||||||
|
period at the end of daylight saving time.
|
||||||
|
|
||||||
|
>>> from pytz import timezone
|
||||||
|
>>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
|
||||||
|
>>> amdam = timezone('Europe/Amsterdam')
|
||||||
|
>>> dt = datetime(2004, 10, 31, 2, 0, 0)
|
||||||
|
>>> loc_dt1 = amdam.localize(dt, is_dst=True)
|
||||||
|
>>> loc_dt2 = amdam.localize(dt, is_dst=False)
|
||||||
|
>>> loc_dt1.strftime(fmt)
|
||||||
|
'2004-10-31 02:00:00 CEST (+0200)'
|
||||||
|
>>> loc_dt2.strftime(fmt)
|
||||||
|
'2004-10-31 02:00:00 CET (+0100)'
|
||||||
|
>>> str(loc_dt2 - loc_dt1)
|
||||||
|
'1:00:00'
|
||||||
|
|
||||||
|
Use is_dst=None to raise an AmbiguousTimeError for ambiguous
|
||||||
|
times at the end of daylight saving time
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... loc_dt1 = amdam.localize(dt, is_dst=None)
|
||||||
|
... except AmbiguousTimeError:
|
||||||
|
... print('Ambiguous')
|
||||||
|
Ambiguous
|
||||||
|
|
||||||
|
is_dst defaults to False
|
||||||
|
|
||||||
|
>>> amdam.localize(dt) == amdam.localize(dt, False)
|
||||||
|
True
|
||||||
|
|
||||||
|
is_dst is also used to determine the correct timezone in the
|
||||||
|
wallclock times jumped over at the start of daylight saving time.
|
||||||
|
|
||||||
|
>>> pacific = timezone('US/Pacific')
|
||||||
|
>>> dt = datetime(2008, 3, 9, 2, 0, 0)
|
||||||
|
>>> ploc_dt1 = pacific.localize(dt, is_dst=True)
|
||||||
|
>>> ploc_dt2 = pacific.localize(dt, is_dst=False)
|
||||||
|
>>> ploc_dt1.strftime(fmt)
|
||||||
|
'2008-03-09 02:00:00 PDT (-0700)'
|
||||||
|
>>> ploc_dt2.strftime(fmt)
|
||||||
|
'2008-03-09 02:00:00 PST (-0800)'
|
||||||
|
>>> str(ploc_dt2 - ploc_dt1)
|
||||||
|
'1:00:00'
|
||||||
|
|
||||||
|
Use is_dst=None to raise a NonExistentTimeError for these skipped
|
||||||
|
times.
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... loc_dt1 = pacific.localize(dt, is_dst=None)
|
||||||
|
... except NonExistentTimeError:
|
||||||
|
... print('Non-existent')
|
||||||
|
Non-existent
|
||||||
|
'''
|
||||||
|
if dt.tzinfo is not None:
|
||||||
|
raise ValueError('Not naive datetime (tzinfo is already set)')
|
||||||
|
|
||||||
|
# Find the two best possibilities.
|
||||||
|
possible_loc_dt = set()
|
||||||
|
for delta in [timedelta(days=-1), timedelta(days=1)]:
|
||||||
|
loc_dt = dt + delta
|
||||||
|
idx = max(0, bisect_right(
|
||||||
|
self._utc_transition_times, loc_dt) - 1)
|
||||||
|
inf = self._transition_info[idx]
|
||||||
|
tzinfo = self._tzinfos[inf]
|
||||||
|
loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo))
|
||||||
|
if loc_dt.replace(tzinfo=None) == dt:
|
||||||
|
possible_loc_dt.add(loc_dt)
|
||||||
|
|
||||||
|
if len(possible_loc_dt) == 1:
|
||||||
|
return possible_loc_dt.pop()
|
||||||
|
|
||||||
|
# If there are no possibly correct timezones, we are attempting
|
||||||
|
# to convert a time that never happened - the time period jumped
|
||||||
|
# during the start-of-DST transition period.
|
||||||
|
if len(possible_loc_dt) == 0:
|
||||||
|
# If we refuse to guess, raise an exception.
|
||||||
|
if is_dst is None:
|
||||||
|
raise NonExistentTimeError(dt)
|
||||||
|
|
||||||
|
# If we are forcing the pre-DST side of the DST transition, we
|
||||||
|
# obtain the correct timezone by winding the clock forward a few
|
||||||
|
# hours.
|
||||||
|
elif is_dst:
|
||||||
|
return self.localize(
|
||||||
|
dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6)
|
||||||
|
|
||||||
|
# If we are forcing the post-DST side of the DST transition, we
|
||||||
|
# obtain the correct timezone by winding the clock back.
|
||||||
|
else:
|
||||||
|
return self.localize(
|
||||||
|
dt - timedelta(hours=6), is_dst=False) + timedelta(hours=6)
|
||||||
|
|
||||||
|
|
||||||
|
# If we get this far, we have multiple possible timezones - this
|
||||||
|
# is an ambiguous case occuring during the end-of-DST transition.
|
||||||
|
|
||||||
|
# If told to be strict, raise an exception since we have an
|
||||||
|
# ambiguous case
|
||||||
|
if is_dst is None:
|
||||||
|
raise AmbiguousTimeError(dt)
|
||||||
|
|
||||||
|
# Filter out the possiblilities that don't match the requested
|
||||||
|
# is_dst
|
||||||
|
filtered_possible_loc_dt = [
|
||||||
|
p for p in possible_loc_dt
|
||||||
|
if bool(p.tzinfo._dst) == is_dst
|
||||||
|
]
|
||||||
|
|
||||||
|
# Hopefully we only have one possibility left. Return it.
|
||||||
|
if len(filtered_possible_loc_dt) == 1:
|
||||||
|
return filtered_possible_loc_dt[0]
|
||||||
|
|
||||||
|
if len(filtered_possible_loc_dt) == 0:
|
||||||
|
filtered_possible_loc_dt = list(possible_loc_dt)
|
||||||
|
|
||||||
|
# If we get this far, we have in a wierd timezone transition
|
||||||
|
# where the clocks have been wound back but is_dst is the same
|
||||||
|
# in both (eg. Europe/Warsaw 1915 when they switched to CET).
|
||||||
|
# At this point, we just have to guess unless we allow more
|
||||||
|
# hints to be passed in (such as the UTC offset or abbreviation),
|
||||||
|
# but that is just getting silly.
|
||||||
|
#
|
||||||
|
# Choose the earliest (by UTC) applicable timezone if is_dst=True
|
||||||
|
# Choose the latest (by UTC) applicable timezone if is_dst=False
|
||||||
|
# i.e., behave like end-of-DST transition
|
||||||
|
dates = {} # utc -> local
|
||||||
|
for local_dt in filtered_possible_loc_dt:
|
||||||
|
utc_time = local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset
|
||||||
|
assert utc_time not in dates
|
||||||
|
dates[utc_time] = local_dt
|
||||||
|
return dates[[min, max][not is_dst](dates)]
|
||||||
|
|
||||||
|
def utcoffset(self, dt, is_dst=None):
|
||||||
|
'''See datetime.tzinfo.utcoffset
|
||||||
|
|
||||||
|
The is_dst parameter may be used to remove ambiguity during DST
|
||||||
|
transitions.
|
||||||
|
|
||||||
|
>>> from pytz import timezone
|
||||||
|
>>> tz = timezone('America/St_Johns')
|
||||||
|
>>> ambiguous = datetime(2009, 10, 31, 23, 30)
|
||||||
|
|
||||||
|
>>> tz.utcoffset(ambiguous, is_dst=False)
|
||||||
|
datetime.timedelta(-1, 73800)
|
||||||
|
|
||||||
|
>>> tz.utcoffset(ambiguous, is_dst=True)
|
||||||
|
datetime.timedelta(-1, 77400)
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... tz.utcoffset(ambiguous)
|
||||||
|
... except AmbiguousTimeError:
|
||||||
|
... print('Ambiguous')
|
||||||
|
Ambiguous
|
||||||
|
|
||||||
|
'''
|
||||||
|
if dt is None:
|
||||||
|
return None
|
||||||
|
elif dt.tzinfo is not self:
|
||||||
|
dt = self.localize(dt, is_dst)
|
||||||
|
return dt.tzinfo._utcoffset
|
||||||
|
else:
|
||||||
|
return self._utcoffset
|
||||||
|
|
||||||
|
def dst(self, dt, is_dst=None):
|
||||||
|
'''See datetime.tzinfo.dst
|
||||||
|
|
||||||
|
The is_dst parameter may be used to remove ambiguity during DST
|
||||||
|
transitions.
|
||||||
|
|
||||||
|
>>> from pytz import timezone
|
||||||
|
>>> tz = timezone('America/St_Johns')
|
||||||
|
|
||||||
|
>>> normal = datetime(2009, 9, 1)
|
||||||
|
|
||||||
|
>>> tz.dst(normal)
|
||||||
|
datetime.timedelta(0, 3600)
|
||||||
|
>>> tz.dst(normal, is_dst=False)
|
||||||
|
datetime.timedelta(0, 3600)
|
||||||
|
>>> tz.dst(normal, is_dst=True)
|
||||||
|
datetime.timedelta(0, 3600)
|
||||||
|
|
||||||
|
>>> ambiguous = datetime(2009, 10, 31, 23, 30)
|
||||||
|
|
||||||
|
>>> tz.dst(ambiguous, is_dst=False)
|
||||||
|
datetime.timedelta(0)
|
||||||
|
>>> tz.dst(ambiguous, is_dst=True)
|
||||||
|
datetime.timedelta(0, 3600)
|
||||||
|
>>> try:
|
||||||
|
... tz.dst(ambiguous)
|
||||||
|
... except AmbiguousTimeError:
|
||||||
|
... print('Ambiguous')
|
||||||
|
Ambiguous
|
||||||
|
|
||||||
|
'''
|
||||||
|
if dt is None:
|
||||||
|
return None
|
||||||
|
elif dt.tzinfo is not self:
|
||||||
|
dt = self.localize(dt, is_dst)
|
||||||
|
return dt.tzinfo._dst
|
||||||
|
else:
|
||||||
|
return self._dst
|
||||||
|
|
||||||
|
def tzname(self, dt, is_dst=None):
|
||||||
|
'''See datetime.tzinfo.tzname
|
||||||
|
|
||||||
|
The is_dst parameter may be used to remove ambiguity during DST
|
||||||
|
transitions.
|
||||||
|
|
||||||
|
>>> from pytz import timezone
|
||||||
|
>>> tz = timezone('America/St_Johns')
|
||||||
|
|
||||||
|
>>> normal = datetime(2009, 9, 1)
|
||||||
|
|
||||||
|
>>> tz.tzname(normal)
|
||||||
|
'NDT'
|
||||||
|
>>> tz.tzname(normal, is_dst=False)
|
||||||
|
'NDT'
|
||||||
|
>>> tz.tzname(normal, is_dst=True)
|
||||||
|
'NDT'
|
||||||
|
|
||||||
|
>>> ambiguous = datetime(2009, 10, 31, 23, 30)
|
||||||
|
|
||||||
|
>>> tz.tzname(ambiguous, is_dst=False)
|
||||||
|
'NST'
|
||||||
|
>>> tz.tzname(ambiguous, is_dst=True)
|
||||||
|
'NDT'
|
||||||
|
>>> try:
|
||||||
|
... tz.tzname(ambiguous)
|
||||||
|
... except AmbiguousTimeError:
|
||||||
|
... print('Ambiguous')
|
||||||
|
Ambiguous
|
||||||
|
'''
|
||||||
|
if dt is None:
|
||||||
|
return self.zone
|
||||||
|
elif dt.tzinfo is not self:
|
||||||
|
dt = self.localize(dt, is_dst)
|
||||||
|
return dt.tzinfo._tzname
|
||||||
|
else:
|
||||||
|
return self._tzname
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if self._dst:
|
||||||
|
dst = 'DST'
|
||||||
|
else:
|
||||||
|
dst = 'STD'
|
||||||
|
if self._utcoffset > _notime:
|
||||||
|
return '<DstTzInfo %r %s+%s %s>' % (
|
||||||
|
self.zone, self._tzname, self._utcoffset, dst
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return '<DstTzInfo %r %s%s %s>' % (
|
||||||
|
self.zone, self._tzname, self._utcoffset, dst
|
||||||
|
)
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
# Special pickle to zone remains a singleton and to cope with
|
||||||
|
# database changes.
|
||||||
|
return pytz._p, (
|
||||||
|
self.zone,
|
||||||
|
_to_seconds(self._utcoffset),
|
||||||
|
_to_seconds(self._dst),
|
||||||
|
self._tzname
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def unpickler(zone, utcoffset=None, dstoffset=None, tzname=None):
|
||||||
|
"""Factory function for unpickling pytz tzinfo instances.
|
||||||
|
|
||||||
|
This is shared for both StaticTzInfo and DstTzInfo instances, because
|
||||||
|
database changes could cause a zones implementation to switch between
|
||||||
|
these two base classes and we can't break pickles on a pytz version
|
||||||
|
upgrade.
|
||||||
|
"""
|
||||||
|
# Raises a KeyError if zone no longer exists, which should never happen
|
||||||
|
# and would be a bug.
|
||||||
|
tz = pytz.timezone(zone)
|
||||||
|
|
||||||
|
# A StaticTzInfo - just return it
|
||||||
|
if utcoffset is None:
|
||||||
|
return tz
|
||||||
|
|
||||||
|
# This pickle was created from a DstTzInfo. We need to
|
||||||
|
# determine which of the list of tzinfo instances for this zone
|
||||||
|
# to use in order to restore the state of any datetime instances using
|
||||||
|
# it correctly.
|
||||||
|
utcoffset = memorized_timedelta(utcoffset)
|
||||||
|
dstoffset = memorized_timedelta(dstoffset)
|
||||||
|
try:
|
||||||
|
return tz._tzinfos[(utcoffset, dstoffset, tzname)]
|
||||||
|
except KeyError:
|
||||||
|
# The particular state requested in this timezone no longer exists.
|
||||||
|
# This indicates a corrupt pickle, or the timezone database has been
|
||||||
|
# corrected violently enough to make this particular
|
||||||
|
# (utcoffset,dstoffset) no longer exist in the zone, or the
|
||||||
|
# abbreviation has been changed.
|
||||||
|
pass
|
||||||
|
|
||||||
|
# See if we can find an entry differing only by tzname. Abbreviations
|
||||||
|
# get changed from the initial guess by the database maintainers to
|
||||||
|
# match reality when this information is discovered.
|
||||||
|
for localized_tz in tz._tzinfos.values():
|
||||||
|
if (localized_tz._utcoffset == utcoffset
|
||||||
|
and localized_tz._dst == dstoffset):
|
||||||
|
return localized_tz
|
||||||
|
|
||||||
|
# This (utcoffset, dstoffset) information has been removed from the
|
||||||
|
# zone. Add it back. This might occur when the database maintainers have
|
||||||
|
# corrected incorrect information. datetime instances using this
|
||||||
|
# incorrect information will continue to do so, exactly as they were
|
||||||
|
# before being pickled. This is purely an overly paranoid safety net - I
|
||||||
|
# doubt this will ever been needed in real life.
|
||||||
|
inf = (utcoffset, dstoffset, tzname)
|
||||||
|
tz._tzinfos[inf] = tz.__class__(inf, tz._tzinfos)
|
||||||
|
return tz._tzinfos[inf]
|
BIN
lib/pytz/zoneinfo/Africa/Abidjan
Normal file
BIN
lib/pytz/zoneinfo/Africa/Abidjan
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Accra
Normal file
BIN
lib/pytz/zoneinfo/Africa/Accra
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Addis_Ababa
Normal file
BIN
lib/pytz/zoneinfo/Africa/Addis_Ababa
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Algiers
Normal file
BIN
lib/pytz/zoneinfo/Africa/Algiers
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Asmara
Normal file
BIN
lib/pytz/zoneinfo/Africa/Asmara
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Asmera
Normal file
BIN
lib/pytz/zoneinfo/Africa/Asmera
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Bamako
Normal file
BIN
lib/pytz/zoneinfo/Africa/Bamako
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Bangui
Normal file
BIN
lib/pytz/zoneinfo/Africa/Bangui
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Banjul
Normal file
BIN
lib/pytz/zoneinfo/Africa/Banjul
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Bissau
Normal file
BIN
lib/pytz/zoneinfo/Africa/Bissau
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Blantyre
Normal file
BIN
lib/pytz/zoneinfo/Africa/Blantyre
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Brazzaville
Normal file
BIN
lib/pytz/zoneinfo/Africa/Brazzaville
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Bujumbura
Normal file
BIN
lib/pytz/zoneinfo/Africa/Bujumbura
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Cairo
Normal file
BIN
lib/pytz/zoneinfo/Africa/Cairo
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Casablanca
Normal file
BIN
lib/pytz/zoneinfo/Africa/Casablanca
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Ceuta
Normal file
BIN
lib/pytz/zoneinfo/Africa/Ceuta
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Conakry
Normal file
BIN
lib/pytz/zoneinfo/Africa/Conakry
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Dakar
Normal file
BIN
lib/pytz/zoneinfo/Africa/Dakar
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Dar_es_Salaam
Normal file
BIN
lib/pytz/zoneinfo/Africa/Dar_es_Salaam
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Djibouti
Normal file
BIN
lib/pytz/zoneinfo/Africa/Djibouti
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Douala
Normal file
BIN
lib/pytz/zoneinfo/Africa/Douala
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/El_Aaiun
Normal file
BIN
lib/pytz/zoneinfo/Africa/El_Aaiun
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Freetown
Normal file
BIN
lib/pytz/zoneinfo/Africa/Freetown
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Gaborone
Normal file
BIN
lib/pytz/zoneinfo/Africa/Gaborone
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Harare
Normal file
BIN
lib/pytz/zoneinfo/Africa/Harare
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Johannesburg
Normal file
BIN
lib/pytz/zoneinfo/Africa/Johannesburg
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Juba
Normal file
BIN
lib/pytz/zoneinfo/Africa/Juba
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Kampala
Normal file
BIN
lib/pytz/zoneinfo/Africa/Kampala
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Khartoum
Normal file
BIN
lib/pytz/zoneinfo/Africa/Khartoum
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Kigali
Normal file
BIN
lib/pytz/zoneinfo/Africa/Kigali
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Kinshasa
Normal file
BIN
lib/pytz/zoneinfo/Africa/Kinshasa
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Lagos
Normal file
BIN
lib/pytz/zoneinfo/Africa/Lagos
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Libreville
Normal file
BIN
lib/pytz/zoneinfo/Africa/Libreville
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Lome
Normal file
BIN
lib/pytz/zoneinfo/Africa/Lome
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Luanda
Normal file
BIN
lib/pytz/zoneinfo/Africa/Luanda
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Lubumbashi
Normal file
BIN
lib/pytz/zoneinfo/Africa/Lubumbashi
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Lusaka
Normal file
BIN
lib/pytz/zoneinfo/Africa/Lusaka
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Malabo
Normal file
BIN
lib/pytz/zoneinfo/Africa/Malabo
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Maputo
Normal file
BIN
lib/pytz/zoneinfo/Africa/Maputo
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Maseru
Normal file
BIN
lib/pytz/zoneinfo/Africa/Maseru
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Mbabane
Normal file
BIN
lib/pytz/zoneinfo/Africa/Mbabane
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Mogadishu
Normal file
BIN
lib/pytz/zoneinfo/Africa/Mogadishu
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Monrovia
Normal file
BIN
lib/pytz/zoneinfo/Africa/Monrovia
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Nairobi
Normal file
BIN
lib/pytz/zoneinfo/Africa/Nairobi
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Ndjamena
Normal file
BIN
lib/pytz/zoneinfo/Africa/Ndjamena
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Niamey
Normal file
BIN
lib/pytz/zoneinfo/Africa/Niamey
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Nouakchott
Normal file
BIN
lib/pytz/zoneinfo/Africa/Nouakchott
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Ouagadougou
Normal file
BIN
lib/pytz/zoneinfo/Africa/Ouagadougou
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Porto-Novo
Normal file
BIN
lib/pytz/zoneinfo/Africa/Porto-Novo
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Sao_Tome
Normal file
BIN
lib/pytz/zoneinfo/Africa/Sao_Tome
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Timbuktu
Normal file
BIN
lib/pytz/zoneinfo/Africa/Timbuktu
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Tripoli
Normal file
BIN
lib/pytz/zoneinfo/Africa/Tripoli
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Tunis
Normal file
BIN
lib/pytz/zoneinfo/Africa/Tunis
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/Africa/Windhoek
Normal file
BIN
lib/pytz/zoneinfo/Africa/Windhoek
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Adak
Normal file
BIN
lib/pytz/zoneinfo/America/Adak
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Anchorage
Normal file
BIN
lib/pytz/zoneinfo/America/Anchorage
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Anguilla
Normal file
BIN
lib/pytz/zoneinfo/America/Anguilla
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Antigua
Normal file
BIN
lib/pytz/zoneinfo/America/Antigua
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Araguaina
Normal file
BIN
lib/pytz/zoneinfo/America/Araguaina
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Buenos_Aires
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Buenos_Aires
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Catamarca
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Catamarca
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/ComodRivadavia
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/ComodRivadavia
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Cordoba
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Cordoba
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Jujuy
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Jujuy
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/La_Rioja
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/La_Rioja
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Mendoza
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Mendoza
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Rio_Gallegos
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Rio_Gallegos
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Salta
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Salta
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/San_Juan
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/San_Juan
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/San_Luis
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/San_Luis
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Tucuman
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Tucuman
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Argentina/Ushuaia
Normal file
BIN
lib/pytz/zoneinfo/America/Argentina/Ushuaia
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Aruba
Normal file
BIN
lib/pytz/zoneinfo/America/Aruba
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Asuncion
Normal file
BIN
lib/pytz/zoneinfo/America/Asuncion
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Atikokan
Normal file
BIN
lib/pytz/zoneinfo/America/Atikokan
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Atka
Normal file
BIN
lib/pytz/zoneinfo/America/Atka
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Bahia
Normal file
BIN
lib/pytz/zoneinfo/America/Bahia
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Bahia_Banderas
Normal file
BIN
lib/pytz/zoneinfo/America/Bahia_Banderas
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Barbados
Normal file
BIN
lib/pytz/zoneinfo/America/Barbados
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Belem
Normal file
BIN
lib/pytz/zoneinfo/America/Belem
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Belize
Normal file
BIN
lib/pytz/zoneinfo/America/Belize
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Blanc-Sablon
Normal file
BIN
lib/pytz/zoneinfo/America/Blanc-Sablon
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Boa_Vista
Normal file
BIN
lib/pytz/zoneinfo/America/Boa_Vista
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Bogota
Normal file
BIN
lib/pytz/zoneinfo/America/Bogota
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Boise
Normal file
BIN
lib/pytz/zoneinfo/America/Boise
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Buenos_Aires
Normal file
BIN
lib/pytz/zoneinfo/America/Buenos_Aires
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Cambridge_Bay
Normal file
BIN
lib/pytz/zoneinfo/America/Cambridge_Bay
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Campo_Grande
Normal file
BIN
lib/pytz/zoneinfo/America/Campo_Grande
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Cancun
Normal file
BIN
lib/pytz/zoneinfo/America/Cancun
Normal file
Binary file not shown.
BIN
lib/pytz/zoneinfo/America/Caracas
Normal file
BIN
lib/pytz/zoneinfo/America/Caracas
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue