############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import math import os import pickle import platform import sys import time import unittest from datetime import date from datetime import datetime from datetime import timedelta from datetime import tzinfo import pytz from DateTime import DateTime from DateTime.DateTime import _findLocalTimeZoneName try: __file__ except NameError: # pragma: no cover f = sys.argv[0] else: f = __file__ IS_PYPY = getattr(platform, 'python_implementation', lambda: None)() == 'PyPy' DATADIR = os.path.dirname(os.path.abspath(f)) del f ZERO = timedelta(0) 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 class DateTimeTests(unittest.TestCase): def _compare(self, dt1, dt2): '''Compares the internal representation of dt1 with the representation in dt2. Allows sub-millisecond variations. Primarily for testing.''' self.assertEqual(round(dt1._t, 3), round(dt2._t, 3)) self.assertEqual(round(dt1._d, 9), round(dt2._d, 9)) self.assertEqual(round(dt1.time, 9), round(dt2.time, 9)) self.assertEqual(dt1.millis(), dt2.millis()) self.assertEqual(dt1._micros, dt2._micros) def testBug1203(self): # 01:59:60 occurred in old DateTime dt = DateTime(7200, 'GMT') self.assertTrue(str(dt).find('60') < 0, dt) def testDSTInEffect(self): # Checks GMT offset for a DST date in the US/Eastern time zone dt = DateTime(2000, 5, 9, 15, 0, 0, 'US/Eastern') self.assertEqual(dt.toZone('GMT').hour(), 19, (dt, dt.toZone('GMT'))) def testDSTNotInEffect(self): # Checks GMT offset for a non-DST date in the US/Eastern time zone dt = DateTime(2000, 11, 9, 15, 0, 0, 'US/Eastern') self.assertEqual(dt.toZone('GMT').hour(), 20, (dt, dt.toZone('GMT'))) def testAddPrecision(self): # Precision of serial additions dt = DateTime() self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt), dt) # checks problem reported in # https://github.com/zopefoundation/DateTime/issues/41 dt = DateTime(2038, 10, 7, 8, 52, 44.959840, "UTC") self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt), dt) def testConsistentSecondMicroRounding(self): dt = DateTime(2038, 10, 7, 8, 52, 44.9598398, "UTC") self.assertEqual(int(dt.second() * 1000000), dt.micros() % 60000000) def testConstructor3(self): # Constructor from date/time string dt = DateTime() dt1s = '%d/%d/%d %d:%d:%f %s' % ( dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second(), dt.timezone()) dt1 = DateTime(dt1s) # Compare representations as it's the # only way to compare the dates to the same accuracy self.assertEqual(repr(dt), repr(dt1)) def testConstructor4(self): # Constructor from time float dt = DateTime() dt1 = DateTime(float(dt)) self._compare(dt, dt1) def testConstructor5(self): # Constructor from time float and timezone dt = DateTime() dt1 = DateTime(float(dt), dt.timezone()) self.assertEqual(str(dt), str(dt1), (dt, dt1)) dt1 = DateTime(float(dt), str(dt.timezone())) self.assertEqual(str(dt), str(dt1), (dt, dt1)) def testConstructor6(self): # Constructor from year and julian date # This test must normalize the time zone, or it *will* break when # DST changes! dt1 = DateTime(2000, 5.500000578705) dt = DateTime('2000/1/5 12:00:00.050 pm %s' % dt1.localZone()) self._compare(dt, dt1) def testConstructor7(self): # Constructor from parts dt = DateTime() dt1 = DateTime( dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second(), dt.timezone()) # Compare representations as it's the # only way to compare the dates to the same accuracy self.assertEqual(repr(dt), repr(dt1)) def testDayOfWeek(self): # Compare to the datetime.date value to make it locale independent expected = date(2000, 6, 16).strftime('%A') # strftime() used to always be passed a day of week of 0 dt = DateTime('2000/6/16') s = dt.strftime('%A') self.assertEqual(s, expected, (dt, s)) def testOldDate(self): # Fails when an 1800 date is displayed with negative signs dt = DateTime('1830/5/6 12:31:46.213 pm') dt1 = dt.toZone('GMT+6') self.assertTrue(str(dt1).find('-') < 0, (dt, dt1)) def testSubtraction(self): # Reconstruction of a DateTime from its parts, with subtraction # this also tests the accuracy of addition and reconstruction dt = DateTime() dt1 = dt - 3.141592653 dt2 = DateTime( dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second()) dt3 = dt2 - 3.141592653 self.assertEqual(dt1, dt3, (dt, dt1, dt2, dt3)) def testTZ1add(self): # Time zone manipulation: add to a date dt = DateTime('1997/3/8 1:45am GMT-4') dt1 = DateTime('1997/3/9 1:45pm GMT+8') self.assertTrue((dt + 1.0).equalTo(dt1)) def testTZ1sub(self): # Time zone manipulation: subtract from a date dt = DateTime('1997/3/8 1:45am GMT-4') dt1 = DateTime('1997/3/9 1:45pm GMT+8') self.assertTrue((dt1 - 1.0).equalTo(dt)) def testTZ1diff(self): # Time zone manipulation: diff two dates dt = DateTime('1997/3/8 1:45am GMT-4') dt1 = DateTime('1997/3/9 1:45pm GMT+8') self.assertEqual(dt1 - dt, 1.0, (dt, dt1)) def test_compare_methods(self): # Compare two dates using several methods dt = DateTime('1997/1/1') dt1 = DateTime('1997/2/2') self.assertTrue(dt1.greaterThan(dt)) self.assertTrue(dt1.greaterThanEqualTo(dt)) self.assertTrue(dt.lessThan(dt1)) self.assertTrue(dt.lessThanEqualTo(dt1)) self.assertTrue(dt.notEqualTo(dt1)) self.assertFalse(dt.equalTo(dt1)) # Compare a date to float dt = DateTime(1.0) self.assertTrue(dt == DateTime(1.0)) # testing __eq__ self.assertFalse(dt != DateTime(1.0)) # testing __ne__ self.assertFalse(dt.greaterThan(1.0)) self.assertTrue(dt.greaterThanEqualTo(1.0)) self.assertFalse(dt.lessThan(1.0)) self.assertTrue(dt.lessThanEqualTo(1.0)) self.assertFalse(dt.notEqualTo(1.0)) self.assertTrue(dt.equalTo(1.0)) # Compare a date to int dt = DateTime(1) self.assertEqual(dt, DateTime(1.0)) self.assertTrue(dt == DateTime(1)) # testing __eq__ self.assertFalse(dt != DateTime(1)) # testing __ne__ self.assertFalse(dt.greaterThan(1)) self.assertTrue(dt.greaterThanEqualTo(1)) self.assertFalse(dt.lessThan(1)) self.assertTrue(dt.lessThanEqualTo(1)) self.assertFalse(dt.notEqualTo(1)) self.assertTrue(dt.equalTo(1)) # Compare a date to string; there is no implicit type conversion # but behavior if consistent as when comparing, for example, an int # and a string. dt = DateTime("2023") self.assertFalse(dt == "2023") # testing __eq__ self.assertTrue(dt != "2023") # testing __ne__ self.assertRaises(TypeError, dt.greaterThan, "2023") self.assertRaises(TypeError, dt.greaterThanEqualTo, "2023") self.assertRaises(TypeError, dt.lessThan, "2023") self.assertRaises(TypeError, dt.lessThanEqualTo, "2023") self.assertTrue(dt.notEqualTo("2023")) self.assertFalse(dt.equalTo("2023")) def test_compare_methods_none(self): # Compare a date to None for dt in (DateTime('1997/1/1'), DateTime(0)): self.assertTrue(dt.greaterThan(None)) self.assertTrue(dt.greaterThanEqualTo(None)) self.assertFalse(dt.lessThan(None)) self.assertFalse(dt.lessThanEqualTo(None)) self.assertTrue(dt.notEqualTo(None)) self.assertFalse(dt.equalTo(None)) def test_pickle(self): dt = DateTime() data = pickle.dumps(dt, 1) new = pickle.loads(data) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def test_pickle_with_tz(self): dt = DateTime('2002/5/2 8:00am GMT+8') data = pickle.dumps(dt, 1) new = pickle.loads(data) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def test_pickle_asdatetime_with_tz(self): dt = DateTime('2002/5/2 8:00am GMT+8') data = pickle.dumps(dt.asdatetime(), 1) new = DateTime(pickle.loads(data)) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def test_pickle_with_numerical_tz(self): for dt_str in ('2007/01/02 12:34:56.789 +0300', '2007/01/02 12:34:56.789 +0430', '2007/01/02 12:34:56.789 -1234'): dt = DateTime(dt_str) data = pickle.dumps(dt, 1) new = pickle.loads(data) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def test_pickle_with_micros(self): dt = DateTime('2002/5/2 8:00:14.123 GMT+8') data = pickle.dumps(dt, 1) new = pickle.loads(data) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def test_pickle_old(self): dt = DateTime('2002/5/2 8:00am GMT+0') data = ( '(cDateTime.DateTime\nDateTime\nq\x01Noq\x02}q\x03(U\x05' '_amonq\x04U\x03Mayq\x05U\x05_adayq\x06U\x03Thuq\x07U\x05_pmonq' '\x08h\x05U\x05_hourq\tK\x08U\x05_fmonq\nh\x05U\x05_pdayq\x0bU' '\x04Thu.q\x0cU\x05_fdayq\rU\x08Thursdayq\x0eU\x03_pmq\x0fU\x02amq' '\x10U\x02_tq\x11GA\xcehy\x00\x00\x00\x00U\x07_minuteq\x12K\x00U' '\x07_microsq\x13L1020326400000000L\nU\x02_dq\x14G@\xe2\x12j\xaa' '\xaa\xaa\xabU\x07_secondq\x15G\x00\x00\x00\x00\x00\x00\x00\x00U' '\x03_tzq\x16U\x05GMT+0q\x17U\x06_monthq\x18K\x05U' '\x0f_timezone_naiveq\x19I00\nU\x04_dayq\x1aK\x02U\x05_yearq' '\x1bM\xd2\x07U\x08_nearsecq\x1cG\x00\x00\x00\x00\x00\x00\x00' '\x00U\x07_pmhourq\x1dK\x08U\n_dayoffsetq\x1eK\x04U\x04timeq' '\x1fG?\xd5UUUV\x00\x00ub.') data = data.encode('latin-1') new = pickle.loads(data) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def test_pickle_old_without_micros(self): dt = DateTime('2002/5/2 8:00am GMT+0') data = ( '(cDateTime.DateTime\nDateTime\nq\x01Noq\x02}q\x03(U\x05' '_amonq\x04U\x03Mayq\x05U\x05_adayq\x06U\x03Thuq\x07U\x05_pmonq' '\x08h\x05U\x05_hourq\tK\x08U\x05_fmonq\nh\x05U\x05_pdayq\x0bU' '\x04Thu.q\x0cU\x05_fdayq\rU\x08Thursdayq\x0eU\x03_pmq\x0fU' '\x02amq\x10U\x02_tq\x11GA\xcehy\x00\x00\x00\x00U\x07_minuteq' '\x12K\x00U\x02_dq\x13G@\xe2\x12j\xaa\xaa\xaa\xabU\x07_secondq' '\x14G\x00\x00\x00\x00\x00\x00\x00\x00U\x03_tzq\x15U\x05GMT+0q' '\x16U\x06_monthq\x17K\x05U\x0f_timezone_naiveq\x18I00\nU' '\x04_dayq\x19K\x02U\x05_yearq\x1aM\xd2\x07U\x08_nearsecq' '\x1bG\x00\x00\x00\x00\x00\x00\x00\x00U\x07_pmhourq\x1cK\x08U' '\n_dayoffsetq\x1dK\x04U\x04timeq\x1eG?\xd5UUUV\x00\x00ub.') data = data.encode('latin-1') new = pickle.loads(data) for key in DateTime.__slots__: self.assertEqual(getattr(dt, key), getattr(new, key)) def testTZ2(self): # Time zone manipulation test 2 dt = DateTime() dt1 = dt.toZone('GMT') s = dt.second() s1 = dt1.second() self.assertEqual(s, s1, (dt, dt1, s, s1)) def testTZDiffDaylight(self): # Diff dates across daylight savings dates dt = DateTime('2000/6/8 1:45am US/Eastern') dt1 = DateTime('2000/12/8 12:45am US/Eastern') self.assertEqual(dt1 - dt, 183, (dt, dt1, dt1 - dt)) def testY10KDate(self): # Comparison of a Y10K date and a Y2K date dt = DateTime('10213/09/21') dt1 = DateTime(2000, 1, 1) dsec = (dt.millis() - dt1.millis()) / 1000.0 ddays = math.floor((dsec / 86400.0) + 0.5) self.assertEqual(ddays, 3000000, ddays) def test_tzoffset(self): # Test time-zone given as an offset # GMT dt = DateTime('Tue, 10 Sep 2001 09:41:03 GMT') self.assertEqual(dt.tzoffset(), 0) # Timezone by name, a timezone that hasn't got daylightsaving. dt = DateTime('Tue, 2 Mar 2001 09:41:03 GMT+3') self.assertEqual(dt.tzoffset(), 10800) # Timezone by name, has daylightsaving but is not in effect. dt = DateTime('Tue, 21 Jan 2001 09:41:03 PST') self.assertEqual(dt.tzoffset(), -28800) # Timezone by name, with daylightsaving in effect dt = DateTime('Tue, 24 Aug 2001 09:41:03 PST') self.assertEqual(dt.tzoffset(), -25200) # A negative numerical timezone dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0400') self.assertEqual(dt.tzoffset(), -14400) # A positive numerical timzone dt = DateTime('Tue, 6 Dec 1966 01:41:03 +0200') self.assertEqual(dt.tzoffset(), 7200) # A negative numerical timezone with minutes. dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0637') self.assertEqual(dt.tzoffset(), -23820) # A positive numerical timezone with minutes. dt = DateTime('Tue, 24 Jul 2001 09:41:03 +0425') self.assertEqual(dt.tzoffset(), 15900) def testISO8601(self): # ISO8601 reference dates ref0 = DateTime('2002/5/2 8:00am GMT') ref1 = DateTime('2002/5/2 8:00am US/Eastern') ref2 = DateTime('2006/11/6 10:30 GMT') ref3 = DateTime('2004/06/14 14:30:15 GMT-3') ref4 = DateTime('2006/01/01 GMT') # Basic tests # Though this is timezone naive and according to specification should # be interpreted in the local timezone, to preserve backwards # compatibility with previously expected behaviour. isoDt = DateTime('2002-05-02T08:00:00') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002-05-02T08:00:00Z') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002-05-02T08:00:00+00:00') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002-05-02T08:00:00-04:00') self.assertTrue(ref1.equalTo(isoDt)) isoDt = DateTime('2002-05-02 08:00:00-04:00') self.assertTrue(ref1.equalTo(isoDt)) # Bug 1386: the colon in the timezone offset is optional isoDt = DateTime('2002-05-02T08:00:00-0400') self.assertTrue(ref1.equalTo(isoDt)) # Bug 2191: date reduced formats isoDt = DateTime('2006-01-01') self.assertTrue(ref4.equalTo(isoDt)) isoDt = DateTime('200601-01') self.assertTrue(ref4.equalTo(isoDt)) isoDt = DateTime('20060101') self.assertTrue(ref4.equalTo(isoDt)) isoDt = DateTime('2006-01') self.assertTrue(ref4.equalTo(isoDt)) isoDt = DateTime('200601') self.assertTrue(ref4.equalTo(isoDt)) isoDt = DateTime('2006') self.assertTrue(ref4.equalTo(isoDt)) # Bug 2191: date/time separators are also optional isoDt = DateTime('20020502T08:00:00') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002-05-02T080000') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('20020502T080000') self.assertTrue(ref0.equalTo(isoDt)) # Bug 2191: timezones with only one digit for hour isoDt = DateTime('20020502T080000+0') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('20020502 080000-4') self.assertTrue(ref1.equalTo(isoDt)) isoDt = DateTime('20020502T080000-400') self.assertTrue(ref1.equalTo(isoDt)) isoDt = DateTime('20020502T080000-4:00') self.assertTrue(ref1.equalTo(isoDt)) # Bug 2191: optional seconds/minutes isoDt = DateTime('2002-05-02T0800') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002-05-02T08') self.assertTrue(ref0.equalTo(isoDt)) # Bug 2191: week format isoDt = DateTime('2002-W18-4T0800') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002-W184T0800') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002W18-4T0800') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002W184T08') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2004-W25-1T14:30:15-03:00') self.assertTrue(ref3.equalTo(isoDt)) isoDt = DateTime('2004-W25T14:30:15-03:00') self.assertTrue(ref3.equalTo(isoDt)) # Bug 2191: day of year format isoDt = DateTime('2002-122T0800') self.assertTrue(ref0.equalTo(isoDt)) isoDt = DateTime('2002122T0800') self.assertTrue(ref0.equalTo(isoDt)) # Bug 2191: hours/minutes fractions isoDt = DateTime('2006-11-06T10.5') self.assertTrue(ref2.equalTo(isoDt)) isoDt = DateTime('2006-11-06T10,5') self.assertTrue(ref2.equalTo(isoDt)) isoDt = DateTime('20040614T1430.25-3') self.assertTrue(ref3.equalTo(isoDt)) isoDt = DateTime('2004-06-14T1430,25-3') self.assertTrue(ref3.equalTo(isoDt)) isoDt = DateTime('2004-06-14T14:30.25-3') self.assertTrue(ref3.equalTo(isoDt)) isoDt = DateTime('20040614T14:30,25-3') self.assertTrue(ref3.equalTo(isoDt)) # ISO8601 standard format iso8601_string = '2002-05-02T08:00:00-04:00' iso8601DT = DateTime(iso8601_string) self.assertEqual(iso8601_string, iso8601DT.ISO8601()) # ISO format with no timezone isoDt = DateTime('2006-01-01 00:00:00') self.assertTrue(ref4.equalTo(isoDt)) def testJulianWeek(self): # Check JulianDayWeek function fn = os.path.join(DATADIR, 'julian_testdata.txt') with open(fn) as fd: lines = fd.readlines() for line in lines: d = DateTime(line[:10]) result_from_mx = tuple(map(int, line[12:-2].split(','))) self.assertEqual(result_from_mx[1], d.week()) def testCopyConstructor(self): d = DateTime('2004/04/04') self.assertEqual(DateTime(d), d) self.assertEqual(str(DateTime(d)), str(d)) d2 = DateTime('1999/04/12 01:00:00') self.assertEqual(DateTime(d2), d2) self.assertEqual(str(DateTime(d2)), str(d2)) def testCopyConstructorPreservesTimezone(self): # test for https://bugs.launchpad.net/zope2/+bug/200007 # This always worked in the local timezone, so we need at least # two tests with different zones to be sure at least one of them # is not local. d = DateTime('2004/04/04') self.assertEqual(DateTime(d).timezone(), d.timezone()) d2 = DateTime('2008/04/25 12:00:00 EST') self.assertEqual(DateTime(d2).timezone(), d2.timezone()) self.assertEqual(str(DateTime(d2)), str(d2)) d3 = DateTime('2008/04/25 12:00:00 PST') self.assertEqual(DateTime(d3).timezone(), d3.timezone()) self.assertEqual(str(DateTime(d3)), str(d3)) def testRFC822(self): # rfc822 conversion dt = DateTime('2002-05-02T08:00:00+00:00') self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0000') dt = DateTime('2002-05-02T08:00:00+02:00') self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0200') dt = DateTime('2002-05-02T08:00:00-02:00') self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 -0200') # Checking that conversion from local time is working. dt = DateTime() dts = dt.rfc822().split(' ') times = dts[4].split(':') _isDST = time.localtime(time.time())[8] if _isDST: offset = time.altzone else: offset = time.timezone self.assertEqual(dts[0], dt.aDay() + ',') self.assertEqual(int(dts[1]), dt.day()) self.assertEqual(dts[2], dt.aMonth()) self.assertEqual(int(dts[3]), dt.year()) self.assertEqual(int(times[0]), dt.h_24()) self.assertEqual(int(times[1]), dt.minute()) self.assertEqual(int(times[2]), int(dt.second())) self.assertEqual(dts[5], "%+03d%02d" % divmod((-offset / 60), 60)) def testInternationalDateformat(self): for year in (1990, 2001, 2020): for month in (1, 12): for day in (1, 12, 28, 31): try: d_us = DateTime("%d/%d/%d" % (year, month, day)) except Exception: continue d_int = DateTime("%d.%d.%d" % (day, month, year), datefmt="international") self.assertEqual(d_us, d_int) d_int = DateTime("%d/%d/%d" % (day, month, year), datefmt="international") self.assertEqual(d_us, d_int) def test_intl_format_hyphen(self): d_jan = DateTime('2011-01-11 GMT') d_nov = DateTime('2011-11-01 GMT') d_us = DateTime('11-01-2011 GMT') d_int = DateTime('11-01-2011 GMT', datefmt="international") self.assertNotEqual(d_us, d_int) self.assertEqual(d_us, d_nov) self.assertEqual(d_int, d_jan) def test_calcTimezoneName(self): from DateTime.interfaces import TimeError timezone_dependent_epoch = 2177452800 try: DateTime()._calcTimezoneName(timezone_dependent_epoch, 0) except TimeError: self.fail('Zope Collector issue #484 (negative time bug): ' 'TimeError raised') def testStrftimeTZhandling(self): # strftime timezone testing # This is a test for collector issue #1127 format = '%Y-%m-%d %H:%M %Z' dt = DateTime('Wed, 19 Nov 2003 18:32:07 -0215') dt_string = dt.strftime(format) dt_local = dt.toZone(_findLocalTimeZoneName(0)) dt_localstring = dt_local.strftime(format) self.assertEqual(dt_string, dt_localstring) def testStrftimeFarDates(self): # Checks strftime in dates <= 1900 or >= 2038 dt = DateTime('1900/01/30') self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/1900') dt = DateTime('2040/01/30') self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/2040') def testZoneInFarDates(self): # Checks time zone in dates <= 1900 or >= 2038 dt1 = DateTime('2040/01/30 14:33 GMT+1') dt2 = DateTime('2040/01/30 11:33 GMT-2') self.assertEqual(dt1.strftime('%d/%m/%Y %H:%M'), dt2.strftime('%d/%m/%Y %H:%M')) @unittest.skipIf( IS_PYPY, "Using Non-Ascii characters for strftime doesn't work in PyPy" "https://bitbucket.org/pypy/pypy/issues/2161/pypy3-strftime-does-not-accept-unicode" # noqa: E501 line too long ) def testStrftimeStr(self): dt = DateTime('2002-05-02T08:00:00+00:00') uchar = b'\xc3\xa0'.decode('utf-8') ok = dt.strftime('Le %d/%m/%Y a %Hh%M').replace('a', uchar) ustr = b'Le %d/%m/%Y \xc3\xa0 %Hh%M'.decode('utf-8') self.assertEqual(dt.strftime(ustr), ok) def testTimezoneNaiveHandling(self): # checks that we assign timezone naivity correctly dt = DateTime('2007-10-04T08:00:00+00:00') self.assertFalse(dt.timezoneNaive(), 'error with naivity handling in __parse_iso8601') dt = DateTime('2007-10-04T08:00:00Z') self.assertFalse(dt.timezoneNaive(), 'error with naivity handling in __parse_iso8601') dt = DateTime('2007-10-04T08:00:00') self.assertTrue(dt.timezoneNaive(), 'error with naivity handling in __parse_iso8601') dt = DateTime('2007/10/04 15:12:33.487618 GMT+1') self.assertFalse(dt.timezoneNaive(), 'error with naivity handling in _parse') dt = DateTime('2007/10/04 15:12:33.487618') self.assertTrue(dt.timezoneNaive(), 'error with naivity handling in _parse') dt = DateTime() self.assertFalse(dt.timezoneNaive(), 'error with naivity for current time') s = '2007-10-04T08:00:00' dt = DateTime(s) self.assertEqual(s, dt.ISO8601()) s = '2007-10-04T08:00:00+00:00' dt = DateTime(s) self.assertEqual(s, dt.ISO8601()) def testConversions(self): sdt0 = datetime.now() # this is a timezone naive datetime dt0 = DateTime(sdt0) self.assertTrue(dt0.timezoneNaive(), (sdt0, dt0)) sdt1 = datetime(2007, 10, 4, 18, 14, 42, 580, pytz.utc) dt1 = DateTime(sdt1) self.assertFalse(dt1.timezoneNaive(), (sdt1, dt1)) # convert back sdt2 = dt0.asdatetime() self.assertEqual(sdt0, sdt2) sdt3 = dt1.utcdatetime() # this returns a timezone naive datetime self.assertEqual(sdt1.hour, sdt3.hour) dt4 = DateTime('2007-10-04T10:00:00+05:00') sdt4 = datetime(2007, 10, 4, 5, 0) self.assertEqual(dt4.utcdatetime(), sdt4) self.assertEqual(dt4.asdatetime(), sdt4.replace(tzinfo=pytz.utc)) dt5 = DateTime('2007-10-23 10:00:00 US/Eastern') tz = pytz.timezone('US/Eastern') sdt5 = datetime(2007, 10, 23, 10, 0, tzinfo=tz) dt6 = DateTime(sdt5) self.assertEqual(dt5.asdatetime(), sdt5) self.assertEqual(dt6.asdatetime(), sdt5) self.assertEqual(dt5, dt6) self.assertEqual(dt5.asdatetime().tzinfo, tz) self.assertEqual(dt6.asdatetime().tzinfo, tz) def testBasicTZ(self): # psycopg2 supplies it's own tzinfo instances, with no `zone` attribute tz = FixedOffset(60, 'GMT+1') dt1 = datetime(2008, 8, 5, 12, 0, tzinfo=tz) DT = DateTime(dt1) dt2 = DT.asdatetime() offset1 = dt1.tzinfo.utcoffset(dt1) offset2 = dt2.tzinfo.utcoffset(dt2) self.assertEqual(offset1, offset2) def testEDTTimezone(self): # should be able to parse EDT timezones: see lp:599856. dt = DateTime("Mon, 28 Jun 2010 10:12:25 EDT") self.assertEqual(dt.Day(), 'Monday') self.assertEqual(dt.day(), 28) self.assertEqual(dt.Month(), 'June') self.assertEqual(dt.timezone(), 'GMT-4') def testParseISO8601(self): parsed = DateTime()._parse_iso8601('2010-10-10') self.assertEqual(parsed, (2010, 10, 10, 0, 0, 0, 'GMT+0000')) def test_interface(self): from DateTime.interfaces import IDateTime self.assertTrue(IDateTime.providedBy(DateTime())) def test_security(self): dt = DateTime() self.assertEqual(dt.__roles__, None) self.assertEqual(dt.__allow_access_to_unprotected_subobjects__, 1) def test_format(self): dt = DateTime(1968, 3, 10, 23, 45, 0, 'Europe/Vienna') fmt = '%d.%m.%Y %H:%M' result = dt.strftime(fmt) unformatted_result = '1968/03/10 23:45:00 Europe/Vienna' self.assertEqual(result, f'{dt:%d.%m.%Y %H:%M}') self.assertEqual(unformatted_result, f'{dt}') self.assertEqual(unformatted_result, f'{dt}') self.assertEqual(result, f'{dt:{fmt}}') self.assertEqual(unformatted_result, f'{dt:}') self.assertEqual(unformatted_result, f'{dt}') def test_suite(): import doctest return unittest.TestSuite([ unittest.defaultTestLoader.loadTestsFromTestCase(DateTimeTests), doctest.DocFileSuite('DateTime.txt', package='DateTime'), doctest.DocFileSuite('pytz.txt', package='DateTime'), ])