diff options
Diffstat (limited to 'Windows/dateutil/test/test_tz.py')
-rw-r--r-- | Windows/dateutil/test/test_tz.py | 2781 |
1 files changed, 0 insertions, 2781 deletions
diff --git a/Windows/dateutil/test/test_tz.py b/Windows/dateutil/test/test_tz.py deleted file mode 100644 index bb0f4b7d..00000000 --- a/Windows/dateutil/test/test_tz.py +++ /dev/null @@ -1,2781 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from ._common import PicklableMixin -from ._common import TZEnvContext, TZWinContext -from ._common import WarningTestMixin -from ._common import ComparesEqual - -from datetime import datetime, timedelta -from datetime import time as dt_time -from datetime import tzinfo -from six import PY2 -from io import BytesIO, StringIO -import unittest - -import sys -import base64 -import copy -import gc -import weakref - -from functools import partial - -IS_WIN = sys.platform.startswith('win') - -import pytest - -# dateutil imports -from dateutil.relativedelta import relativedelta, SU, TH -from dateutil.parser import parse -from dateutil import tz as tz -from dateutil import zoneinfo - -try: - from dateutil import tzwin -except ImportError as e: - if IS_WIN: - raise e - else: - pass - -MISSING_TARBALL = ("This test fails if you don't have the dateutil " - "timezone file installed. Please read the README") - -TZFILE_EST5EDT = b""" -VFppZgAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAADrAAAABAAAABCeph5wn7rrYKCGAHCh -ms1gomXicKOD6eCkaq5wpTWnYKZTyvCnFYlgqDOs8Kj+peCqE47wqt6H4KvzcPCsvmngrdNS8K6e -S+CvszTwsH4t4LGcUXCyZ0pgs3wzcLRHLGC1XBVwticOYLc793C4BvBguRvZcLnm0mC7BPXwu8a0 -YLzk1/C9r9DgvsS58L+PsuDApJvwwW+U4MKEffDDT3bgxGRf8MUvWODGTXxwxw864MgtXnDI+Fdg -yg1AcMrYOWDLiPBw0iP0cNJg++DTdeTw1EDd4NVVxvDWIL/g1zWo8NgAoeDZFYrw2eCD4Nr+p3Db -wGXg3N6JcN2pgmDevmtw34lkYOCeTXDhaUZg4n4vcONJKGDkXhFw5Vcu4OZHLfDnNxDg6CcP8OkW -8uDqBvHw6vbU4Ovm0/Ds1rbg7ca18O6/02Dvr9Jw8J+1YPGPtHDyf5dg82+WcPRfeWD1T3hw9j9b -YPcvWnD4KHfg+Q88cPoIWeD6+Fjw++g74PzYOvD9yB3g/rgc8P+n/+AAl/7wAYfh4AJ34PADcP5g -BGD9cAVQ4GAGQN9wBzDCYAeNGXAJEKRgCa2U8ArwhmAL4IVwDNmi4A3AZ3AOuYTgD6mD8BCZZuAR -iWXwEnlI4BNpR/AUWSrgFUkp8BY5DOAXKQvwGCIpYBkI7fAaAgtgGvIKcBvh7WAc0exwHcHPYB6x -znAfobFgIHYA8CGBk2AiVeLwI2qv4CQ1xPAlSpHgJhWm8Ccqc+An/sNwKQpV4CnepXAq6jfgK76H -cCzTVGAtnmlwLrM2YC9+S3AwkxhgMWdn8DJy+mAzR0nwNFLcYDUnK/A2Mr5gNwcN8Dgb2uA45u/w -Ofu84DrG0fA7257gPK/ucD27gOA+j9BwP5ti4EBvsnBBhH9gQk+UcENkYWBEL3ZwRURDYEYPWHBH -JCVgR/h08EkEB2BJ2FbwSuPpYEu4OPBMzQXgTZga8E6s5+BPd/zwUIzJ4FFhGXBSbKvgU0D7cFRM -jeBVIN1wVixv4FcAv3BYFYxgWOChcFn1bmBawINwW9VQYFypn/BdtTJgXomB8F+VFGBgaWPwYX4w -4GJJRfBjXhLgZCkn8GU99OBmEkRwZx3W4GfyJnBo/bjgadIIcGrdmuBrsepwbMa3YG2RzHBupplg -b3GucHCGe2BxWsrwcmZdYHM6rPB0Rj9gdRqO8HYvW+B2+nDweA894HjaUvB57x/gero08HvPAeB8 -o1Fwfa7j4H6DM3B/jsXgAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAB -AAEAAQABAgMBAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAB -AAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEA -AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAB -AAEAAQABAAEAAQABAAEAAQABAAEAAf//x8ABAP//ubAABP//x8ABCP//x8ABDEVEVABFU1QARVdU -AEVQVAAAAAABAAAAAQ== -""" - -EUROPE_HELSINKI = b""" -VFppZgAAAAAAAAAAAAAAAAAAAAAAAAAFAAAABQAAAAAAAAB1AAAABQAAAA2kc28Yy85RYMy/hdAV -I+uQFhPckBcDzZAX876QGOOvkBnToJAaw5GQG7y9EBysrhAdnJ8QHoyQEB98gRAgbHIQIVxjECJM -VBAjPEUQJCw2ECUcJxAmDBgQJwVDkCf1NJAo5SWQKdUWkCrFB5ArtPiQLKTpkC2U2pAuhMuQL3S8 -kDBkrZAxXdkQMnK0EDM9uxA0UpYQNR2dEDYyeBA2/X8QOBuUkDjdYRA5+3aQOr1DEDvbWJA8pl+Q -Pbs6kD6GQZA/mxyQQGYjkEGEORBCRgWQQ2QbEEQl55BFQ/0QRgXJkEcj3xBH7uYQSQPBEEnOyBBK -46MQS66qEEzMv5BNjowQTqyhkE9ubhBQjIOQUVeKkFJsZZBTN2yQVExHkFUXTpBWLCmQVvcwkFgV -RhBY1xKQWfUoEFq29JBb1QoQXKAREF207BBef/MQX5TOEGBf1RBhfeqQYj+3EGNdzJBkH5kQZT2u -kGYItZBnHZCQZ+iXkGj9cpBpyHmQat1UkGuoW5BsxnEQbYg9kG6mUxBvaB+QcIY1EHFRPBByZhcQ -czEeEHRF+RB1EQAQdi8VkHbw4hB4DveQeNDEEHnu2ZB6sKYQe867kHyZwpB9rp2QfnmkkH+Of5AC -AQIDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQD -BAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAMEAwQDBAME -AwQAABdoAAAAACowAQQAABwgAAkAACowAQQAABwgAAlITVQARUVTVABFRVQAAAAAAQEAAAABAQ== -""" - -NEW_YORK = b""" -VFppZgAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAABcAAADrAAAABAAAABCeph5wn7rrYKCGAHCh -ms1gomXicKOD6eCkaq5wpTWnYKZTyvCnFYlgqDOs8Kj+peCqE47wqt6H4KvzcPCsvmngrdNS8K6e -S+CvszTwsH4t4LGcUXCyZ0pgs3wzcLRHLGC1XBVwticOYLc793C4BvBguRvZcLnm0mC7BPXwu8a0 -YLzk1/C9r9DgvsS58L+PsuDApJvwwW+U4MKEffDDT3bgxGRf8MUvWODGTXxwxw864MgtXnDI+Fdg -yg1AcMrYOWDLiPBw0iP0cNJg++DTdeTw1EDd4NVVxvDWIL/g1zWo8NgAoeDZFYrw2eCD4Nr+p3Db -wGXg3N6JcN2pgmDevmtw34lkYOCeTXDhaUZg4n4vcONJKGDkXhFw5Vcu4OZHLfDnNxDg6CcP8OkW -8uDqBvHw6vbU4Ovm0/Ds1rbg7ca18O6/02Dvr9Jw8J+1YPGPtHDyf5dg82+WcPRfeWD1T3hw9j9b -YPcvWnD4KHfg+Q88cPoIWeD6+Fjw++g74PzYOvD9yB3g/rgc8P+n/+AAl/7wAYfh4AJ34PADcP5g -BGD9cAVQ4GEGQN9yBzDCYgeNGXMJEKRjCa2U9ArwhmQL4IV1DNmi5Q3AZ3YOuYTmD6mD9xCZZucR -iWX4EnlI6BNpR/kUWSrpFUkp+RY5DOoXKQv6GCIpaxkI7fsaAgtsGvIKfBvh7Wwc0ex8HcHPbR6x -zn0fobFtIHYA/SGBk20iVeL+I2qv7iQ1xP4lSpHuJhWm/ycqc+8n/sOAKQpV8CnepYAq6jfxK76H -gSzTVHItnmmCLrM2cy9+S4MwkxhzMWdoBDJy+nQzR0oENFLcdTUnLAU2Mr51NwcOBjgb2vY45vAG -Ofu89jrG0gY72572PK/uhj27gPY+j9CGP5ti9kBvsoZBhH92Qk+UhkNkYXZEL3aHRURDd0XzqQdH -LV/3R9OLB0kNQfdJs20HSu0j90uciYdM1kB3TXxrh062IndPXE2HUJYEd1E8L4dSdeZ3UxwRh1RV -yHdU+/OHVjWqd1blEAdYHsb3WMTyB1n+qPdapNQHW96K91yEtgddvmz3XmSYB1+eTvdgTbSHYYdr -d2ItlodjZ013ZA14h2VHL3dl7VqHZycRd2fNPIdpBvN3aa0eh2rm1XdrljsHbM/x9212HQdur9P3 -b1X/B3CPtfdxNeEHcm+X93MVwwd0T3n3dP7fh3Y4lnd23sGHeBh4d3i+o4d5+Fp3ep6Fh3vYPHd8 -fmeHfbged35eSYd/mAB3AAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAB -AAEAAQABAgMBAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAB -AAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEA -AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQAB -AAEAAQABAAEAAQABAAEAAQABAAEAAf//x8ABAP//ubAABP//x8ABCP//x8ABDEVEVABFU1QARVdU -AEVQVAAEslgAAAAAAQWk7AEAAAACB4YfggAAAAMJZ1MDAAAABAtIhoQAAAAFDSsLhQAAAAYPDD8G -AAAABxDtcocAAAAIEs6mCAAAAAkVn8qJAAAACheA/goAAAALGWIxiwAAAAwdJeoMAAAADSHa5Q0A -AAAOJZ6djgAAAA8nf9EPAAAAECpQ9ZAAAAARLDIpEQAAABIuE1ySAAAAEzDnJBMAAAAUM7hIlAAA -ABU2jBAVAAAAFkO3G5YAAAAXAAAAAQAAAAE= -""" - -TZICAL_EST5EDT = """ -BEGIN:VTIMEZONE -TZID:US-Eastern -LAST-MODIFIED:19870101T000000Z -TZURL:http://zones.stds_r_us.net/tz/US-Eastern -BEGIN:STANDARD -DTSTART:19671029T020000 -RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 -TZOFFSETFROM:-0400 -TZOFFSETTO:-0500 -TZNAME:EST -END:STANDARD -BEGIN:DAYLIGHT -DTSTART:19870405T020000 -RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 -TZOFFSETFROM:-0500 -TZOFFSETTO:-0400 -TZNAME:EDT -END:DAYLIGHT -END:VTIMEZONE -""" - -TZICAL_PST8PDT = """ -BEGIN:VTIMEZONE -TZID:US-Pacific -LAST-MODIFIED:19870101T000000Z -BEGIN:STANDARD -DTSTART:19671029T020000 -RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 -TZOFFSETFROM:-0700 -TZOFFSETTO:-0800 -TZNAME:PST -END:STANDARD -BEGIN:DAYLIGHT -DTSTART:19870405T020000 -RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 -TZOFFSETFROM:-0800 -TZOFFSETTO:-0700 -TZNAME:PDT -END:DAYLIGHT -END:VTIMEZONE -""" - -EST_TUPLE = ('EST', timedelta(hours=-5), timedelta(hours=0)) -EDT_TUPLE = ('EDT', timedelta(hours=-4), timedelta(hours=1)) - -SUPPORTS_SUB_MINUTE_OFFSETS = sys.version_info >= (3, 6) - - -### -# Helper functions -def get_timezone_tuple(dt): - """Retrieve a (tzname, utcoffset, dst) tuple for a given DST""" - return dt.tzname(), dt.utcoffset(), dt.dst() - - -### -# Mix-ins -class context_passthrough(object): - def __init__(*args, **kwargs): - pass - - def __enter__(*args, **kwargs): - pass - - def __exit__(*args, **kwargs): - pass - - -class TzFoldMixin(object): - """ Mix-in class for testing ambiguous times """ - def gettz(self, tzname): - raise NotImplementedError - - def _get_tzname(self, tzname): - return tzname - - def _gettz_context(self, tzname): - return context_passthrough() - - def testFoldPositiveUTCOffset(self): - # Test that we can resolve ambiguous times - tzname = self._get_tzname('Australia/Sydney') - - with self._gettz_context(tzname): - SYD = self.gettz(tzname) - - t0_u = datetime(2012, 3, 31, 15, 30, tzinfo=tz.tzutc()) # AEST - t1_u = datetime(2012, 3, 31, 16, 30, tzinfo=tz.tzutc()) # AEDT - - t0_syd0 = t0_u.astimezone(SYD) - t1_syd1 = t1_u.astimezone(SYD) - - self.assertEqual(t0_syd0.replace(tzinfo=None), - datetime(2012, 4, 1, 2, 30)) - - self.assertEqual(t1_syd1.replace(tzinfo=None), - datetime(2012, 4, 1, 2, 30)) - - self.assertEqual(t0_syd0.utcoffset(), timedelta(hours=11)) - self.assertEqual(t1_syd1.utcoffset(), timedelta(hours=10)) - - def testGapPositiveUTCOffset(self): - # Test that we don't have a problem around gaps. - tzname = self._get_tzname('Australia/Sydney') - - with self._gettz_context(tzname): - SYD = self.gettz(tzname) - - t0_u = datetime(2012, 10, 6, 15, 30, tzinfo=tz.tzutc()) # AEST - t1_u = datetime(2012, 10, 6, 16, 30, tzinfo=tz.tzutc()) # AEDT - - t0 = t0_u.astimezone(SYD) - t1 = t1_u.astimezone(SYD) - - self.assertEqual(t0.replace(tzinfo=None), - datetime(2012, 10, 7, 1, 30)) - - self.assertEqual(t1.replace(tzinfo=None), - datetime(2012, 10, 7, 3, 30)) - - self.assertEqual(t0.utcoffset(), timedelta(hours=10)) - self.assertEqual(t1.utcoffset(), timedelta(hours=11)) - - def testFoldNegativeUTCOffset(self): - # Test that we can resolve ambiguous times - tzname = self._get_tzname('America/Toronto') - - with self._gettz_context(tzname): - TOR = self.gettz(tzname) - - t0_u = datetime(2011, 11, 6, 5, 30, tzinfo=tz.tzutc()) - t1_u = datetime(2011, 11, 6, 6, 30, tzinfo=tz.tzutc()) - - t0_tor = t0_u.astimezone(TOR) - t1_tor = t1_u.astimezone(TOR) - - self.assertEqual(t0_tor.replace(tzinfo=None), - datetime(2011, 11, 6, 1, 30)) - - self.assertEqual(t1_tor.replace(tzinfo=None), - datetime(2011, 11, 6, 1, 30)) - - self.assertNotEqual(t0_tor.tzname(), t1_tor.tzname()) - self.assertEqual(t0_tor.utcoffset(), timedelta(hours=-4.0)) - self.assertEqual(t1_tor.utcoffset(), timedelta(hours=-5.0)) - - def testGapNegativeUTCOffset(self): - # Test that we don't have a problem around gaps. - tzname = self._get_tzname('America/Toronto') - - with self._gettz_context(tzname): - TOR = self.gettz(tzname) - - t0_u = datetime(2011, 3, 13, 6, 30, tzinfo=tz.tzutc()) - t1_u = datetime(2011, 3, 13, 7, 30, tzinfo=tz.tzutc()) - - t0 = t0_u.astimezone(TOR) - t1 = t1_u.astimezone(TOR) - - self.assertEqual(t0.replace(tzinfo=None), - datetime(2011, 3, 13, 1, 30)) - - self.assertEqual(t1.replace(tzinfo=None), - datetime(2011, 3, 13, 3, 30)) - - self.assertNotEqual(t0, t1) - self.assertEqual(t0.utcoffset(), timedelta(hours=-5.0)) - self.assertEqual(t1.utcoffset(), timedelta(hours=-4.0)) - - def testFoldLondon(self): - tzname = self._get_tzname('Europe/London') - - with self._gettz_context(tzname): - LON = self.gettz(tzname) - UTC = tz.tzutc() - - t0_u = datetime(2013, 10, 27, 0, 30, tzinfo=UTC) # BST - t1_u = datetime(2013, 10, 27, 1, 30, tzinfo=UTC) # GMT - - t0 = t0_u.astimezone(LON) - t1 = t1_u.astimezone(LON) - - self.assertEqual(t0.replace(tzinfo=None), - datetime(2013, 10, 27, 1, 30)) - - self.assertEqual(t1.replace(tzinfo=None), - datetime(2013, 10, 27, 1, 30)) - - self.assertEqual(t0.utcoffset(), timedelta(hours=1)) - self.assertEqual(t1.utcoffset(), timedelta(hours=0)) - - def testFoldIndependence(self): - tzname = self._get_tzname('America/New_York') - - with self._gettz_context(tzname): - NYC = self.gettz(tzname) - UTC = tz.tzutc() - hour = timedelta(hours=1) - - # Firmly 2015-11-01 0:30 EDT-4 - pre_dst = datetime(2015, 11, 1, 0, 30, tzinfo=NYC) - - # Ambiguous between 2015-11-01 1:30 EDT-4 and 2015-11-01 1:30 EST-5 - in_dst = pre_dst + hour - in_dst_tzname_0 = in_dst.tzname() # Stash the tzname - EDT - - # Doing the arithmetic in UTC creates a date that is unambiguously - # 2015-11-01 1:30 EDT-5 - in_dst_via_utc = (pre_dst.astimezone(UTC) + 2*hour).astimezone(NYC) - - # Make sure the dates are actually ambiguous - self.assertEqual(in_dst, in_dst_via_utc) - - # Make sure we got the right folding behavior - self.assertNotEqual(in_dst_via_utc.tzname(), in_dst_tzname_0) - - # Now check to make sure in_dst's tzname hasn't changed - self.assertEqual(in_dst_tzname_0, in_dst.tzname()) - - def testInZoneFoldEquality(self): - # Two datetimes in the same zone are considered to be equal if their - # wall times are equal, even if they have different absolute times. - - tzname = self._get_tzname('America/New_York') - - with self._gettz_context(tzname): - NYC = self.gettz(tzname) - UTC = tz.tzutc() - - dt0 = datetime(2011, 11, 6, 1, 30, tzinfo=NYC) - dt1 = tz.enfold(dt0, fold=1) - - # Make sure these actually represent different times - self.assertNotEqual(dt0.astimezone(UTC), dt1.astimezone(UTC)) - - # Test that they compare equal - self.assertEqual(dt0, dt1) - - def _test_ambiguous_time(self, dt, tzid, ambiguous): - # This is a test to check that the individual is_ambiguous values - # on the _tzinfo subclasses work. - tzname = self._get_tzname(tzid) - - with self._gettz_context(tzname): - tzi = self.gettz(tzname) - - self.assertEqual(tz.datetime_ambiguous(dt, tz=tzi), ambiguous) - - def testAmbiguousNegativeUTCOffset(self): - self._test_ambiguous_time(datetime(2015, 11, 1, 1, 30), - 'America/New_York', True) - - def testAmbiguousPositiveUTCOffset(self): - self._test_ambiguous_time(datetime(2012, 4, 1, 2, 30), - 'Australia/Sydney', True) - - def testUnambiguousNegativeUTCOffset(self): - self._test_ambiguous_time(datetime(2015, 11, 1, 2, 30), - 'America/New_York', False) - - def testUnambiguousPositiveUTCOffset(self): - self._test_ambiguous_time(datetime(2012, 4, 1, 3, 30), - 'Australia/Sydney', False) - - def testUnambiguousGapNegativeUTCOffset(self): - # Imaginary time - self._test_ambiguous_time(datetime(2011, 3, 13, 2, 30), - 'America/New_York', False) - - def testUnambiguousGapPositiveUTCOffset(self): - # Imaginary time - self._test_ambiguous_time(datetime(2012, 10, 7, 2, 30), - 'Australia/Sydney', False) - - def _test_imaginary_time(self, dt, tzid, exists): - tzname = self._get_tzname(tzid) - with self._gettz_context(tzname): - tzi = self.gettz(tzname) - - self.assertEqual(tz.datetime_exists(dt, tz=tzi), exists) - - def testImaginaryNegativeUTCOffset(self): - self._test_imaginary_time(datetime(2011, 3, 13, 2, 30), - 'America/New_York', False) - - def testNotImaginaryNegativeUTCOffset(self): - self._test_imaginary_time(datetime(2011, 3, 13, 1, 30), - 'America/New_York', True) - - def testImaginaryPositiveUTCOffset(self): - self._test_imaginary_time(datetime(2012, 10, 7, 2, 30), - 'Australia/Sydney', False) - - def testNotImaginaryPositiveUTCOffset(self): - self._test_imaginary_time(datetime(2012, 10, 7, 1, 30), - 'Australia/Sydney', True) - - def testNotImaginaryFoldNegativeUTCOffset(self): - self._test_imaginary_time(datetime(2015, 11, 1, 1, 30), - 'America/New_York', True) - - def testNotImaginaryFoldPositiveUTCOffset(self): - self._test_imaginary_time(datetime(2012, 4, 1, 3, 30), - 'Australia/Sydney', True) - - @unittest.skip("Known failure in Python 3.6.") - def testEqualAmbiguousComparison(self): - tzname = self._get_tzname('Australia/Sydney') - - with self._gettz_context(tzname): - SYD0 = self.gettz(tzname) - SYD1 = self.gettz(tzname) - - t0_u = datetime(2012, 3, 31, 14, 30, tzinfo=tz.tzutc()) # AEST - - t0_syd0 = t0_u.astimezone(SYD0) - t0_syd1 = t0_u.astimezone(SYD1) - - # This is considered an "inter-zone comparison" because it's an - # ambiguous datetime. - self.assertEqual(t0_syd0, t0_syd1) - - -class TzWinFoldMixin(object): - def get_args(self, tzname): - return (tzname, ) - - class context(object): - def __init__(*args, **kwargs): - pass - - def __enter__(*args, **kwargs): - pass - - def __exit__(*args, **kwargs): - pass - - def get_utc_transitions(self, tzi, year, gap): - dston, dstoff = tzi.transitions(year) - if gap: - t_n = dston - timedelta(minutes=30) - - t0_u = t_n.replace(tzinfo=tzi).astimezone(tz.tzutc()) - t1_u = t0_u + timedelta(hours=1) - else: - # Get 1 hour before the first ambiguous date - t_n = dstoff - timedelta(minutes=30) - - t0_u = t_n.replace(tzinfo=tzi).astimezone(tz.tzutc()) - t_n += timedelta(hours=1) # Naive ambiguous date - t0_u = t0_u + timedelta(hours=1) # First ambiguous date - t1_u = t0_u + timedelta(hours=1) # Second ambiguous date - - return t_n, t0_u, t1_u - - def testFoldPositiveUTCOffset(self): - # Test that we can resolve ambiguous times - tzname = 'AUS Eastern Standard Time' - args = self.get_args(tzname) - - with self.context(tzname): - # Calling fromutc() alters the tzfile object - SYD = self.tzclass(*args) - - # Get the transition time in UTC from the object, because - # Windows doesn't store historical info - t_n, t0_u, t1_u = self.get_utc_transitions(SYD, 2012, False) - - # Using fresh tzfiles - t0_syd = t0_u.astimezone(SYD) - t1_syd = t1_u.astimezone(SYD) - - self.assertEqual(t0_syd.replace(tzinfo=None), t_n) - - self.assertEqual(t1_syd.replace(tzinfo=None), t_n) - - self.assertEqual(t0_syd.utcoffset(), timedelta(hours=11)) - self.assertEqual(t1_syd.utcoffset(), timedelta(hours=10)) - self.assertNotEqual(t0_syd.tzname(), t1_syd.tzname()) - - def testGapPositiveUTCOffset(self): - # Test that we don't have a problem around gaps. - tzname = 'AUS Eastern Standard Time' - args = self.get_args(tzname) - - with self.context(tzname): - SYD = self.tzclass(*args) - - t_n, t0_u, t1_u = self.get_utc_transitions(SYD, 2012, True) - - t0 = t0_u.astimezone(SYD) - t1 = t1_u.astimezone(SYD) - - self.assertEqual(t0.replace(tzinfo=None), t_n) - - self.assertEqual(t1.replace(tzinfo=None), t_n + timedelta(hours=2)) - - self.assertEqual(t0.utcoffset(), timedelta(hours=10)) - self.assertEqual(t1.utcoffset(), timedelta(hours=11)) - - def testFoldNegativeUTCOffset(self): - # Test that we can resolve ambiguous times - tzname = 'Eastern Standard Time' - args = self.get_args(tzname) - - with self.context(tzname): - TOR = self.tzclass(*args) - - t_n, t0_u, t1_u = self.get_utc_transitions(TOR, 2011, False) - - t0_tor = t0_u.astimezone(TOR) - t1_tor = t1_u.astimezone(TOR) - - self.assertEqual(t0_tor.replace(tzinfo=None), t_n) - self.assertEqual(t1_tor.replace(tzinfo=None), t_n) - - self.assertNotEqual(t0_tor.tzname(), t1_tor.tzname()) - self.assertEqual(t0_tor.utcoffset(), timedelta(hours=-4.0)) - self.assertEqual(t1_tor.utcoffset(), timedelta(hours=-5.0)) - - def testGapNegativeUTCOffset(self): - # Test that we don't have a problem around gaps. - tzname = 'Eastern Standard Time' - args = self.get_args(tzname) - - with self.context(tzname): - TOR = self.tzclass(*args) - - t_n, t0_u, t1_u = self.get_utc_transitions(TOR, 2011, True) - - t0 = t0_u.astimezone(TOR) - t1 = t1_u.astimezone(TOR) - - self.assertEqual(t0.replace(tzinfo=None), - t_n) - - self.assertEqual(t1.replace(tzinfo=None), - t_n + timedelta(hours=2)) - - self.assertNotEqual(t0.tzname(), t1.tzname()) - self.assertEqual(t0.utcoffset(), timedelta(hours=-5.0)) - self.assertEqual(t1.utcoffset(), timedelta(hours=-4.0)) - - def testFoldIndependence(self): - tzname = 'Eastern Standard Time' - args = self.get_args(tzname) - - with self.context(tzname): - NYC = self.tzclass(*args) - UTC = tz.tzutc() - hour = timedelta(hours=1) - - # Firmly 2015-11-01 0:30 EDT-4 - t_n, t0_u, t1_u = self.get_utc_transitions(NYC, 2015, False) - - pre_dst = (t_n - hour).replace(tzinfo=NYC) - - # Currently, there's no way around the fact that this resolves to an - # ambiguous date, which defaults to EST. I'm not hard-coding in the - # answer, though, because the preferred behavior would be that this - # results in a time on the EDT side. - - # Ambiguous between 2015-11-01 1:30 EDT-4 and 2015-11-01 1:30 EST-5 - in_dst = pre_dst + hour - in_dst_tzname_0 = in_dst.tzname() # Stash the tzname - EDT - - # Doing the arithmetic in UTC creates a date that is unambiguously - # 2015-11-01 1:30 EDT-5 - in_dst_via_utc = (pre_dst.astimezone(UTC) + 2*hour).astimezone(NYC) - - # Make sure we got the right folding behavior - self.assertNotEqual(in_dst_via_utc.tzname(), in_dst_tzname_0) - - # Now check to make sure in_dst's tzname hasn't changed - self.assertEqual(in_dst_tzname_0, in_dst.tzname()) - - def testInZoneFoldEquality(self): - # Two datetimes in the same zone are considered to be equal if their - # wall times are equal, even if they have different absolute times. - tzname = 'Eastern Standard Time' - args = self.get_args(tzname) - - with self.context(tzname): - NYC = self.tzclass(*args) - UTC = tz.tzutc() - - t_n, t0_u, t1_u = self.get_utc_transitions(NYC, 2011, False) - - dt0 = t_n.replace(tzinfo=NYC) - dt1 = tz.enfold(dt0, fold=1) - - # Make sure these actually represent different times - self.assertNotEqual(dt0.astimezone(UTC), dt1.astimezone(UTC)) - - # Test that they compare equal - self.assertEqual(dt0, dt1) - -### -# Test Cases -class TzUTCTest(unittest.TestCase): - def testSingleton(self): - UTC_0 = tz.tzutc() - UTC_1 = tz.tzutc() - - self.assertIs(UTC_0, UTC_1) - - def testOffset(self): - ct = datetime(2009, 4, 1, 12, 11, 13, tzinfo=tz.tzutc()) - - self.assertEqual(ct.utcoffset(), timedelta(seconds=0)) - - def testDst(self): - ct = datetime(2009, 4, 1, 12, 11, 13, tzinfo=tz.tzutc()) - - self.assertEqual(ct.dst(), timedelta(seconds=0)) - - def testTzName(self): - ct = datetime(2009, 4, 1, 12, 11, 13, tzinfo=tz.tzutc()) - self.assertEqual(ct.tzname(), 'UTC') - - def testEquality(self): - UTC0 = tz.tzutc() - UTC1 = tz.tzutc() - - self.assertEqual(UTC0, UTC1) - - def testInequality(self): - UTC = tz.tzutc() - UTCp4 = tz.tzoffset('UTC+4', 14400) - - self.assertNotEqual(UTC, UTCp4) - - def testInequalityInteger(self): - self.assertFalse(tz.tzutc() == 7) - self.assertNotEqual(tz.tzutc(), 7) - - def testInequalityUnsupported(self): - self.assertEqual(tz.tzutc(), ComparesEqual) - - def testRepr(self): - UTC = tz.tzutc() - self.assertEqual(repr(UTC), 'tzutc()') - - def testTimeOnlyUTC(self): - # https://github.com/dateutil/dateutil/issues/132 - # tzutc doesn't care - tz_utc = tz.tzutc() - self.assertEqual(dt_time(13, 20, tzinfo=tz_utc).utcoffset(), - timedelta(0)) - - def testAmbiguity(self): - # Pick an arbitrary datetime, this should always return False. - dt = datetime(2011, 9, 1, 2, 30, tzinfo=tz.tzutc()) - - self.assertFalse(tz.datetime_ambiguous(dt)) - - -@pytest.mark.tzoffset -class TzOffsetTest(unittest.TestCase): - def testTimedeltaOffset(self): - est = tz.tzoffset('EST', timedelta(hours=-5)) - est_s = tz.tzoffset('EST', -18000) - - self.assertEqual(est, est_s) - - def testTzNameNone(self): - gmt5 = tz.tzoffset(None, -18000) # -5:00 - self.assertIs(datetime(2003, 10, 26, 0, 0, tzinfo=gmt5).tzname(), - None) - - def testTimeOnlyOffset(self): - # tzoffset doesn't care - tz_offset = tz.tzoffset('+3', 3600) - self.assertEqual(dt_time(13, 20, tzinfo=tz_offset).utcoffset(), - timedelta(seconds=3600)) - - def testTzOffsetRepr(self): - tname = 'EST' - tzo = tz.tzoffset(tname, -5 * 3600) - self.assertEqual(repr(tzo), "tzoffset(" + repr(tname) + ", -18000)") - - def testEquality(self): - utc = tz.tzoffset('UTC', 0) - gmt = tz.tzoffset('GMT', 0) - - self.assertEqual(utc, gmt) - - def testUTCEquality(self): - utc = tz.tzutc() - o_utc = tz.tzoffset('UTC', 0) - - self.assertEqual(utc, o_utc) - self.assertEqual(o_utc, utc) - - def testInequalityInvalid(self): - tzo = tz.tzoffset('-3', -3 * 3600) - self.assertFalse(tzo == -3) - self.assertNotEqual(tzo, -3) - - def testInequalityUnsupported(self): - tzo = tz.tzoffset('-5', -5 * 3600) - - self.assertTrue(tzo == ComparesEqual) - self.assertFalse(tzo != ComparesEqual) - self.assertEqual(tzo, ComparesEqual) - - def testAmbiguity(self): - # Pick an arbitrary datetime, this should always return False. - dt = datetime(2011, 9, 1, 2, 30, tzinfo=tz.tzoffset("EST", -5 * 3600)) - - self.assertFalse(tz.datetime_ambiguous(dt)) - - def testTzOffsetInstance(self): - tz1 = tz.tzoffset.instance('EST', timedelta(hours=-5)) - tz2 = tz.tzoffset.instance('EST', timedelta(hours=-5)) - - assert tz1 is not tz2 - - def testTzOffsetSingletonDifferent(self): - tz1 = tz.tzoffset('EST', timedelta(hours=-5)) - tz2 = tz.tzoffset('EST', -18000) - - assert tz1 is tz2 - - -@pytest.mark.smoke -@pytest.mark.tzoffset -def test_tzoffset_weakref(): - UTC1 = tz.tzoffset('UTC', 0) - UTC_ref = weakref.ref(tz.tzoffset('UTC', 0)) - UTC1 is UTC_ref() - del UTC1 - gc.collect() - - assert UTC_ref() is not None # Should be in the strong cache - assert UTC_ref() is tz.tzoffset('UTC', 0) - - # Fill the strong cache with other items - for offset in range(5,15): - tz.tzoffset('RandomZone', offset) - - gc.collect() - assert UTC_ref() is None - assert UTC_ref() is not tz.tzoffset('UTC', 0) - - -@pytest.mark.tzoffset -@pytest.mark.parametrize('args', [ - ('UTC', 0), - ('EST', -18000), - ('EST', timedelta(hours=-5)), - (None, timedelta(hours=3)), -]) -def test_tzoffset_singleton(args): - tz1 = tz.tzoffset(*args) - tz2 = tz.tzoffset(*args) - - assert tz1 is tz2 - - -@pytest.mark.tzoffset -@pytest.mark.skipif(not SUPPORTS_SUB_MINUTE_OFFSETS, - reason='Sub-minute offsets not supported') -def test_tzoffset_sub_minute(): - delta = timedelta(hours=12, seconds=30) - test_datetime = datetime(2000, 1, 1, tzinfo=tz.tzoffset(None, delta)) - assert test_datetime.utcoffset() == delta - - -@pytest.mark.tzoffset -@pytest.mark.skipif(SUPPORTS_SUB_MINUTE_OFFSETS, - reason='Sub-minute offsets supported') -def test_tzoffset_sub_minute_rounding(): - delta = timedelta(hours=12, seconds=30) - test_date = datetime(2000, 1, 1, tzinfo=tz.tzoffset(None, delta)) - assert test_date.utcoffset() == timedelta(hours=12, minutes=1) - - -@pytest.mark.tzlocal -class TzLocalTest(unittest.TestCase): - def testEquality(self): - tz1 = tz.tzlocal() - tz2 = tz.tzlocal() - - # Explicitly calling == and != here to ensure the operators work - self.assertTrue(tz1 == tz2) - self.assertFalse(tz1 != tz2) - - def testInequalityFixedOffset(self): - tzl = tz.tzlocal() - tzos = tz.tzoffset('LST', tzl._std_offset.total_seconds()) - tzod = tz.tzoffset('LDT', tzl._std_offset.total_seconds()) - - self.assertFalse(tzl == tzos) - self.assertFalse(tzl == tzod) - self.assertTrue(tzl != tzos) - self.assertTrue(tzl != tzod) - - def testInequalityInvalid(self): - tzl = tz.tzlocal() - - self.assertTrue(tzl != 1) - self.assertFalse(tzl == 1) - - # TODO: Use some sort of universal local mocking so that it's clear - # that we're expecting tzlocal to *not* be Pacific/Kiritimati - LINT = tz.gettz('Pacific/Kiritimati') - self.assertTrue(tzl != LINT) - self.assertFalse(tzl == LINT) - - def testInequalityUnsupported(self): - tzl = tz.tzlocal() - - self.assertTrue(tzl == ComparesEqual) - self.assertFalse(tzl != ComparesEqual) - - def testRepr(self): - tzl = tz.tzlocal() - - self.assertEqual(repr(tzl), 'tzlocal()') - - -@pytest.mark.parametrize('args,kwargs', [ - (('EST', -18000), {}), - (('EST', timedelta(hours=-5)), {}), - (('EST',), {'offset': -18000}), - (('EST',), {'offset': timedelta(hours=-5)}), - (tuple(), {'name': 'EST', 'offset': -18000}) -]) -def test_tzoffset_is(args, kwargs): - tz_ref = tz.tzoffset('EST', -18000) - assert tz.tzoffset(*args, **kwargs) is tz_ref - - -def test_tzoffset_is_not(): - assert tz.tzoffset('EDT', -14400) is not tz.tzoffset('EST', -18000) - - -@pytest.mark.tzlocal -@unittest.skipIf(IS_WIN, "requires Unix") -@unittest.skipUnless(TZEnvContext.tz_change_allowed(), - TZEnvContext.tz_change_disallowed_message()) -class TzLocalNixTest(unittest.TestCase, TzFoldMixin): - # This is a set of tests for `tzlocal()` on *nix systems - - # POSIX string indicating change to summer time on the 2nd Sunday in March - # at 2AM, and ending the 1st Sunday in November at 2AM. (valid >= 2007) - TZ_EST = 'EST+5EDT,M3.2.0/2,M11.1.0/2' - - # POSIX string for AEST/AEDT (valid >= 2008) - TZ_AEST = 'AEST-10AEDT,M10.1.0/2,M4.1.0/3' - - # POSIX string for BST/GMT - TZ_LON = 'GMT0BST,M3.5.0,M10.5.0' - - # POSIX string for UTC - UTC = 'UTC' - - def gettz(self, tzname): - # Actual time zone changes are handled by the _gettz_context function - return tz.tzlocal() - - def _gettz_context(self, tzname): - tzname_map = {'Australia/Sydney': self.TZ_AEST, - 'America/Toronto': self.TZ_EST, - 'America/New_York': self.TZ_EST, - 'Europe/London': self.TZ_LON} - - return TZEnvContext(tzname_map.get(tzname, tzname)) - - def _testTzFunc(self, tzval, func, std_val, dst_val): - """ - This generates tests about how the behavior of a function ``func`` - changes between STD and DST (e.g. utcoffset, tzname, dst). - - It assume that DST starts the 2nd Sunday in March and ends the 1st - Sunday in November - """ - with TZEnvContext(tzval): - dt1 = datetime(2015, 2, 1, 12, 0, tzinfo=tz.tzlocal()) # STD - dt2 = datetime(2015, 5, 1, 12, 0, tzinfo=tz.tzlocal()) # DST - - self.assertEqual(func(dt1), std_val) - self.assertEqual(func(dt2), dst_val) - - def _testTzName(self, tzval, std_name, dst_name): - func = datetime.tzname - - self._testTzFunc(tzval, func, std_name, dst_name) - - def testTzNameDST(self): - # Test tzname in a zone with DST - self._testTzName(self.TZ_EST, 'EST', 'EDT') - - def testTzNameUTC(self): - # Test tzname in a zone without DST - self._testTzName(self.UTC, 'UTC', 'UTC') - - def _testOffset(self, tzval, std_off, dst_off): - func = datetime.utcoffset - - self._testTzFunc(tzval, func, std_off, dst_off) - - def testOffsetDST(self): - self._testOffset(self.TZ_EST, timedelta(hours=-5), timedelta(hours=-4)) - - def testOffsetUTC(self): - self._testOffset(self.UTC, timedelta(0), timedelta(0)) - - def _testDST(self, tzval, dst_dst): - func = datetime.dst - std_dst = timedelta(0) - - self._testTzFunc(tzval, func, std_dst, dst_dst) - - def testDSTDST(self): - self._testDST(self.TZ_EST, timedelta(hours=1)) - - def testDSTUTC(self): - self._testDST(self.UTC, timedelta(0)) - - def testTimeOnlyOffsetLocalUTC(self): - with TZEnvContext(self.UTC): - self.assertEqual(dt_time(13, 20, tzinfo=tz.tzlocal()).utcoffset(), - timedelta(0)) - - def testTimeOnlyOffsetLocalDST(self): - with TZEnvContext(self.TZ_EST): - self.assertIs(dt_time(13, 20, tzinfo=tz.tzlocal()).utcoffset(), - None) - - def testTimeOnlyDSTLocalUTC(self): - with TZEnvContext(self.UTC): - self.assertEqual(dt_time(13, 20, tzinfo=tz.tzlocal()).dst(), - timedelta(0)) - - def testTimeOnlyDSTLocalDST(self): - with TZEnvContext(self.TZ_EST): - self.assertIs(dt_time(13, 20, tzinfo=tz.tzlocal()).dst(), - None) - - def testUTCEquality(self): - with TZEnvContext(self.UTC): - assert tz.tzlocal() == tz.tzutc() - - -# TODO: Maybe a better hack than this? -def mark_tzlocal_nix(f): - marks = [ - pytest.mark.tzlocal, - pytest.mark.skipif(IS_WIN, reason='requires Unix'), - pytest.mark.skipif(not TZEnvContext.tz_change_allowed, - reason=TZEnvContext.tz_change_disallowed_message()) - ] - - for mark in reversed(marks): - f = mark(f) - - return f - - -@mark_tzlocal_nix -@pytest.mark.parametrize('tzvar', ['UTC', 'GMT0', 'UTC0']) -def test_tzlocal_utc_equal(tzvar): - with TZEnvContext(tzvar): - assert tz.tzlocal() == tz.UTC - - -@mark_tzlocal_nix -@pytest.mark.parametrize('tzvar', [ - 'Europe/London', 'America/New_York', - 'GMT0BST', 'EST5EDT']) -def test_tzlocal_utc_unequal(tzvar): - with TZEnvContext(tzvar): - assert tz.tzlocal() != tz.UTC - - -@mark_tzlocal_nix -def test_tzlocal_local_time_trim_colon(): - with TZEnvContext(':/etc/localtime'): - assert tz.gettz() is not None - - -@mark_tzlocal_nix -@pytest.mark.parametrize('tzvar, tzoff', [ - ('EST5', tz.tzoffset('EST', -18000)), - ('GMT', tz.tzoffset('GMT', 0)), - ('YAKT-9', tz.tzoffset('YAKT', timedelta(hours=9))), - ('JST-9', tz.tzoffset('JST', timedelta(hours=9))), -]) -def test_tzlocal_offset_equal(tzvar, tzoff): - with TZEnvContext(tzvar): - # Including both to test both __eq__ and __ne__ - assert tz.tzlocal() == tzoff - assert not (tz.tzlocal() != tzoff) - - -@mark_tzlocal_nix -@pytest.mark.parametrize('tzvar, tzoff', [ - ('EST5EDT', tz.tzoffset('EST', -18000)), - ('GMT0BST', tz.tzoffset('GMT', 0)), - ('EST5', tz.tzoffset('EST', -14400)), - ('YAKT-9', tz.tzoffset('JST', timedelta(hours=9))), - ('JST-9', tz.tzoffset('YAKT', timedelta(hours=9))), -]) -def test_tzlocal_offset_unequal(tzvar, tzoff): - with TZEnvContext(tzvar): - # Including both to test both __eq__ and __ne__ - assert tz.tzlocal() != tzoff - assert not (tz.tzlocal() == tzoff) - - -@pytest.mark.gettz -class GettzTest(unittest.TestCase, TzFoldMixin): - gettz = staticmethod(tz.gettz) - - def testGettz(self): - # bug 892569 - str(self.gettz('UTC')) - - def testGetTzEquality(self): - self.assertEqual(self.gettz('UTC'), self.gettz('UTC')) - - def testTimeOnlyGettz(self): - # gettz returns None - tz_get = self.gettz('Europe/Minsk') - self.assertIs(dt_time(13, 20, tzinfo=tz_get).utcoffset(), None) - - def testTimeOnlyGettzDST(self): - # gettz returns None - tz_get = self.gettz('Europe/Minsk') - self.assertIs(dt_time(13, 20, tzinfo=tz_get).dst(), None) - - def testTimeOnlyGettzTzName(self): - tz_get = self.gettz('Europe/Minsk') - self.assertIs(dt_time(13, 20, tzinfo=tz_get).tzname(), None) - - def testTimeOnlyFormatZ(self): - tz_get = self.gettz('Europe/Minsk') - t = dt_time(13, 20, tzinfo=tz_get) - - self.assertEqual(t.strftime('%H%M%Z'), '1320') - - def testPortugalDST(self): - # In 1996, Portugal changed from CET to WET - PORTUGAL = self.gettz('Portugal') - - t_cet = datetime(1996, 3, 31, 1, 59, tzinfo=PORTUGAL) - - self.assertEqual(t_cet.tzname(), 'CET') - self.assertEqual(t_cet.utcoffset(), timedelta(hours=1)) - self.assertEqual(t_cet.dst(), timedelta(0)) - - t_west = datetime(1996, 3, 31, 2, 1, tzinfo=PORTUGAL) - - self.assertEqual(t_west.tzname(), 'WEST') - self.assertEqual(t_west.utcoffset(), timedelta(hours=1)) - self.assertEqual(t_west.dst(), timedelta(hours=1)) - - def testGettzCacheTzFile(self): - NYC1 = tz.gettz('America/New_York') - NYC2 = tz.gettz('America/New_York') - - assert NYC1 is NYC2 - - def testGettzCacheTzLocal(self): - local1 = tz.gettz() - local2 = tz.gettz() - - assert local1 is not local2 - - -@pytest.mark.gettz -@pytest.mark.parametrize('badzone', [ - 'Fake.Region/Abcdefghijklmnop', # Violates several tz project name rules -]) -def test_gettz_badzone(badzone): - # Make sure passing a bad TZ string to gettz returns None (GH #800) - tzi = tz.gettz(badzone) - assert tzi is None - - -@pytest.mark.gettz -def test_gettz_badzone_unicode(): - # Make sure a unicode string can be passed to TZ (GH #802) - # When fixed, combine this with test_gettz_badzone - tzi = tz.gettz('🐼') - assert tzi is None - - -@pytest.mark.gettz -@pytest.mark.xfail(IS_WIN, reason='zoneinfo separately cached') -def test_gettz_cache_clear(): - NYC1 = tz.gettz('America/New_York') - tz.gettz.cache_clear() - - NYC2 = tz.gettz('America/New_York') - - assert NYC1 is not NYC2 - -@pytest.mark.gettz -@pytest.mark.xfail(IS_WIN, reason='zoneinfo separately cached') -def test_gettz_set_cache_size(): - tz.gettz.cache_clear() - tz.gettz.set_cache_size(3) - - MONACO_ref = weakref.ref(tz.gettz('Europe/Monaco')) - EASTER_ref = weakref.ref(tz.gettz('Pacific/Easter')) - CURRIE_ref = weakref.ref(tz.gettz('Australia/Currie')) - - gc.collect() - - assert MONACO_ref() is not None - assert EASTER_ref() is not None - assert CURRIE_ref() is not None - - tz.gettz.set_cache_size(2) - gc.collect() - - assert MONACO_ref() is None - -@pytest.mark.xfail(IS_WIN, reason="Windows does not use system zoneinfo") -@pytest.mark.smoke -@pytest.mark.gettz -def test_gettz_weakref(): - tz.gettz.cache_clear() - tz.gettz.set_cache_size(2) - NYC1 = tz.gettz('America/New_York') - NYC_ref = weakref.ref(tz.gettz('America/New_York')) - - assert NYC1 is NYC_ref() - - del NYC1 - gc.collect() - - assert NYC_ref() is not None # Should still be in the strong cache - assert tz.gettz('America/New_York') is NYC_ref() - - # Populate strong cache with other timezones - tz.gettz('Europe/Monaco') - tz.gettz('Pacific/Easter') - tz.gettz('Australia/Currie') - - gc.collect() - assert NYC_ref() is None # Should have been pushed out - assert tz.gettz('America/New_York') is not NYC_ref() - -class ZoneInfoGettzTest(GettzTest, WarningTestMixin): - def gettz(self, name): - zoneinfo_file = zoneinfo.get_zonefile_instance() - return zoneinfo_file.get(name) - - def testZoneInfoFileStart1(self): - tz = self.gettz("EST5EDT") - self.assertEqual(datetime(2003, 4, 6, 1, 59, tzinfo=tz).tzname(), "EST", - MISSING_TARBALL) - self.assertEqual(datetime(2003, 4, 6, 2, 00, tzinfo=tz).tzname(), "EDT") - - def testZoneInfoFileEnd1(self): - tzc = self.gettz("EST5EDT") - self.assertEqual(datetime(2003, 10, 26, 0, 59, tzinfo=tzc).tzname(), - "EDT", MISSING_TARBALL) - - end_est = tz.enfold(datetime(2003, 10, 26, 1, 00, tzinfo=tzc), fold=1) - self.assertEqual(end_est.tzname(), "EST") - - def testZoneInfoOffsetSignal(self): - utc = self.gettz("UTC") - nyc = self.gettz("America/New_York") - self.assertNotEqual(utc, None, MISSING_TARBALL) - self.assertNotEqual(nyc, None) - t0 = datetime(2007, 11, 4, 0, 30, tzinfo=nyc) - t1 = t0.astimezone(utc) - t2 = t1.astimezone(nyc) - self.assertEqual(t0, t2) - self.assertEqual(nyc.dst(t0), timedelta(hours=1)) - - def testZoneInfoCopy(self): - # copy.copy() called on a ZoneInfo file was returning the same instance - CHI = self.gettz('America/Chicago') - CHI_COPY = copy.copy(CHI) - - self.assertIsNot(CHI, CHI_COPY) - self.assertEqual(CHI, CHI_COPY) - - def testZoneInfoDeepCopy(self): - CHI = self.gettz('America/Chicago') - CHI_COPY = copy.deepcopy(CHI) - - self.assertIsNot(CHI, CHI_COPY) - self.assertEqual(CHI, CHI_COPY) - - def testZoneInfoInstanceCaching(self): - zif_0 = zoneinfo.get_zonefile_instance() - zif_1 = zoneinfo.get_zonefile_instance() - - self.assertIs(zif_0, zif_1) - - def testZoneInfoNewInstance(self): - zif_0 = zoneinfo.get_zonefile_instance() - zif_1 = zoneinfo.get_zonefile_instance(new_instance=True) - zif_2 = zoneinfo.get_zonefile_instance() - - self.assertIsNot(zif_0, zif_1) - self.assertIs(zif_1, zif_2) - - def testZoneInfoDeprecated(self): - with self.assertWarns(DeprecationWarning): - zoneinfo.gettz('US/Eastern') - - def testZoneInfoMetadataDeprecated(self): - with self.assertWarns(DeprecationWarning): - zoneinfo.gettz_db_metadata() - - -class TZRangeTest(unittest.TestCase, TzFoldMixin): - TZ_EST = tz.tzrange('EST', timedelta(hours=-5), - 'EDT', timedelta(hours=-4), - start=relativedelta(month=3, day=1, hour=2, - weekday=SU(+2)), - end=relativedelta(month=11, day=1, hour=1, - weekday=SU(+1))) - - TZ_AEST = tz.tzrange('AEST', timedelta(hours=10), - 'AEDT', timedelta(hours=11), - start=relativedelta(month=10, day=1, hour=2, - weekday=SU(+1)), - end=relativedelta(month=4, day=1, hour=2, - weekday=SU(+1))) - - TZ_LON = tz.tzrange('GMT', timedelta(hours=0), - 'BST', timedelta(hours=1), - start=relativedelta(month=3, day=31, weekday=SU(-1), - hours=2), - end=relativedelta(month=10, day=31, weekday=SU(-1), - hours=1)) - # POSIX string for UTC - UTC = 'UTC' - - def gettz(self, tzname): - tzname_map = {'Australia/Sydney': self.TZ_AEST, - 'America/Toronto': self.TZ_EST, - 'America/New_York': self.TZ_EST, - 'Europe/London': self.TZ_LON} - - return tzname_map[tzname] - - def testRangeCmp1(self): - self.assertEqual(tz.tzstr("EST5EDT"), - tz.tzrange("EST", -18000, "EDT", -14400, - relativedelta(hours=+2, - month=4, day=1, - weekday=SU(+1)), - relativedelta(hours=+1, - month=10, day=31, - weekday=SU(-1)))) - - def testRangeCmp2(self): - self.assertEqual(tz.tzstr("EST5EDT"), - tz.tzrange("EST", -18000, "EDT")) - - def testRangeOffsets(self): - TZR = tz.tzrange('EST', -18000, 'EDT', -14400, - start=relativedelta(hours=2, month=4, day=1, - weekday=SU(+2)), - end=relativedelta(hours=1, month=10, day=31, - weekday=SU(-1))) - - dt_std = datetime(2014, 4, 11, 12, 0, tzinfo=TZR) # STD - dt_dst = datetime(2016, 4, 11, 12, 0, tzinfo=TZR) # DST - - dst_zero = timedelta(0) - dst_hour = timedelta(hours=1) - - std_offset = timedelta(hours=-5) - dst_offset = timedelta(hours=-4) - - # Check dst() - self.assertEqual(dt_std.dst(), dst_zero) - self.assertEqual(dt_dst.dst(), dst_hour) - - # Check utcoffset() - self.assertEqual(dt_std.utcoffset(), std_offset) - self.assertEqual(dt_dst.utcoffset(), dst_offset) - - # Check tzname - self.assertEqual(dt_std.tzname(), 'EST') - self.assertEqual(dt_dst.tzname(), 'EDT') - - def testTimeOnlyRangeFixed(self): - # This is a fixed-offset zone, so tzrange allows this - tz_range = tz.tzrange('dflt', stdoffset=timedelta(hours=-3)) - self.assertEqual(dt_time(13, 20, tzinfo=tz_range).utcoffset(), - timedelta(hours=-3)) - - def testTimeOnlyRange(self): - # tzrange returns None because this zone has DST - tz_range = tz.tzrange('EST', timedelta(hours=-5), - 'EDT', timedelta(hours=-4)) - self.assertIs(dt_time(13, 20, tzinfo=tz_range).utcoffset(), None) - - def testBrokenIsDstHandling(self): - # tzrange._isdst() was using a date() rather than a datetime(). - # Issue reported by Lennart Regebro. - dt = datetime(2007, 8, 6, 4, 10, tzinfo=tz.tzutc()) - self.assertEqual(dt.astimezone(tz=tz.gettz("GMT+2")), - datetime(2007, 8, 6, 6, 10, tzinfo=tz.tzstr("GMT+2"))) - - def testRangeTimeDelta(self): - # Test that tzrange can be specified with a timedelta instead of an int. - EST5EDT_td = tz.tzrange('EST', timedelta(hours=-5), - 'EDT', timedelta(hours=-4)) - - EST5EDT_sec = tz.tzrange('EST', -18000, - 'EDT', -14400) - - self.assertEqual(EST5EDT_td, EST5EDT_sec) - - def testRangeEquality(self): - TZR1 = tz.tzrange('EST', -18000, 'EDT', -14400) - - # Standard abbreviation different - TZR2 = tz.tzrange('ET', -18000, 'EDT', -14400) - self.assertNotEqual(TZR1, TZR2) - - # DST abbreviation different - TZR3 = tz.tzrange('EST', -18000, 'EMT', -14400) - self.assertNotEqual(TZR1, TZR3) - - # STD offset different - TZR4 = tz.tzrange('EST', -14000, 'EDT', -14400) - self.assertNotEqual(TZR1, TZR4) - - # DST offset different - TZR5 = tz.tzrange('EST', -18000, 'EDT', -18000) - self.assertNotEqual(TZR1, TZR5) - - # Start delta different - TZR6 = tz.tzrange('EST', -18000, 'EDT', -14400, - start=relativedelta(hours=+1, month=3, - day=1, weekday=SU(+2))) - self.assertNotEqual(TZR1, TZR6) - - # End delta different - TZR7 = tz.tzrange('EST', -18000, 'EDT', -14400, - end=relativedelta(hours=+1, month=11, - day=1, weekday=SU(+2))) - self.assertNotEqual(TZR1, TZR7) - - def testRangeInequalityUnsupported(self): - TZR = tz.tzrange('EST', -18000, 'EDT', -14400) - - self.assertFalse(TZR == 4) - self.assertTrue(TZR == ComparesEqual) - self.assertFalse(TZR != ComparesEqual) - - -@pytest.mark.tzstr -class TZStrTest(unittest.TestCase, TzFoldMixin): - # POSIX string indicating change to summer time on the 2nd Sunday in March - # at 2AM, and ending the 1st Sunday in November at 2AM. (valid >= 2007) - TZ_EST = 'EST+5EDT,M3.2.0/2,M11.1.0/2' - - # POSIX string for AEST/AEDT (valid >= 2008) - TZ_AEST = 'AEST-10AEDT,M10.1.0/2,M4.1.0/3' - - # POSIX string for GMT/BST - TZ_LON = 'GMT0BST,M3.5.0,M10.5.0' - - def gettz(self, tzname): - # Actual time zone changes are handled by the _gettz_context function - tzname_map = {'Australia/Sydney': self.TZ_AEST, - 'America/Toronto': self.TZ_EST, - 'America/New_York': self.TZ_EST, - 'Europe/London': self.TZ_LON} - - return tz.tzstr(tzname_map[tzname]) - - def testStrStr(self): - # Test that tz.tzstr() won't throw an error if given a str instead - # of a unicode literal. - self.assertEqual(datetime(2003, 4, 6, 1, 59, - tzinfo=tz.tzstr(str("EST5EDT"))).tzname(), "EST") - self.assertEqual(datetime(2003, 4, 6, 2, 00, - tzinfo=tz.tzstr(str("EST5EDT"))).tzname(), "EDT") - - def testStrInequality(self): - TZS1 = tz.tzstr('EST5EDT4') - - # Standard abbreviation different - TZS2 = tz.tzstr('ET5EDT4') - self.assertNotEqual(TZS1, TZS2) - - # DST abbreviation different - TZS3 = tz.tzstr('EST5EMT') - self.assertNotEqual(TZS1, TZS3) - - # STD offset different - TZS4 = tz.tzstr('EST4EDT4') - self.assertNotEqual(TZS1, TZS4) - - # DST offset different - TZS5 = tz.tzstr('EST5EDT3') - self.assertNotEqual(TZS1, TZS5) - - def testStrInequalityStartEnd(self): - TZS1 = tz.tzstr('EST5EDT4') - - # Start delta different - TZS2 = tz.tzstr('EST5EDT4,M4.2.0/02:00:00,M10-5-0/02:00') - self.assertNotEqual(TZS1, TZS2) - - # End delta different - TZS3 = tz.tzstr('EST5EDT4,M4.2.0/02:00:00,M11-5-0/02:00') - self.assertNotEqual(TZS1, TZS3) - - def testPosixOffset(self): - TZ1 = tz.tzstr('UTC-3') - self.assertEqual(datetime(2015, 1, 1, tzinfo=TZ1).utcoffset(), - timedelta(hours=-3)) - - TZ2 = tz.tzstr('UTC-3', posix_offset=True) - self.assertEqual(datetime(2015, 1, 1, tzinfo=TZ2).utcoffset(), - timedelta(hours=+3)) - - def testStrInequalityUnsupported(self): - TZS = tz.tzstr('EST5EDT') - - self.assertFalse(TZS == 4) - self.assertTrue(TZS == ComparesEqual) - self.assertFalse(TZS != ComparesEqual) - - def testTzStrRepr(self): - TZS1 = tz.tzstr('EST5EDT4') - TZS2 = tz.tzstr('EST') - - self.assertEqual(repr(TZS1), "tzstr(" + repr('EST5EDT4') + ")") - self.assertEqual(repr(TZS2), "tzstr(" + repr('EST') + ")") - - def testTzStrFailure(self): - with self.assertRaises(ValueError): - tz.tzstr('InvalidString;439999') - - def testTzStrSingleton(self): - tz1 = tz.tzstr('EST5EDT') - tz2 = tz.tzstr('CST4CST') - tz3 = tz.tzstr('EST5EDT') - - self.assertIsNot(tz1, tz2) - self.assertIs(tz1, tz3) - - def testTzStrSingletonPosix(self): - tz_t1 = tz.tzstr('GMT+3', posix_offset=True) - tz_f1 = tz.tzstr('GMT+3', posix_offset=False) - - tz_t2 = tz.tzstr('GMT+3', posix_offset=True) - tz_f2 = tz.tzstr('GMT+3', posix_offset=False) - - self.assertIs(tz_t1, tz_t2) - self.assertIsNot(tz_t1, tz_f1) - - self.assertIs(tz_f1, tz_f2) - - def testTzStrInstance(self): - tz1 = tz.tzstr('EST5EDT') - tz2 = tz.tzstr.instance('EST5EDT') - tz3 = tz.tzstr.instance('EST5EDT') - - assert tz1 is not tz2 - assert tz2 is not tz3 - - # Ensure that these still are all the same zone - assert tz1 == tz2 == tz3 - - -@pytest.mark.smoke -@pytest.mark.tzstr -def test_tzstr_weakref(): - tz_t1 = tz.tzstr('EST5EDT') - tz_t2_ref = weakref.ref(tz.tzstr('EST5EDT')) - assert tz_t1 is tz_t2_ref() - - del tz_t1 - gc.collect() - - assert tz_t2_ref() is not None - assert tz.tzstr('EST5EDT') is tz_t2_ref() - - for offset in range(5,15): - tz.tzstr('GMT+{}'.format(offset)) - gc.collect() - - assert tz_t2_ref() is None - assert tz.tzstr('EST5EDT') is not tz_t2_ref() - - -@pytest.mark.tzstr -@pytest.mark.parametrize('tz_str,expected', [ - # From https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html - ('', tz.tzrange(None)), # TODO: Should change this so tz.tzrange('') works - ('EST+5EDT,M3.2.0/2,M11.1.0/12', - tz.tzrange('EST', -18000, 'EDT', -14400, - start=relativedelta(month=3, day=1, weekday=SU(2), hours=2), - end=relativedelta(month=11, day=1, weekday=SU(1), hours=11))), - ('WART4WARST,J1/0,J365/25', # This is DST all year, Western Argentina Summer Time - tz.tzrange('WART', timedelta(hours=-4), 'WARST', - start=relativedelta(month=1, day=1, hours=0), - end=relativedelta(month=12, day=31, days=1))), - ('IST-2IDT,M3.4.4/26,M10.5.0', # Israel Standard / Daylight Time - tz.tzrange('IST', timedelta(hours=2), 'IDT', - start=relativedelta(month=3, day=1, weekday=TH(4), days=1, hours=2), - end=relativedelta(month=10, day=31, weekday=SU(-1), hours=1))), - ('WGT3WGST,M3.5.0/2,M10.5.0/1', - tz.tzrange('WGT', timedelta(hours=-3), 'WGST', - start=relativedelta(month=3, day=31, weekday=SU(-1), hours=2), - end=relativedelta(month=10, day=31, weekday=SU(-1), hours=0))), - - # Different offset specifications - ('WGT0300WGST', - tz.tzrange('WGT', timedelta(hours=-3), 'WGST')), - ('WGT03:00WGST', - tz.tzrange('WGT', timedelta(hours=-3), 'WGST')), - ('AEST-1100AEDT', - tz.tzrange('AEST', timedelta(hours=11), 'AEDT')), - ('AEST-11:00AEDT', - tz.tzrange('AEST', timedelta(hours=11), 'AEDT')), - - # Different time formats - ('EST5EDT,M3.2.0/4:00,M11.1.0/3:00', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(month=3, day=1, weekday=SU(2), hours=4), - end=relativedelta(month=11, day=1, weekday=SU(1), hours=2))), - ('EST5EDT,M3.2.0/04:00,M11.1.0/03:00', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(month=3, day=1, weekday=SU(2), hours=4), - end=relativedelta(month=11, day=1, weekday=SU(1), hours=2))), - ('EST5EDT,M3.2.0/0400,M11.1.0/0300', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(month=3, day=1, weekday=SU(2), hours=4), - end=relativedelta(month=11, day=1, weekday=SU(1), hours=2))), -]) -def test_valid_GNU_tzstr(tz_str, expected): - tzi = tz.tzstr(tz_str) - - assert tzi == expected - - -@pytest.mark.tzstr -@pytest.mark.parametrize('tz_str, expected', [ - ('EST5EDT,5,4,0,7200,11,3,0,7200', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(month=5, day=1, weekday=SU(+4), hours=+2), - end=relativedelta(month=11, day=1, weekday=SU(+3), hours=+1))), - ('EST5EDT,5,-4,0,7200,11,3,0,7200', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(hours=+2, month=5, day=31, weekday=SU(-4)), - end=relativedelta(hours=+1, month=11, day=1, weekday=SU(+3)))), - ('EST5EDT,5,4,0,7200,11,-3,0,7200', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(hours=+2, month=5, day=1, weekday=SU(+4)), - end=relativedelta(hours=+1, month=11, day=31, weekday=SU(-3)))), - ('EST5EDT,5,4,0,7200,11,-3,0,7200,3600', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(hours=+2, month=5, day=1, weekday=SU(+4)), - end=relativedelta(hours=+1, month=11, day=31, weekday=SU(-3)))), - ('EST5EDT,5,4,0,7200,11,-3,0,7200,3600', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(hours=+2, month=5, day=1, weekday=SU(+4)), - end=relativedelta(hours=+1, month=11, day=31, weekday=SU(-3)))), - ('EST5EDT,5,4,0,7200,11,-3,0,7200,-3600', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', timedelta(hours=-6), - start=relativedelta(hours=+2, month=5, day=1, weekday=SU(+4)), - end=relativedelta(hours=+3, month=11, day=31, weekday=SU(-3)))), - ('EST5EDT,5,4,0,7200,11,-3,0,7200,+7200', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', timedelta(hours=-3), - start=relativedelta(hours=+2, month=5, day=1, weekday=SU(+4)), - end=relativedelta(hours=0, month=11, day=31, weekday=SU(-3)))), - ('EST5EDT,5,4,0,7200,11,-3,0,7200,+3600', - tz.tzrange('EST', timedelta(hours=-5), 'EDT', - start=relativedelta(hours=+2, month=5, day=1, weekday=SU(+4)), - end=relativedelta(hours=+1, month=11, day=31, weekday=SU(-3)))), -]) -def test_valid_dateutil_format(tz_str, expected): - # This tests the dateutil-specific format that is used widely in the tests - # and examples. It is unclear where this format originated from. - with pytest.warns(tz.DeprecatedTzFormatWarning): - tzi = tz.tzstr.instance(tz_str) - - assert tzi == expected - - -@pytest.mark.tzstr -@pytest.mark.parametrize('tz_str', [ - 'hdfiughdfuig,dfughdfuigpu87ñ::', - ',dfughdfuigpu87ñ::', - '-1:WART4WARST,J1,J365/25', - 'WART4WARST,J1,J365/-25', - 'IST-2IDT,M3.4.-1/26,M10.5.0', - 'IST-2IDT,M3,2000,1/26,M10,5,0' -]) -def test_invalid_GNU_tzstr(tz_str): - with pytest.raises(ValueError): - tz.tzstr(tz_str) - - -# Different representations of the same default rule set -DEFAULT_TZSTR_RULES_EQUIV_2003 = [ - 'EST5EDT', - 'EST5EDT4,M4.1.0/02:00:00,M10-5-0/02:00', - 'EST5EDT4,95/02:00:00,298/02:00', - 'EST5EDT4,J96/02:00:00,J299/02:00', - 'EST5EDT4,J96/02:00:00,J299/02' -] - - -@pytest.mark.tzstr -@pytest.mark.parametrize('tz_str', DEFAULT_TZSTR_RULES_EQUIV_2003) -def test_tzstr_default_start(tz_str): - tzi = tz.tzstr(tz_str) - dt_std = datetime(2003, 4, 6, 1, 59, tzinfo=tzi) - dt_dst = datetime(2003, 4, 6, 2, 00, tzinfo=tzi) - - assert get_timezone_tuple(dt_std) == EST_TUPLE - assert get_timezone_tuple(dt_dst) == EDT_TUPLE - - -@pytest.mark.tzstr -@pytest.mark.parametrize('tz_str', DEFAULT_TZSTR_RULES_EQUIV_2003) -def test_tzstr_default_end(tz_str): - tzi = tz.tzstr(tz_str) - dt_dst = datetime(2003, 10, 26, 0, 59, tzinfo=tzi) - dt_dst_ambig = datetime(2003, 10, 26, 1, 00, tzinfo=tzi) - dt_std_ambig = tz.enfold(dt_dst_ambig, fold=1) - dt_std = datetime(2003, 10, 26, 2, 00, tzinfo=tzi) - - assert get_timezone_tuple(dt_dst) == EDT_TUPLE - assert get_timezone_tuple(dt_dst_ambig) == EDT_TUPLE - assert get_timezone_tuple(dt_std_ambig) == EST_TUPLE - assert get_timezone_tuple(dt_std) == EST_TUPLE - - -@pytest.mark.tzstr -@pytest.mark.parametrize('tzstr_1', ['EST5EDT', - 'EST5EDT4,M4.1.0/02:00:00,M10-5-0/02:00']) -@pytest.mark.parametrize('tzstr_2', ['EST5EDT', - 'EST5EDT4,M4.1.0/02:00:00,M10-5-0/02:00']) -def test_tzstr_default_cmp(tzstr_1, tzstr_2): - tz1 = tz.tzstr(tzstr_1) - tz2 = tz.tzstr(tzstr_2) - - assert tz1 == tz2 - -class TZICalTest(unittest.TestCase, TzFoldMixin): - def _gettz_str_tuple(self, tzname): - TZ_EST = ( - 'BEGIN:VTIMEZONE', - 'TZID:US-Eastern', - 'BEGIN:STANDARD', - 'DTSTART:19971029T020000', - 'RRULE:FREQ=YEARLY;BYDAY=+1SU;BYMONTH=11', - 'TZOFFSETFROM:-0400', - 'TZOFFSETTO:-0500', - 'TZNAME:EST', - 'END:STANDARD', - 'BEGIN:DAYLIGHT', - 'DTSTART:19980301T020000', - 'RRULE:FREQ=YEARLY;BYDAY=+2SU;BYMONTH=03', - 'TZOFFSETFROM:-0500', - 'TZOFFSETTO:-0400', - 'TZNAME:EDT', - 'END:DAYLIGHT', - 'END:VTIMEZONE' - ) - - TZ_PST = ( - 'BEGIN:VTIMEZONE', - 'TZID:US-Pacific', - 'BEGIN:STANDARD', - 'DTSTART:19971029T020000', - 'RRULE:FREQ=YEARLY;BYDAY=+1SU;BYMONTH=11', - 'TZOFFSETFROM:-0700', - 'TZOFFSETTO:-0800', - 'TZNAME:PST', - 'END:STANDARD', - 'BEGIN:DAYLIGHT', - 'DTSTART:19980301T020000', - 'RRULE:FREQ=YEARLY;BYDAY=+2SU;BYMONTH=03', - 'TZOFFSETFROM:-0800', - 'TZOFFSETTO:-0700', - 'TZNAME:PDT', - 'END:DAYLIGHT', - 'END:VTIMEZONE' - ) - - TZ_AEST = ( - 'BEGIN:VTIMEZONE', - 'TZID:Australia-Sydney', - 'BEGIN:STANDARD', - 'DTSTART:19980301T030000', - 'RRULE:FREQ=YEARLY;BYDAY=+1SU;BYMONTH=04', - 'TZOFFSETFROM:+1100', - 'TZOFFSETTO:+1000', - 'TZNAME:AEST', - 'END:STANDARD', - 'BEGIN:DAYLIGHT', - 'DTSTART:19971029T020000', - 'RRULE:FREQ=YEARLY;BYDAY=+1SU;BYMONTH=10', - 'TZOFFSETFROM:+1000', - 'TZOFFSETTO:+1100', - 'TZNAME:AEDT', - 'END:DAYLIGHT', - 'END:VTIMEZONE' - ) - - TZ_LON = ( - 'BEGIN:VTIMEZONE', - 'TZID:Europe-London', - 'BEGIN:STANDARD', - 'DTSTART:19810301T030000', - 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10;BYHOUR=02', - 'TZOFFSETFROM:+0100', - 'TZOFFSETTO:+0000', - 'TZNAME:GMT', - 'END:STANDARD', - 'BEGIN:DAYLIGHT', - 'DTSTART:19961001T030000', - 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=03;BYHOUR=01', - 'TZOFFSETFROM:+0000', - 'TZOFFSETTO:+0100', - 'TZNAME:BST', - 'END:DAYLIGHT', - 'END:VTIMEZONE' - ) - - tzname_map = {'Australia/Sydney': TZ_AEST, - 'America/Toronto': TZ_EST, - 'America/New_York': TZ_EST, - 'America/Los_Angeles': TZ_PST, - 'Europe/London': TZ_LON} - - return tzname_map[tzname] - - def _gettz_str(self, tzname): - return '\n'.join(self._gettz_str_tuple(tzname)) - - def _tzstr_dtstart_with_params(self, tzname, param_str): - # Adds parameters to the DTSTART values of a given tzstr - tz_str_tuple = self._gettz_str_tuple(tzname) - - out_tz = [] - for line in tz_str_tuple: - if line.startswith('DTSTART'): - name, value = line.split(':', 1) - line = name + ';' + param_str + ':' + value - - out_tz.append(line) - - return '\n'.join(out_tz) - - def gettz(self, tzname): - tz_str = self._gettz_str(tzname) - - tzc = tz.tzical(StringIO(tz_str)).get() - - return tzc - - def testRepr(self): - instr = StringIO(TZICAL_PST8PDT) - instr.name = 'StringIO(PST8PDT)' - tzc = tz.tzical(instr) - - self.assertEqual(repr(tzc), "tzical(" + repr(instr.name) + ")") - - # Test performance - def _test_us_zone(self, tzc, func, values, start): - if start: - dt1 = datetime(2003, 3, 9, 1, 59) - dt2 = datetime(2003, 3, 9, 2, 00) - fold = [0, 0] - else: - dt1 = datetime(2003, 11, 2, 0, 59) - dt2 = datetime(2003, 11, 2, 1, 00) - fold = [0, 1] - - dts = (tz.enfold(dt.replace(tzinfo=tzc), fold=f) - for dt, f in zip((dt1, dt2), fold)) - - for value, dt in zip(values, dts): - self.assertEqual(func(dt), value) - - def _test_multi_zones(self, tzstrs, tzids, func, values, start): - tzic = tz.tzical(StringIO('\n'.join(tzstrs))) - for tzid, vals in zip(tzids, values): - tzc = tzic.get(tzid) - - self._test_us_zone(tzc, func, vals, start) - - def _prepare_EST(self): - tz_str = self._gettz_str('America/New_York') - return tz.tzical(StringIO(tz_str)).get() - - def _testEST(self, start, test_type, tzc=None): - if tzc is None: - tzc = self._prepare_EST() - - argdict = { - 'name': (datetime.tzname, ('EST', 'EDT')), - 'offset': (datetime.utcoffset, (timedelta(hours=-5), - timedelta(hours=-4))), - 'dst': (datetime.dst, (timedelta(hours=0), - timedelta(hours=1))) - } - - func, values = argdict[test_type] - - if not start: - values = reversed(values) - - self._test_us_zone(tzc, func, values, start=start) - - def testESTStartName(self): - self._testEST(start=True, test_type='name') - - def testESTEndName(self): - self._testEST(start=False, test_type='name') - - def testESTStartOffset(self): - self._testEST(start=True, test_type='offset') - - def testESTEndOffset(self): - self._testEST(start=False, test_type='offset') - - def testESTStartDST(self): - self._testEST(start=True, test_type='dst') - - def testESTEndDST(self): - self._testEST(start=False, test_type='dst') - - def testESTValueDatetime(self): - # Violating one-test-per-test rule because we're not set up to do - # parameterized tests and the manual proliferation is getting a bit - # out of hand. - tz_str = self._tzstr_dtstart_with_params('America/New_York', - 'VALUE=DATE-TIME') - - tzc = tz.tzical(StringIO(tz_str)).get() - - for start in (True, False): - for test_type in ('name', 'offset', 'dst'): - self._testEST(start=start, test_type=test_type, tzc=tzc) - - def _testMultizone(self, start, test_type): - tzstrs = (self._gettz_str('America/New_York'), - self._gettz_str('America/Los_Angeles')) - tzids = ('US-Eastern', 'US-Pacific') - - argdict = { - 'name': (datetime.tzname, (('EST', 'EDT'), - ('PST', 'PDT'))), - 'offset': (datetime.utcoffset, ((timedelta(hours=-5), - timedelta(hours=-4)), - (timedelta(hours=-8), - timedelta(hours=-7)))), - 'dst': (datetime.dst, ((timedelta(hours=0), - timedelta(hours=1)), - (timedelta(hours=0), - timedelta(hours=1)))) - } - - func, values = argdict[test_type] - - if not start: - values = map(reversed, values) - - self._test_multi_zones(tzstrs, tzids, func, values, start) - - def testMultiZoneStartName(self): - self._testMultizone(start=True, test_type='name') - - def testMultiZoneEndName(self): - self._testMultizone(start=False, test_type='name') - - def testMultiZoneStartOffset(self): - self._testMultizone(start=True, test_type='offset') - - def testMultiZoneEndOffset(self): - self._testMultizone(start=False, test_type='offset') - - def testMultiZoneStartDST(self): - self._testMultizone(start=True, test_type='dst') - - def testMultiZoneEndDST(self): - self._testMultizone(start=False, test_type='dst') - - def testMultiZoneKeys(self): - est_str = self._gettz_str('America/New_York') - pst_str = self._gettz_str('America/Los_Angeles') - tzic = tz.tzical(StringIO('\n'.join((est_str, pst_str)))) - - # Sort keys because they are in a random order, being dictionary keys - keys = sorted(tzic.keys()) - - self.assertEqual(keys, ['US-Eastern', 'US-Pacific']) - - # Test error conditions - def testEmptyString(self): - with self.assertRaises(ValueError): - tz.tzical(StringIO("")) - - def testMultiZoneGet(self): - tzic = tz.tzical(StringIO(TZICAL_EST5EDT + TZICAL_PST8PDT)) - - with self.assertRaises(ValueError): - tzic.get() - - def testDtstartDate(self): - tz_str = self._tzstr_dtstart_with_params('America/New_York', - 'VALUE=DATE') - with self.assertRaises(ValueError): - tz.tzical(StringIO(tz_str)) - - def testDtstartTzid(self): - tz_str = self._tzstr_dtstart_with_params('America/New_York', - 'TZID=UTC') - with self.assertRaises(ValueError): - tz.tzical(StringIO(tz_str)) - - def testDtstartBadParam(self): - tz_str = self._tzstr_dtstart_with_params('America/New_York', - 'FOO=BAR') - with self.assertRaises(ValueError): - tz.tzical(StringIO(tz_str)) - - # Test Parsing - def testGap(self): - tzic = tz.tzical(StringIO('\n'.join((TZICAL_EST5EDT, TZICAL_PST8PDT)))) - - keys = sorted(tzic.keys()) - self.assertEqual(keys, ['US-Eastern', 'US-Pacific']) - - -class TZTest(unittest.TestCase): - def testFileStart1(self): - tzc = tz.tzfile(BytesIO(base64.b64decode(TZFILE_EST5EDT))) - self.assertEqual(datetime(2003, 4, 6, 1, 59, tzinfo=tzc).tzname(), "EST") - self.assertEqual(datetime(2003, 4, 6, 2, 00, tzinfo=tzc).tzname(), "EDT") - - def testFileEnd1(self): - tzc = tz.tzfile(BytesIO(base64.b64decode(TZFILE_EST5EDT))) - self.assertEqual(datetime(2003, 10, 26, 0, 59, tzinfo=tzc).tzname(), - "EDT") - end_est = tz.enfold(datetime(2003, 10, 26, 1, 00, tzinfo=tzc)) - self.assertEqual(end_est.tzname(), "EST") - - def testFileLastTransition(self): - # After the last transition, it goes to standard time in perpetuity - tzc = tz.tzfile(BytesIO(base64.b64decode(TZFILE_EST5EDT))) - self.assertEqual(datetime(2037, 10, 25, 0, 59, tzinfo=tzc).tzname(), - "EDT") - - last_date = tz.enfold(datetime(2037, 10, 25, 1, 00, tzinfo=tzc), fold=1) - self.assertEqual(last_date.tzname(), - "EST") - - self.assertEqual(datetime(2038, 5, 25, 12, 0, tzinfo=tzc).tzname(), - "EST") - - def testInvalidFile(self): - # Should throw a ValueError if an invalid file is passed - with self.assertRaises(ValueError): - tz.tzfile(BytesIO(b'BadFile')) - - def testFilestreamWithNameRepr(self): - # If fileobj is a filestream with a "name" attribute this name should - # be reflected in the tz object's repr - fileobj = BytesIO(base64.b64decode(TZFILE_EST5EDT)) - fileobj.name = 'foo' - tzc = tz.tzfile(fileobj) - self.assertEqual(repr(tzc), 'tzfile(' + repr('foo') + ')') - - def testLeapCountDecodesProperly(self): - # This timezone has leapcnt, and failed to decode until - # Eugene Oden notified about the issue. - - # As leap information is currently unused (and unstored) by tzfile() we - # can only indirectly test this: Take advantage of tzfile() not closing - # the input file if handed in as an opened file and assert that the - # full file content has been read by tzfile(). Note: For this test to - # work NEW_YORK must be in TZif version 1 format i.e. no more data - # after TZif v1 header + data has been read - fileobj = BytesIO(base64.b64decode(NEW_YORK)) - tz.tzfile(fileobj) - # we expect no remaining file content now, i.e. zero-length; if there's - # still data we haven't read the file format correctly - remaining_tzfile_content = fileobj.read() - self.assertEqual(len(remaining_tzfile_content), 0) - - def testIsStd(self): - # NEW_YORK tzfile contains this isstd information: - isstd_expected = (0, 0, 0, 1) - tzc = tz.tzfile(BytesIO(base64.b64decode(NEW_YORK))) - # gather the actual information as parsed by the tzfile class - isstd = [] - for ttinfo in tzc._ttinfo_list: - # ttinfo objects contain boolean values - isstd.append(int(ttinfo.isstd)) - # ttinfo list may contain more entries than isstd file content - isstd = tuple(isstd[:len(isstd_expected)]) - self.assertEqual( - isstd_expected, isstd, - "isstd UTC/local indicators parsed: %s != tzfile contents: %s" - % (isstd, isstd_expected)) - - def testGMTHasNoDaylight(self): - # tz.tzstr("GMT+2") improperly considered daylight saving time. - # Issue reported by Lennart Regebro. - dt = datetime(2007, 8, 6, 4, 10) - self.assertEqual(tz.gettz("GMT+2").dst(dt), timedelta(0)) - - def testGMTOffset(self): - # GMT and UTC offsets have inverted signal when compared to the - # usual TZ variable handling. - dt = datetime(2007, 8, 6, 4, 10, tzinfo=tz.tzutc()) - self.assertEqual(dt.astimezone(tz=tz.tzstr("GMT+2")), - datetime(2007, 8, 6, 6, 10, tzinfo=tz.tzstr("GMT+2"))) - self.assertEqual(dt.astimezone(tz=tz.gettz("UTC-2")), - datetime(2007, 8, 6, 2, 10, tzinfo=tz.tzstr("UTC-2"))) - - @unittest.skipIf(IS_WIN, "requires Unix") - @unittest.skipUnless(TZEnvContext.tz_change_allowed(), - TZEnvContext.tz_change_disallowed_message()) - def testTZSetDoesntCorrupt(self): - # if we start in non-UTC then tzset UTC make sure parse doesn't get - # confused - with TZEnvContext('UTC'): - # this should parse to UTC timezone not the original timezone - dt = parse('2014-07-20T12:34:56+00:00') - self.assertEqual(str(dt), '2014-07-20 12:34:56+00:00') - - -@pytest.mark.tzfile -@pytest.mark.skipif(not SUPPORTS_SUB_MINUTE_OFFSETS, - reason='Sub-minute offsets not supported') -def test_tzfile_sub_minute_offset(): - # If user running python 3.6 or newer, exact offset is used - tzc = tz.tzfile(BytesIO(base64.b64decode(EUROPE_HELSINKI))) - offset = timedelta(hours=1, minutes=39, seconds=52) - assert datetime(1900, 1, 1, 0, 0, tzinfo=tzc).utcoffset() == offset - - -@pytest.mark.tzfile -@pytest.mark.skipif(SUPPORTS_SUB_MINUTE_OFFSETS, - reason='Sub-minute offsets supported.') -def test_sub_minute_rounding_tzfile(): - # This timezone has an offset of 5992 seconds in 1900-01-01. - # For python version pre-3.6, this will be rounded - tzc = tz.tzfile(BytesIO(base64.b64decode(EUROPE_HELSINKI))) - offset = timedelta(hours=1, minutes=40) - assert datetime(1900, 1, 1, 0, 0, tzinfo=tzc).utcoffset() == offset - - -@pytest.mark.tzfile -def test_samoa_transition(): - # utcoffset() was erroneously returning +14:00 an hour early (GH #812) - APIA = tz.gettz('Pacific/Apia') - dt = datetime(2011, 12, 29, 23, 59, tzinfo=APIA) - assert dt.utcoffset() == timedelta(hours=-10) - - # Make sure the transition actually works, too - dt_after = (dt.astimezone(tz.UTC) + timedelta(minutes=1)).astimezone(APIA) - assert dt_after == datetime(2011, 12, 31, tzinfo=APIA) - assert dt_after.utcoffset() == timedelta(hours=14) - - -@unittest.skipUnless(IS_WIN, "Requires Windows") -class TzWinTest(unittest.TestCase, TzWinFoldMixin): - def setUp(self): - self.tzclass = tzwin.tzwin - - def testTzResLoadName(self): - # This may not work right on non-US locales. - tzr = tzwin.tzres() - self.assertEqual(tzr.load_name(112), "Eastern Standard Time") - - def testTzResNameFromString(self): - tzr = tzwin.tzres() - self.assertEqual(tzr.name_from_string('@tzres.dll,-221'), - 'Alaskan Daylight Time') - - self.assertEqual(tzr.name_from_string('Samoa Daylight Time'), - 'Samoa Daylight Time') - - with self.assertRaises(ValueError): - tzr.name_from_string('@tzres.dll,100') - - def testIsdstZoneWithNoDaylightSaving(self): - tz = tzwin.tzwin("UTC") - dt = parse("2013-03-06 19:08:15") - self.assertFalse(tz._isdst(dt)) - - def testOffset(self): - tz = tzwin.tzwin("Cape Verde Standard Time") - self.assertEqual(tz.utcoffset(datetime(1995, 5, 21, 12, 9, 13)), - timedelta(-1, 82800)) - - def testTzwinName(self): - # https://github.com/dateutil/dateutil/issues/143 - tw = tz.tzwin('Eastern Standard Time') - - # Cover the transitions for at least two years. - ESTs = 'Eastern Standard Time' - EDTs = 'Eastern Daylight Time' - transition_dates = [(datetime(2015, 3, 8, 0, 59), ESTs), - (datetime(2015, 3, 8, 3, 1), EDTs), - (datetime(2015, 11, 1, 0, 59), EDTs), - (datetime(2015, 11, 1, 3, 1), ESTs), - (datetime(2016, 3, 13, 0, 59), ESTs), - (datetime(2016, 3, 13, 3, 1), EDTs), - (datetime(2016, 11, 6, 0, 59), EDTs), - (datetime(2016, 11, 6, 3, 1), ESTs)] - - for t_date, expected in transition_dates: - self.assertEqual(t_date.replace(tzinfo=tw).tzname(), expected) - - def testTzwinRepr(self): - tw = tz.tzwin('Yakutsk Standard Time') - self.assertEqual(repr(tw), 'tzwin(' + - repr('Yakutsk Standard Time') + ')') - - def testTzWinEquality(self): - # https://github.com/dateutil/dateutil/issues/151 - tzwin_names = ('Eastern Standard Time', - 'West Pacific Standard Time', - 'Yakutsk Standard Time', - 'Iran Standard Time', - 'UTC') - - for tzwin_name in tzwin_names: - # Get two different instances to compare - tw1 = tz.tzwin(tzwin_name) - tw2 = tz.tzwin(tzwin_name) - - self.assertEqual(tw1, tw2) - - def testTzWinInequality(self): - # https://github.com/dateutil/dateutil/issues/151 - # Note these last two currently differ only in their name. - tzwin_names = (('Eastern Standard Time', 'Yakutsk Standard Time'), - ('Greenwich Standard Time', 'GMT Standard Time'), - ('GMT Standard Time', 'UTC'), - ('E. South America Standard Time', - 'Argentina Standard Time')) - - for tzwn1, tzwn2 in tzwin_names: - # Get two different instances to compare - tw1 = tz.tzwin(tzwn1) - tw2 = tz.tzwin(tzwn2) - - self.assertNotEqual(tw1, tw2) - - def testTzWinEqualityInvalid(self): - # Compare to objects that do not implement comparison with this - # (should default to False) - UTC = tz.tzutc() - EST = tz.tzwin('Eastern Standard Time') - - self.assertFalse(EST == UTC) - self.assertFalse(EST == 1) - self.assertFalse(UTC == EST) - - self.assertTrue(EST != UTC) - self.assertTrue(EST != 1) - - def testTzWinInequalityUnsupported(self): - # Compare it to an object that is promiscuous about equality, but for - # which tzwin does not implement an equality operator. - EST = tz.tzwin('Eastern Standard Time') - self.assertTrue(EST == ComparesEqual) - self.assertFalse(EST != ComparesEqual) - - def testTzwinTimeOnlyDST(self): - # For zones with DST, .dst() should return None - tw_est = tz.tzwin('Eastern Standard Time') - self.assertIs(dt_time(14, 10, tzinfo=tw_est).dst(), None) - - # This zone has no DST, so .dst() can return 0 - tw_sast = tz.tzwin('South Africa Standard Time') - self.assertEqual(dt_time(14, 10, tzinfo=tw_sast).dst(), - timedelta(0)) - - def testTzwinTimeOnlyUTCOffset(self): - # For zones with DST, .utcoffset() should return None - tw_est = tz.tzwin('Eastern Standard Time') - self.assertIs(dt_time(14, 10, tzinfo=tw_est).utcoffset(), None) - - # This zone has no DST, so .utcoffset() returns standard offset - tw_sast = tz.tzwin('South Africa Standard Time') - self.assertEqual(dt_time(14, 10, tzinfo=tw_sast).utcoffset(), - timedelta(hours=2)) - - def testTzwinTimeOnlyTZName(self): - # For zones with DST, the name defaults to standard time - tw_est = tz.tzwin('Eastern Standard Time') - self.assertEqual(dt_time(14, 10, tzinfo=tw_est).tzname(), - 'Eastern Standard Time') - - # For zones with no DST, this should work normally. - tw_sast = tz.tzwin('South Africa Standard Time') - self.assertEqual(dt_time(14, 10, tzinfo=tw_sast).tzname(), - 'South Africa Standard Time') - - -@unittest.skipUnless(IS_WIN, "Requires Windows") -@unittest.skipUnless(TZWinContext.tz_change_allowed(), - TZWinContext.tz_change_disallowed_message()) -class TzWinLocalTest(unittest.TestCase, TzWinFoldMixin): - - def setUp(self): - self.tzclass = tzwin.tzwinlocal - self.context = TZWinContext - - def get_args(self, tzname): - return () - - def testLocal(self): - # Not sure how to pin a local time zone, so for now we're just going - # to run this and make sure it doesn't raise an error - # See Github Issue #135: https://github.com/dateutil/dateutil/issues/135 - datetime.now(tzwin.tzwinlocal()) - - def testTzwinLocalUTCOffset(self): - with TZWinContext('Eastern Standard Time'): - tzwl = tzwin.tzwinlocal() - self.assertEqual(datetime(2014, 3, 11, tzinfo=tzwl).utcoffset(), - timedelta(hours=-4)) - - def testTzwinLocalName(self): - # https://github.com/dateutil/dateutil/issues/143 - ESTs = 'Eastern Standard Time' - EDTs = 'Eastern Daylight Time' - transition_dates = [(datetime(2015, 3, 8, 0, 59), ESTs), - (datetime(2015, 3, 8, 3, 1), EDTs), - (datetime(2015, 11, 1, 0, 59), EDTs), - (datetime(2015, 11, 1, 3, 1), ESTs), - (datetime(2016, 3, 13, 0, 59), ESTs), - (datetime(2016, 3, 13, 3, 1), EDTs), - (datetime(2016, 11, 6, 0, 59), EDTs), - (datetime(2016, 11, 6, 3, 1), ESTs)] - - with TZWinContext('Eastern Standard Time'): - tw = tz.tzwinlocal() - - for t_date, expected in transition_dates: - self.assertEqual(t_date.replace(tzinfo=tw).tzname(), expected) - - def testTzWinLocalRepr(self): - tw = tz.tzwinlocal() - self.assertEqual(repr(tw), 'tzwinlocal()') - - def testTzwinLocalRepr(self): - # https://github.com/dateutil/dateutil/issues/143 - with TZWinContext('Eastern Standard Time'): - tw = tz.tzwinlocal() - - self.assertEqual(str(tw), 'tzwinlocal(' + - repr('Eastern Standard Time') + ')') - - with TZWinContext('Pacific Standard Time'): - tw = tz.tzwinlocal() - - self.assertEqual(str(tw), 'tzwinlocal(' + - repr('Pacific Standard Time') + ')') - - def testTzwinLocalEquality(self): - tw_est = tz.tzwin('Eastern Standard Time') - tw_pst = tz.tzwin('Pacific Standard Time') - - with TZWinContext('Eastern Standard Time'): - twl1 = tz.tzwinlocal() - twl2 = tz.tzwinlocal() - - self.assertEqual(twl1, twl2) - self.assertEqual(twl1, tw_est) - self.assertNotEqual(twl1, tw_pst) - - with TZWinContext('Pacific Standard Time'): - twl1 = tz.tzwinlocal() - twl2 = tz.tzwinlocal() - tw = tz.tzwin('Pacific Standard Time') - - self.assertEqual(twl1, twl2) - self.assertEqual(twl1, tw) - self.assertEqual(twl1, tw_pst) - self.assertNotEqual(twl1, tw_est) - - def testTzwinLocalTimeOnlyDST(self): - # For zones with DST, .dst() should return None - with TZWinContext('Eastern Standard Time'): - twl = tz.tzwinlocal() - self.assertIs(dt_time(14, 10, tzinfo=twl).dst(), None) - - # This zone has no DST, so .dst() can return 0 - with TZWinContext('South Africa Standard Time'): - twl = tz.tzwinlocal() - self.assertEqual(dt_time(14, 10, tzinfo=twl).dst(), timedelta(0)) - - def testTzwinLocalTimeOnlyUTCOffset(self): - # For zones with DST, .utcoffset() should return None - with TZWinContext('Eastern Standard Time'): - twl = tz.tzwinlocal() - self.assertIs(dt_time(14, 10, tzinfo=twl).utcoffset(), None) - - # This zone has no DST, so .utcoffset() returns standard offset - with TZWinContext('South Africa Standard Time'): - twl = tz.tzwinlocal() - self.assertEqual(dt_time(14, 10, tzinfo=twl).utcoffset(), - timedelta(hours=2)) - - def testTzwinLocalTimeOnlyTZName(self): - # For zones with DST, the name defaults to standard time - with TZWinContext('Eastern Standard Time'): - twl = tz.tzwinlocal() - self.assertEqual(dt_time(14, 10, tzinfo=twl).tzname(), - 'Eastern Standard Time') - - # For zones with no DST, this should work normally. - with TZWinContext('South Africa Standard Time'): - twl = tz.tzwinlocal() - self.assertEqual(dt_time(14, 10, tzinfo=twl).tzname(), - 'South Africa Standard Time') - - -class TzPickleTest(PicklableMixin, unittest.TestCase): - _asfile = False - - def setUp(self): - self.assertPicklable = partial(self.assertPicklable, - asfile=self._asfile) - - def testPickleTzUTC(self): - self.assertPicklable(tz.tzutc(), singleton=True) - - def testPickleTzOffsetZero(self): - self.assertPicklable(tz.tzoffset('UTC', 0), singleton=True) - - def testPickleTzOffsetPos(self): - self.assertPicklable(tz.tzoffset('UTC+1', 3600), singleton=True) - - def testPickleTzOffsetNeg(self): - self.assertPicklable(tz.tzoffset('UTC-1', -3600), singleton=True) - - @pytest.mark.tzlocal - def testPickleTzLocal(self): - self.assertPicklable(tz.tzlocal()) - - def testPickleTzFileEST5EDT(self): - tzc = tz.tzfile(BytesIO(base64.b64decode(TZFILE_EST5EDT))) - self.assertPicklable(tzc) - - def testPickleTzFileEurope_Helsinki(self): - tzc = tz.tzfile(BytesIO(base64.b64decode(EUROPE_HELSINKI))) - self.assertPicklable(tzc) - - def testPickleTzFileNew_York(self): - tzc = tz.tzfile(BytesIO(base64.b64decode(NEW_YORK))) - self.assertPicklable(tzc) - - @unittest.skip("Known failure") - def testPickleTzICal(self): - tzc = tz.tzical(StringIO(TZICAL_EST5EDT)).get() - self.assertPicklable(tzc) - - def testPickleTzGettz(self): - self.assertPicklable(tz.gettz('America/New_York')) - - def testPickleZoneFileGettz(self): - zoneinfo_file = zoneinfo.get_zonefile_instance() - tzi = zoneinfo_file.get('America/New_York') - self.assertIsNot(tzi, None) - self.assertPicklable(tzi) - - -class TzPickleFileTest(TzPickleTest): - """ Run all the TzPickleTest tests, using a temporary file """ - _asfile = True - - -class DatetimeAmbiguousTest(unittest.TestCase): - """ Test the datetime_exists / datetime_ambiguous functions """ - - def testNoTzSpecified(self): - with self.assertRaises(ValueError): - tz.datetime_ambiguous(datetime(2016, 4, 1, 2, 9)) - - def _get_no_support_tzinfo_class(self, dt_start, dt_end, dst_only=False): - # Generates a class of tzinfo with no support for is_ambiguous - # where dates between dt_start and dt_end are ambiguous. - - class FoldingTzInfo(tzinfo): - def utcoffset(self, dt): - if not dst_only: - dt_n = dt.replace(tzinfo=None) - - if dt_start <= dt_n < dt_end and getattr(dt_n, 'fold', 0): - return timedelta(hours=-1) - - return timedelta(hours=0) - - def dst(self, dt): - dt_n = dt.replace(tzinfo=None) - - if dt_start <= dt_n < dt_end and getattr(dt_n, 'fold', 0): - return timedelta(hours=1) - else: - return timedelta(0) - - return FoldingTzInfo - - def _get_no_support_tzinfo(self, dt_start, dt_end, dst_only=False): - return self._get_no_support_tzinfo_class(dt_start, dt_end, dst_only)() - - def testNoSupportAmbiguityFoldNaive(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_no_support_tzinfo(dt_start, dt_end) - - self.assertTrue(tz.datetime_ambiguous(datetime(2018, 9, 1, 1, 30), - tz=tzi)) - - def testNoSupportAmbiguityFoldAware(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_no_support_tzinfo(dt_start, dt_end) - - self.assertTrue(tz.datetime_ambiguous(datetime(2018, 9, 1, 1, 30, - tzinfo=tzi))) - - def testNoSupportAmbiguityUnambiguousNaive(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_no_support_tzinfo(dt_start, dt_end) - - self.assertFalse(tz.datetime_ambiguous(datetime(2018, 10, 1, 12, 30), - tz=tzi)) - - def testNoSupportAmbiguityUnambiguousAware(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_no_support_tzinfo(dt_start, dt_end) - - self.assertFalse(tz.datetime_ambiguous(datetime(2018, 10, 1, 12, 30, - tzinfo=tzi))) - - def testNoSupportAmbiguityFoldDSTOnly(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_no_support_tzinfo(dt_start, dt_end, dst_only=True) - - self.assertTrue(tz.datetime_ambiguous(datetime(2018, 9, 1, 1, 30), - tz=tzi)) - - def testNoSupportAmbiguityUnambiguousDSTOnly(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_no_support_tzinfo(dt_start, dt_end, dst_only=True) - - self.assertFalse(tz.datetime_ambiguous(datetime(2018, 10, 1, 12, 30), - tz=tzi)) - - def testSupportAmbiguityFoldNaive(self): - tzi = tz.gettz('US/Eastern') - - dt = datetime(2011, 11, 6, 1, 30) - - self.assertTrue(tz.datetime_ambiguous(dt, tz=tzi)) - - def testSupportAmbiguityFoldAware(self): - tzi = tz.gettz('US/Eastern') - - dt = datetime(2011, 11, 6, 1, 30, tzinfo=tzi) - - self.assertTrue(tz.datetime_ambiguous(dt)) - - def testSupportAmbiguityUnambiguousAware(self): - tzi = tz.gettz('US/Eastern') - - dt = datetime(2011, 11, 6, 4, 30) - - self.assertFalse(tz.datetime_ambiguous(dt, tz=tzi)) - - def testSupportAmbiguityUnambiguousNaive(self): - tzi = tz.gettz('US/Eastern') - - dt = datetime(2011, 11, 6, 4, 30, tzinfo=tzi) - - self.assertFalse(tz.datetime_ambiguous(dt)) - - def _get_ambig_error_tzinfo(self, dt_start, dt_end, dst_only=False): - cTzInfo = self._get_no_support_tzinfo_class(dt_start, dt_end, dst_only) - - # Takes the wrong number of arguments and raises an error anyway. - class FoldTzInfoRaises(cTzInfo): - def is_ambiguous(self, dt, other_arg): - raise NotImplementedError('This is not implemented') - - return FoldTzInfoRaises() - - def testIncompatibleAmbiguityFoldNaive(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_ambig_error_tzinfo(dt_start, dt_end) - - self.assertTrue(tz.datetime_ambiguous(datetime(2018, 9, 1, 1, 30), - tz=tzi)) - - def testIncompatibleAmbiguityFoldAware(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_ambig_error_tzinfo(dt_start, dt_end) - - self.assertTrue(tz.datetime_ambiguous(datetime(2018, 9, 1, 1, 30, - tzinfo=tzi))) - - def testIncompatibleAmbiguityUnambiguousNaive(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_ambig_error_tzinfo(dt_start, dt_end) - - self.assertFalse(tz.datetime_ambiguous(datetime(2018, 10, 1, 12, 30), - tz=tzi)) - - def testIncompatibleAmbiguityUnambiguousAware(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_ambig_error_tzinfo(dt_start, dt_end) - - self.assertFalse(tz.datetime_ambiguous(datetime(2018, 10, 1, 12, 30, - tzinfo=tzi))) - - def testIncompatibleAmbiguityFoldDSTOnly(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_ambig_error_tzinfo(dt_start, dt_end, dst_only=True) - - self.assertTrue(tz.datetime_ambiguous(datetime(2018, 9, 1, 1, 30), - tz=tzi)) - - def testIncompatibleAmbiguityUnambiguousDSTOnly(self): - dt_start = datetime(2018, 9, 1, 1, 0) - dt_end = datetime(2018, 9, 1, 2, 0) - - tzi = self._get_ambig_error_tzinfo(dt_start, dt_end, dst_only=True) - - self.assertFalse(tz.datetime_ambiguous(datetime(2018, 10, 1, 12, 30), - tz=tzi)) - - def testSpecifiedTzOverridesAttached(self): - # If a tz is specified, the datetime will be treated as naive. - - # This is not ambiguous in the local zone - dt = datetime(2011, 11, 6, 1, 30, tzinfo=tz.gettz('Australia/Sydney')) - - self.assertFalse(tz.datetime_ambiguous(dt)) - - tzi = tz.gettz('US/Eastern') - self.assertTrue(tz.datetime_ambiguous(dt, tz=tzi)) - - -class DatetimeExistsTest(unittest.TestCase): - def testNoTzSpecified(self): - with self.assertRaises(ValueError): - tz.datetime_exists(datetime(2016, 4, 1, 2, 9)) - - def testInGapNaive(self): - tzi = tz.gettz('Australia/Sydney') - - dt = datetime(2012, 10, 7, 2, 30) - - self.assertFalse(tz.datetime_exists(dt, tz=tzi)) - - def testInGapAware(self): - tzi = tz.gettz('Australia/Sydney') - - dt = datetime(2012, 10, 7, 2, 30, tzinfo=tzi) - - self.assertFalse(tz.datetime_exists(dt)) - - def testExistsNaive(self): - tzi = tz.gettz('Australia/Sydney') - - dt = datetime(2012, 10, 7, 10, 30) - - self.assertTrue(tz.datetime_exists(dt, tz=tzi)) - - def testExistsAware(self): - tzi = tz.gettz('Australia/Sydney') - - dt = datetime(2012, 10, 7, 10, 30, tzinfo=tzi) - - self.assertTrue(tz.datetime_exists(dt)) - - def testSpecifiedTzOverridesAttached(self): - EST = tz.gettz('US/Eastern') - AEST = tz.gettz('Australia/Sydney') - - dt = datetime(2012, 10, 7, 2, 30, tzinfo=EST) # This time exists - - self.assertFalse(tz.datetime_exists(dt, tz=AEST)) - - -class TestEnfold: - def test_enter_fold_default(self): - dt = tz.enfold(datetime(2020, 1, 19, 3, 32)) - - assert dt.fold == 1 - - def test_enter_fold(self): - dt = tz.enfold(datetime(2020, 1, 19, 3, 32), fold=1) - - assert dt.fold == 1 - - def test_exit_fold(self): - dt = tz.enfold(datetime(2020, 1, 19, 3, 32), fold=0) - - # Before Python 3.6, dt.fold won't exist if fold is 0. - assert getattr(dt, 'fold', 0) == 0 - - def test_defold(self): - dt = tz.enfold(datetime(2020, 1, 19, 3, 32), fold=1) - - dt2 = tz.enfold(dt, fold=0) - - assert getattr(dt2, 'fold', 0) == 0 - - def test_fold_replace_args(self): - # This test can be dropped when Python < 3.6 is dropped, since it - # is mainly to cover the `replace` method on _DatetimeWithFold - dt = tz.enfold(datetime(1950, 1, 2, 12, 30, 15, 8), fold=1) - - dt2 = dt.replace(1952, 2, 3, 13, 31, 16, 9) - assert dt2 == tz.enfold(datetime(1952, 2, 3, 13, 31, 16, 9), fold=1) - assert dt2.fold == 1 - - def test_fold_replace_exception_duplicate_args(self): - dt = tz.enfold(datetime(1999, 1, 3), fold=1) - - with pytest.raises(TypeError): - dt.replace(1950, year=2000) - - -@pytest.mark.tz_resolve_imaginary -class ImaginaryDateTest(unittest.TestCase): - def testCanberraForward(self): - tzi = tz.gettz('Australia/Canberra') - dt = datetime(2018, 10, 7, 2, 30, tzinfo=tzi) - dt_act = tz.resolve_imaginary(dt) - dt_exp = datetime(2018, 10, 7, 3, 30, tzinfo=tzi) - self.assertEqual(dt_act, dt_exp) - - def testLondonForward(self): - tzi = tz.gettz('Europe/London') - dt = datetime(2018, 3, 25, 1, 30, tzinfo=tzi) - dt_act = tz.resolve_imaginary(dt) - dt_exp = datetime(2018, 3, 25, 2, 30, tzinfo=tzi) - self.assertEqual(dt_act, dt_exp) - - def testKeivForward(self): - tzi = tz.gettz('Europe/Kiev') - dt = datetime(2018, 3, 25, 3, 30, tzinfo=tzi) - dt_act = tz.resolve_imaginary(dt) - dt_exp = datetime(2018, 3, 25, 4, 30, tzinfo=tzi) - self.assertEqual(dt_act, dt_exp) - - -@pytest.mark.tz_resolve_imaginary -@pytest.mark.parametrize('dt', [ - datetime(2017, 11, 5, 1, 30, tzinfo=tz.gettz('America/New_York')), - datetime(2018, 10, 28, 1, 30, tzinfo=tz.gettz('Europe/London')), - datetime(2017, 4, 2, 2, 30, tzinfo=tz.gettz('Australia/Sydney')), -]) -def test_resolve_imaginary_ambiguous(dt): - assert tz.resolve_imaginary(dt) is dt - - dt_f = tz.enfold(dt) - assert dt is not dt_f - assert tz.resolve_imaginary(dt_f) is dt_f - - -@pytest.mark.tz_resolve_imaginary -@pytest.mark.parametrize('dt', [ - datetime(2017, 6, 2, 12, 30, tzinfo=tz.gettz('America/New_York')), - datetime(2018, 4, 2, 9, 30, tzinfo=tz.gettz('Europe/London')), - datetime(2017, 2, 2, 16, 30, tzinfo=tz.gettz('Australia/Sydney')), - datetime(2017, 12, 2, 12, 30, tzinfo=tz.gettz('America/New_York')), - datetime(2018, 12, 2, 9, 30, tzinfo=tz.gettz('Europe/London')), - datetime(2017, 6, 2, 16, 30, tzinfo=tz.gettz('Australia/Sydney')), - datetime(2025, 9, 25, 1, 17, tzinfo=tz.tzutc()), - datetime(2025, 9, 25, 1, 17, tzinfo=tz.tzoffset('EST', -18000)), - datetime(2019, 3, 4, tzinfo=None) -]) -def test_resolve_imaginary_existing(dt): - assert tz.resolve_imaginary(dt) is dt - - -def __get_kiritimati_resolve_imaginary_test(): - # In the 2018d release of the IANA database, the Kiritimati "imaginary day" - # data was corrected, so if the system zoneinfo is older than 2018d, the - # Kiritimati test will fail. - - tzi = tz.gettz('Pacific/Kiritimati') - new_version = False - if not tz.datetime_exists(datetime(1995, 1, 1, 12, 30), tzi): - zif = zoneinfo.get_zonefile_instance() - if zif.metadata is not None: - new_version = zif.metadata['tzversion'] >= '2018d' - - if new_version: - tzi = zif.get('Pacific/Kiritimati') - else: - new_version = True - - if new_version: - dates = (datetime(1994, 12, 31, 12, 30), datetime(1995, 1, 1, 12, 30)) - else: - dates = (datetime(1995, 1, 1, 12, 30), datetime(1995, 1, 2, 12, 30)) - - return (tzi, ) + dates - - -resolve_imaginary_tests = [ - (tz.gettz('Europe/London'), - datetime(2018, 3, 25, 1, 30), datetime(2018, 3, 25, 2, 30)), - (tz.gettz('America/New_York'), - datetime(2017, 3, 12, 2, 30), datetime(2017, 3, 12, 3, 30)), - (tz.gettz('Australia/Sydney'), - datetime(2014, 10, 5, 2, 0), datetime(2014, 10, 5, 3, 0)), - __get_kiritimati_resolve_imaginary_test(), -] - - -if SUPPORTS_SUB_MINUTE_OFFSETS: - resolve_imaginary_tests.append( - (tz.gettz('Africa/Monrovia'), - datetime(1972, 1, 7, 0, 30), datetime(1972, 1, 7, 1, 14, 30))) - - -@pytest.mark.tz_resolve_imaginary -@pytest.mark.parametrize('tzi, dt, dt_exp', resolve_imaginary_tests) -def test_resolve_imaginary(tzi, dt, dt_exp): - dt = dt.replace(tzinfo=tzi) - dt_exp = dt_exp.replace(tzinfo=tzi) - - dt_r = tz.resolve_imaginary(dt) - assert dt_r == dt_exp - assert dt_r.tzname() == dt_exp.tzname() - assert dt_r.utcoffset() == dt_exp.utcoffset() |