ยปCore Development>Code coverage>Lib/test/datetimetester.py

Python code coverage for Lib/test/datetimetester.py

#countcontent
1n/a"""Test date/time type.
2n/a
3n/aSee http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4n/a"""
5n/afrom test.support import is_resource_enabled
6n/a
7n/aimport itertools
8n/aimport bisect
9n/a
10n/aimport copy
11n/aimport decimal
12n/aimport sys
13n/aimport os
14n/aimport pickle
15n/aimport random
16n/aimport struct
17n/aimport unittest
18n/a
19n/afrom array import array
20n/a
21n/afrom operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
22n/a
23n/afrom test import support
24n/a
25n/aimport datetime as datetime_module
26n/afrom datetime import MINYEAR, MAXYEAR
27n/afrom datetime import timedelta
28n/afrom datetime import tzinfo
29n/afrom datetime import time
30n/afrom datetime import timezone
31n/afrom datetime import date, datetime
32n/aimport time as _time
33n/a
34n/a# Needed by test_datetime
35n/aimport _strptime
36n/a#
37n/a
38n/a
39n/apickle_choices = [(pickle, pickle, proto)
40n/a for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
41n/aassert len(pickle_choices) == pickle.HIGHEST_PROTOCOL + 1
42n/a
43n/a# An arbitrary collection of objects of non-datetime types, for testing
44n/a# mixed-type comparisons.
45n/aOTHERSTUFF = (10, 34.5, "abc", {}, [], ())
46n/a
47n/a
48n/a# XXX Copied from test_float.
49n/aINF = float("inf")
50n/aNAN = float("nan")
51n/a
52n/a
53n/a#############################################################################
54n/a# module tests
55n/a
56n/aclass TestModule(unittest.TestCase):
57n/a
58n/a def test_constants(self):
59n/a datetime = datetime_module
60n/a self.assertEqual(datetime.MINYEAR, 1)
61n/a self.assertEqual(datetime.MAXYEAR, 9999)
62n/a
63n/a def test_name_cleanup(self):
64n/a if '_Fast' not in str(self):
65n/a return
66n/a datetime = datetime_module
67n/a names = set(name for name in dir(datetime)
68n/a if not name.startswith('__') and not name.endswith('__'))
69n/a allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
70n/a 'datetime_CAPI', 'time', 'timedelta', 'timezone',
71n/a 'tzinfo'])
72n/a self.assertEqual(names - allowed, set([]))
73n/a
74n/a def test_divide_and_round(self):
75n/a if '_Fast' in str(self):
76n/a return
77n/a dar = datetime_module._divide_and_round
78n/a
79n/a self.assertEqual(dar(-10, -3), 3)
80n/a self.assertEqual(dar(5, -2), -2)
81n/a
82n/a # four cases: (2 signs of a) x (2 signs of b)
83n/a self.assertEqual(dar(7, 3), 2)
84n/a self.assertEqual(dar(-7, 3), -2)
85n/a self.assertEqual(dar(7, -3), -2)
86n/a self.assertEqual(dar(-7, -3), 2)
87n/a
88n/a # ties to even - eight cases:
89n/a # (2 signs of a) x (2 signs of b) x (even / odd quotient)
90n/a self.assertEqual(dar(10, 4), 2)
91n/a self.assertEqual(dar(-10, 4), -2)
92n/a self.assertEqual(dar(10, -4), -2)
93n/a self.assertEqual(dar(-10, -4), 2)
94n/a
95n/a self.assertEqual(dar(6, 4), 2)
96n/a self.assertEqual(dar(-6, 4), -2)
97n/a self.assertEqual(dar(6, -4), -2)
98n/a self.assertEqual(dar(-6, -4), 2)
99n/a
100n/a
101n/a#############################################################################
102n/a# tzinfo tests
103n/a
104n/aclass FixedOffset(tzinfo):
105n/a
106n/a def __init__(self, offset, name, dstoffset=42):
107n/a if isinstance(offset, int):
108n/a offset = timedelta(minutes=offset)
109n/a if isinstance(dstoffset, int):
110n/a dstoffset = timedelta(minutes=dstoffset)
111n/a self.__offset = offset
112n/a self.__name = name
113n/a self.__dstoffset = dstoffset
114n/a def __repr__(self):
115n/a return self.__name.lower()
116n/a def utcoffset(self, dt):
117n/a return self.__offset
118n/a def tzname(self, dt):
119n/a return self.__name
120n/a def dst(self, dt):
121n/a return self.__dstoffset
122n/a
123n/aclass PicklableFixedOffset(FixedOffset):
124n/a
125n/a def __init__(self, offset=None, name=None, dstoffset=None):
126n/a FixedOffset.__init__(self, offset, name, dstoffset)
127n/a
128n/a def __getstate__(self):
129n/a return self.__dict__
130n/a
131n/aclass _TZInfo(tzinfo):
132n/a def utcoffset(self, datetime_module):
133n/a return random.random()
134n/a
135n/aclass TestTZInfo(unittest.TestCase):
136n/a
137n/a def test_refcnt_crash_bug_22044(self):
138n/a tz1 = _TZInfo()
139n/a dt1 = datetime(2014, 7, 21, 11, 32, 3, 0, tz1)
140n/a with self.assertRaises(TypeError):
141n/a dt1.utcoffset()
142n/a
143n/a def test_non_abstractness(self):
144n/a # In order to allow subclasses to get pickled, the C implementation
145n/a # wasn't able to get away with having __init__ raise
146n/a # NotImplementedError.
147n/a useless = tzinfo()
148n/a dt = datetime.max
149n/a self.assertRaises(NotImplementedError, useless.tzname, dt)
150n/a self.assertRaises(NotImplementedError, useless.utcoffset, dt)
151n/a self.assertRaises(NotImplementedError, useless.dst, dt)
152n/a
153n/a def test_subclass_must_override(self):
154n/a class NotEnough(tzinfo):
155n/a def __init__(self, offset, name):
156n/a self.__offset = offset
157n/a self.__name = name
158n/a self.assertTrue(issubclass(NotEnough, tzinfo))
159n/a ne = NotEnough(3, "NotByALongShot")
160n/a self.assertIsInstance(ne, tzinfo)
161n/a
162n/a dt = datetime.now()
163n/a self.assertRaises(NotImplementedError, ne.tzname, dt)
164n/a self.assertRaises(NotImplementedError, ne.utcoffset, dt)
165n/a self.assertRaises(NotImplementedError, ne.dst, dt)
166n/a
167n/a def test_normal(self):
168n/a fo = FixedOffset(3, "Three")
169n/a self.assertIsInstance(fo, tzinfo)
170n/a for dt in datetime.now(), None:
171n/a self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
172n/a self.assertEqual(fo.tzname(dt), "Three")
173n/a self.assertEqual(fo.dst(dt), timedelta(minutes=42))
174n/a
175n/a def test_pickling_base(self):
176n/a # There's no point to pickling tzinfo objects on their own (they
177n/a # carry no data), but they need to be picklable anyway else
178n/a # concrete subclasses can't be pickled.
179n/a orig = tzinfo.__new__(tzinfo)
180n/a self.assertIs(type(orig), tzinfo)
181n/a for pickler, unpickler, proto in pickle_choices:
182n/a green = pickler.dumps(orig, proto)
183n/a derived = unpickler.loads(green)
184n/a self.assertIs(type(derived), tzinfo)
185n/a
186n/a def test_pickling_subclass(self):
187n/a # Make sure we can pickle/unpickle an instance of a subclass.
188n/a offset = timedelta(minutes=-300)
189n/a for otype, args in [
190n/a (PicklableFixedOffset, (offset, 'cookie')),
191n/a (timezone, (offset,)),
192n/a (timezone, (offset, "EST"))]:
193n/a orig = otype(*args)
194n/a oname = orig.tzname(None)
195n/a self.assertIsInstance(orig, tzinfo)
196n/a self.assertIs(type(orig), otype)
197n/a self.assertEqual(orig.utcoffset(None), offset)
198n/a self.assertEqual(orig.tzname(None), oname)
199n/a for pickler, unpickler, proto in pickle_choices:
200n/a green = pickler.dumps(orig, proto)
201n/a derived = unpickler.loads(green)
202n/a self.assertIsInstance(derived, tzinfo)
203n/a self.assertIs(type(derived), otype)
204n/a self.assertEqual(derived.utcoffset(None), offset)
205n/a self.assertEqual(derived.tzname(None), oname)
206n/a
207n/a def test_issue23600(self):
208n/a DSTDIFF = DSTOFFSET = timedelta(hours=1)
209n/a
210n/a class UKSummerTime(tzinfo):
211n/a """Simple time zone which pretends to always be in summer time, since
212n/a that's what shows the failure.
213n/a """
214n/a
215n/a def utcoffset(self, dt):
216n/a return DSTOFFSET
217n/a
218n/a def dst(self, dt):
219n/a return DSTDIFF
220n/a
221n/a def tzname(self, dt):
222n/a return 'UKSummerTime'
223n/a
224n/a tz = UKSummerTime()
225n/a u = datetime(2014, 4, 26, 12, 1, tzinfo=tz)
226n/a t = tz.fromutc(u)
227n/a self.assertEqual(t - t.utcoffset(), u)
228n/a
229n/a
230n/aclass TestTimeZone(unittest.TestCase):
231n/a
232n/a def setUp(self):
233n/a self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
234n/a self.EST = timezone(-timedelta(hours=5), 'EST')
235n/a self.DT = datetime(2010, 1, 1)
236n/a
237n/a def test_str(self):
238n/a for tz in [self.ACDT, self.EST, timezone.utc,
239n/a timezone.min, timezone.max]:
240n/a self.assertEqual(str(tz), tz.tzname(None))
241n/a
242n/a def test_repr(self):
243n/a datetime = datetime_module
244n/a for tz in [self.ACDT, self.EST, timezone.utc,
245n/a timezone.min, timezone.max]:
246n/a # test round-trip
247n/a tzrep = repr(tz)
248n/a self.assertEqual(tz, eval(tzrep))
249n/a
250n/a def test_class_members(self):
251n/a limit = timedelta(hours=23, minutes=59)
252n/a self.assertEqual(timezone.utc.utcoffset(None), ZERO)
253n/a self.assertEqual(timezone.min.utcoffset(None), -limit)
254n/a self.assertEqual(timezone.max.utcoffset(None), limit)
255n/a
256n/a
257n/a def test_constructor(self):
258n/a self.assertIs(timezone.utc, timezone(timedelta(0)))
259n/a self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
260n/a self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
261n/a # invalid offsets
262n/a for invalid in [timedelta(microseconds=1), timedelta(1, 1),
263n/a timedelta(seconds=1), timedelta(1), -timedelta(1)]:
264n/a self.assertRaises(ValueError, timezone, invalid)
265n/a self.assertRaises(ValueError, timezone, -invalid)
266n/a
267n/a with self.assertRaises(TypeError): timezone(None)
268n/a with self.assertRaises(TypeError): timezone(42)
269n/a with self.assertRaises(TypeError): timezone(ZERO, None)
270n/a with self.assertRaises(TypeError): timezone(ZERO, 42)
271n/a with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
272n/a
273n/a def test_inheritance(self):
274n/a self.assertIsInstance(timezone.utc, tzinfo)
275n/a self.assertIsInstance(self.EST, tzinfo)
276n/a
277n/a def test_utcoffset(self):
278n/a dummy = self.DT
279n/a for h in [0, 1.5, 12]:
280n/a offset = h * HOUR
281n/a self.assertEqual(offset, timezone(offset).utcoffset(dummy))
282n/a self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
283n/a
284n/a with self.assertRaises(TypeError): self.EST.utcoffset('')
285n/a with self.assertRaises(TypeError): self.EST.utcoffset(5)
286n/a
287n/a
288n/a def test_dst(self):
289n/a self.assertIsNone(timezone.utc.dst(self.DT))
290n/a
291n/a with self.assertRaises(TypeError): self.EST.dst('')
292n/a with self.assertRaises(TypeError): self.EST.dst(5)
293n/a
294n/a def test_tzname(self):
295n/a self.assertEqual('UTC', timezone.utc.tzname(None))
296n/a self.assertEqual('UTC', timezone(ZERO).tzname(None))
297n/a self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
298n/a self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
299n/a self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
300n/a self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
301n/a
302n/a with self.assertRaises(TypeError): self.EST.tzname('')
303n/a with self.assertRaises(TypeError): self.EST.tzname(5)
304n/a
305n/a def test_fromutc(self):
306n/a with self.assertRaises(ValueError):
307n/a timezone.utc.fromutc(self.DT)
308n/a with self.assertRaises(TypeError):
309n/a timezone.utc.fromutc('not datetime')
310n/a for tz in [self.EST, self.ACDT, Eastern]:
311n/a utctime = self.DT.replace(tzinfo=tz)
312n/a local = tz.fromutc(utctime)
313n/a self.assertEqual(local - utctime, tz.utcoffset(local))
314n/a self.assertEqual(local,
315n/a self.DT.replace(tzinfo=timezone.utc))
316n/a
317n/a def test_comparison(self):
318n/a self.assertNotEqual(timezone(ZERO), timezone(HOUR))
319n/a self.assertEqual(timezone(HOUR), timezone(HOUR))
320n/a self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
321n/a with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
322n/a self.assertIn(timezone(ZERO), {timezone(ZERO)})
323n/a self.assertTrue(timezone(ZERO) != None)
324n/a self.assertFalse(timezone(ZERO) == None)
325n/a
326n/a def test_aware_datetime(self):
327n/a # test that timezone instances can be used by datetime
328n/a t = datetime(1, 1, 1)
329n/a for tz in [timezone.min, timezone.max, timezone.utc]:
330n/a self.assertEqual(tz.tzname(t),
331n/a t.replace(tzinfo=tz).tzname())
332n/a self.assertEqual(tz.utcoffset(t),
333n/a t.replace(tzinfo=tz).utcoffset())
334n/a self.assertEqual(tz.dst(t),
335n/a t.replace(tzinfo=tz).dst())
336n/a
337n/a def test_pickle(self):
338n/a for tz in self.ACDT, self.EST, timezone.min, timezone.max:
339n/a for pickler, unpickler, proto in pickle_choices:
340n/a tz_copy = unpickler.loads(pickler.dumps(tz, proto))
341n/a self.assertEqual(tz_copy, tz)
342n/a tz = timezone.utc
343n/a for pickler, unpickler, proto in pickle_choices:
344n/a tz_copy = unpickler.loads(pickler.dumps(tz, proto))
345n/a self.assertIs(tz_copy, tz)
346n/a
347n/a def test_copy(self):
348n/a for tz in self.ACDT, self.EST, timezone.min, timezone.max:
349n/a tz_copy = copy.copy(tz)
350n/a self.assertEqual(tz_copy, tz)
351n/a tz = timezone.utc
352n/a tz_copy = copy.copy(tz)
353n/a self.assertIs(tz_copy, tz)
354n/a
355n/a def test_deepcopy(self):
356n/a for tz in self.ACDT, self.EST, timezone.min, timezone.max:
357n/a tz_copy = copy.deepcopy(tz)
358n/a self.assertEqual(tz_copy, tz)
359n/a tz = timezone.utc
360n/a tz_copy = copy.deepcopy(tz)
361n/a self.assertIs(tz_copy, tz)
362n/a
363n/a
364n/a#############################################################################
365n/a# Base class for testing a particular aspect of timedelta, time, date and
366n/a# datetime comparisons.
367n/a
368n/aclass HarmlessMixedComparison:
369n/a # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
370n/a
371n/a # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
372n/a # legit constructor.
373n/a
374n/a def test_harmless_mixed_comparison(self):
375n/a me = self.theclass(1, 1, 1)
376n/a
377n/a self.assertFalse(me == ())
378n/a self.assertTrue(me != ())
379n/a self.assertFalse(() == me)
380n/a self.assertTrue(() != me)
381n/a
382n/a self.assertIn(me, [1, 20, [], me])
383n/a self.assertIn([], [me, 1, 20, []])
384n/a
385n/a def test_harmful_mixed_comparison(self):
386n/a me = self.theclass(1, 1, 1)
387n/a
388n/a self.assertRaises(TypeError, lambda: me < ())
389n/a self.assertRaises(TypeError, lambda: me <= ())
390n/a self.assertRaises(TypeError, lambda: me > ())
391n/a self.assertRaises(TypeError, lambda: me >= ())
392n/a
393n/a self.assertRaises(TypeError, lambda: () < me)
394n/a self.assertRaises(TypeError, lambda: () <= me)
395n/a self.assertRaises(TypeError, lambda: () > me)
396n/a self.assertRaises(TypeError, lambda: () >= me)
397n/a
398n/a#############################################################################
399n/a# timedelta tests
400n/a
401n/aclass TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
402n/a
403n/a theclass = timedelta
404n/a
405n/a def test_constructor(self):
406n/a eq = self.assertEqual
407n/a td = timedelta
408n/a
409n/a # Check keyword args to constructor
410n/a eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
411n/a milliseconds=0, microseconds=0))
412n/a eq(td(1), td(days=1))
413n/a eq(td(0, 1), td(seconds=1))
414n/a eq(td(0, 0, 1), td(microseconds=1))
415n/a eq(td(weeks=1), td(days=7))
416n/a eq(td(days=1), td(hours=24))
417n/a eq(td(hours=1), td(minutes=60))
418n/a eq(td(minutes=1), td(seconds=60))
419n/a eq(td(seconds=1), td(milliseconds=1000))
420n/a eq(td(milliseconds=1), td(microseconds=1000))
421n/a
422n/a # Check float args to constructor
423n/a eq(td(weeks=1.0/7), td(days=1))
424n/a eq(td(days=1.0/24), td(hours=1))
425n/a eq(td(hours=1.0/60), td(minutes=1))
426n/a eq(td(minutes=1.0/60), td(seconds=1))
427n/a eq(td(seconds=0.001), td(milliseconds=1))
428n/a eq(td(milliseconds=0.001), td(microseconds=1))
429n/a
430n/a def test_computations(self):
431n/a eq = self.assertEqual
432n/a td = timedelta
433n/a
434n/a a = td(7) # One week
435n/a b = td(0, 60) # One minute
436n/a c = td(0, 0, 1000) # One millisecond
437n/a eq(a+b+c, td(7, 60, 1000))
438n/a eq(a-b, td(6, 24*3600 - 60))
439n/a eq(b.__rsub__(a), td(6, 24*3600 - 60))
440n/a eq(-a, td(-7))
441n/a eq(+a, td(7))
442n/a eq(-b, td(-1, 24*3600 - 60))
443n/a eq(-c, td(-1, 24*3600 - 1, 999000))
444n/a eq(abs(a), a)
445n/a eq(abs(-a), a)
446n/a eq(td(6, 24*3600), a)
447n/a eq(td(0, 0, 60*1000000), b)
448n/a eq(a*10, td(70))
449n/a eq(a*10, 10*a)
450n/a eq(a*10, 10*a)
451n/a eq(b*10, td(0, 600))
452n/a eq(10*b, td(0, 600))
453n/a eq(b*10, td(0, 600))
454n/a eq(c*10, td(0, 0, 10000))
455n/a eq(10*c, td(0, 0, 10000))
456n/a eq(c*10, td(0, 0, 10000))
457n/a eq(a*-1, -a)
458n/a eq(b*-2, -b-b)
459n/a eq(c*-2, -c+-c)
460n/a eq(b*(60*24), (b*60)*24)
461n/a eq(b*(60*24), (60*b)*24)
462n/a eq(c*1000, td(0, 1))
463n/a eq(1000*c, td(0, 1))
464n/a eq(a//7, td(1))
465n/a eq(b//10, td(0, 6))
466n/a eq(c//1000, td(0, 0, 1))
467n/a eq(a//10, td(0, 7*24*360))
468n/a eq(a//3600000, td(0, 0, 7*24*1000))
469n/a eq(a/0.5, td(14))
470n/a eq(b/0.5, td(0, 120))
471n/a eq(a/7, td(1))
472n/a eq(b/10, td(0, 6))
473n/a eq(c/1000, td(0, 0, 1))
474n/a eq(a/10, td(0, 7*24*360))
475n/a eq(a/3600000, td(0, 0, 7*24*1000))
476n/a
477n/a # Multiplication by float
478n/a us = td(microseconds=1)
479n/a eq((3*us) * 0.5, 2*us)
480n/a eq((5*us) * 0.5, 2*us)
481n/a eq(0.5 * (3*us), 2*us)
482n/a eq(0.5 * (5*us), 2*us)
483n/a eq((-3*us) * 0.5, -2*us)
484n/a eq((-5*us) * 0.5, -2*us)
485n/a
486n/a # Issue #23521
487n/a eq(td(seconds=1) * 0.123456, td(microseconds=123456))
488n/a eq(td(seconds=1) * 0.6112295, td(microseconds=611229))
489n/a
490n/a # Division by int and float
491n/a eq((3*us) / 2, 2*us)
492n/a eq((5*us) / 2, 2*us)
493n/a eq((-3*us) / 2.0, -2*us)
494n/a eq((-5*us) / 2.0, -2*us)
495n/a eq((3*us) / -2, -2*us)
496n/a eq((5*us) / -2, -2*us)
497n/a eq((3*us) / -2.0, -2*us)
498n/a eq((5*us) / -2.0, -2*us)
499n/a for i in range(-10, 10):
500n/a eq((i*us/3)//us, round(i/3))
501n/a for i in range(-10, 10):
502n/a eq((i*us/-3)//us, round(i/-3))
503n/a
504n/a # Issue #23521
505n/a eq(td(seconds=1) / (1 / 0.6112295), td(microseconds=611229))
506n/a
507n/a # Issue #11576
508n/a eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
509n/a td(0, 0, 1))
510n/a eq(td(999999999, 1, 1) - td(999999999, 1, 0),
511n/a td(0, 0, 1))
512n/a
513n/a def test_disallowed_computations(self):
514n/a a = timedelta(42)
515n/a
516n/a # Add/sub ints or floats should be illegal
517n/a for i in 1, 1.0:
518n/a self.assertRaises(TypeError, lambda: a+i)
519n/a self.assertRaises(TypeError, lambda: a-i)
520n/a self.assertRaises(TypeError, lambda: i+a)
521n/a self.assertRaises(TypeError, lambda: i-a)
522n/a
523n/a # Division of int by timedelta doesn't make sense.
524n/a # Division by zero doesn't make sense.
525n/a zero = 0
526n/a self.assertRaises(TypeError, lambda: zero // a)
527n/a self.assertRaises(ZeroDivisionError, lambda: a // zero)
528n/a self.assertRaises(ZeroDivisionError, lambda: a / zero)
529n/a self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
530n/a self.assertRaises(TypeError, lambda: a / '')
531n/a
532n/a @support.requires_IEEE_754
533n/a def test_disallowed_special(self):
534n/a a = timedelta(42)
535n/a self.assertRaises(ValueError, a.__mul__, NAN)
536n/a self.assertRaises(ValueError, a.__truediv__, NAN)
537n/a
538n/a def test_basic_attributes(self):
539n/a days, seconds, us = 1, 7, 31
540n/a td = timedelta(days, seconds, us)
541n/a self.assertEqual(td.days, days)
542n/a self.assertEqual(td.seconds, seconds)
543n/a self.assertEqual(td.microseconds, us)
544n/a
545n/a def test_total_seconds(self):
546n/a td = timedelta(days=365)
547n/a self.assertEqual(td.total_seconds(), 31536000.0)
548n/a for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
549n/a td = timedelta(seconds=total_seconds)
550n/a self.assertEqual(td.total_seconds(), total_seconds)
551n/a # Issue8644: Test that td.total_seconds() has the same
552n/a # accuracy as td / timedelta(seconds=1).
553n/a for ms in [-1, -2, -123]:
554n/a td = timedelta(microseconds=ms)
555n/a self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
556n/a
557n/a def test_carries(self):
558n/a t1 = timedelta(days=100,
559n/a weeks=-7,
560n/a hours=-24*(100-49),
561n/a minutes=-3,
562n/a seconds=12,
563n/a microseconds=(3*60 - 12) * 1e6 + 1)
564n/a t2 = timedelta(microseconds=1)
565n/a self.assertEqual(t1, t2)
566n/a
567n/a def test_hash_equality(self):
568n/a t1 = timedelta(days=100,
569n/a weeks=-7,
570n/a hours=-24*(100-49),
571n/a minutes=-3,
572n/a seconds=12,
573n/a microseconds=(3*60 - 12) * 1000000)
574n/a t2 = timedelta()
575n/a self.assertEqual(hash(t1), hash(t2))
576n/a
577n/a t1 += timedelta(weeks=7)
578n/a t2 += timedelta(days=7*7)
579n/a self.assertEqual(t1, t2)
580n/a self.assertEqual(hash(t1), hash(t2))
581n/a
582n/a d = {t1: 1}
583n/a d[t2] = 2
584n/a self.assertEqual(len(d), 1)
585n/a self.assertEqual(d[t1], 2)
586n/a
587n/a def test_pickling(self):
588n/a args = 12, 34, 56
589n/a orig = timedelta(*args)
590n/a for pickler, unpickler, proto in pickle_choices:
591n/a green = pickler.dumps(orig, proto)
592n/a derived = unpickler.loads(green)
593n/a self.assertEqual(orig, derived)
594n/a
595n/a def test_compare(self):
596n/a t1 = timedelta(2, 3, 4)
597n/a t2 = timedelta(2, 3, 4)
598n/a self.assertEqual(t1, t2)
599n/a self.assertTrue(t1 <= t2)
600n/a self.assertTrue(t1 >= t2)
601n/a self.assertFalse(t1 != t2)
602n/a self.assertFalse(t1 < t2)
603n/a self.assertFalse(t1 > t2)
604n/a
605n/a for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
606n/a t2 = timedelta(*args) # this is larger than t1
607n/a self.assertTrue(t1 < t2)
608n/a self.assertTrue(t2 > t1)
609n/a self.assertTrue(t1 <= t2)
610n/a self.assertTrue(t2 >= t1)
611n/a self.assertTrue(t1 != t2)
612n/a self.assertTrue(t2 != t1)
613n/a self.assertFalse(t1 == t2)
614n/a self.assertFalse(t2 == t1)
615n/a self.assertFalse(t1 > t2)
616n/a self.assertFalse(t2 < t1)
617n/a self.assertFalse(t1 >= t2)
618n/a self.assertFalse(t2 <= t1)
619n/a
620n/a for badarg in OTHERSTUFF:
621n/a self.assertEqual(t1 == badarg, False)
622n/a self.assertEqual(t1 != badarg, True)
623n/a self.assertEqual(badarg == t1, False)
624n/a self.assertEqual(badarg != t1, True)
625n/a
626n/a self.assertRaises(TypeError, lambda: t1 <= badarg)
627n/a self.assertRaises(TypeError, lambda: t1 < badarg)
628n/a self.assertRaises(TypeError, lambda: t1 > badarg)
629n/a self.assertRaises(TypeError, lambda: t1 >= badarg)
630n/a self.assertRaises(TypeError, lambda: badarg <= t1)
631n/a self.assertRaises(TypeError, lambda: badarg < t1)
632n/a self.assertRaises(TypeError, lambda: badarg > t1)
633n/a self.assertRaises(TypeError, lambda: badarg >= t1)
634n/a
635n/a def test_str(self):
636n/a td = timedelta
637n/a eq = self.assertEqual
638n/a
639n/a eq(str(td(1)), "1 day, 0:00:00")
640n/a eq(str(td(-1)), "-1 day, 0:00:00")
641n/a eq(str(td(2)), "2 days, 0:00:00")
642n/a eq(str(td(-2)), "-2 days, 0:00:00")
643n/a
644n/a eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
645n/a eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
646n/a eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
647n/a "-210 days, 23:12:34")
648n/a
649n/a eq(str(td(milliseconds=1)), "0:00:00.001000")
650n/a eq(str(td(microseconds=3)), "0:00:00.000003")
651n/a
652n/a eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
653n/a microseconds=999999)),
654n/a "999999999 days, 23:59:59.999999")
655n/a
656n/a def test_repr(self):
657n/a name = 'datetime.' + self.theclass.__name__
658n/a self.assertEqual(repr(self.theclass(1)),
659n/a "%s(1)" % name)
660n/a self.assertEqual(repr(self.theclass(10, 2)),
661n/a "%s(10, 2)" % name)
662n/a self.assertEqual(repr(self.theclass(-10, 2, 400000)),
663n/a "%s(-10, 2, 400000)" % name)
664n/a
665n/a def test_roundtrip(self):
666n/a for td in (timedelta(days=999999999, hours=23, minutes=59,
667n/a seconds=59, microseconds=999999),
668n/a timedelta(days=-999999999),
669n/a timedelta(days=-999999999, seconds=1),
670n/a timedelta(days=1, seconds=2, microseconds=3)):
671n/a
672n/a # Verify td -> string -> td identity.
673n/a s = repr(td)
674n/a self.assertTrue(s.startswith('datetime.'))
675n/a s = s[9:]
676n/a td2 = eval(s)
677n/a self.assertEqual(td, td2)
678n/a
679n/a # Verify identity via reconstructing from pieces.
680n/a td2 = timedelta(td.days, td.seconds, td.microseconds)
681n/a self.assertEqual(td, td2)
682n/a
683n/a def test_resolution_info(self):
684n/a self.assertIsInstance(timedelta.min, timedelta)
685n/a self.assertIsInstance(timedelta.max, timedelta)
686n/a self.assertIsInstance(timedelta.resolution, timedelta)
687n/a self.assertTrue(timedelta.max > timedelta.min)
688n/a self.assertEqual(timedelta.min, timedelta(-999999999))
689n/a self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
690n/a self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
691n/a
692n/a def test_overflow(self):
693n/a tiny = timedelta.resolution
694n/a
695n/a td = timedelta.min + tiny
696n/a td -= tiny # no problem
697n/a self.assertRaises(OverflowError, td.__sub__, tiny)
698n/a self.assertRaises(OverflowError, td.__add__, -tiny)
699n/a
700n/a td = timedelta.max - tiny
701n/a td += tiny # no problem
702n/a self.assertRaises(OverflowError, td.__add__, tiny)
703n/a self.assertRaises(OverflowError, td.__sub__, -tiny)
704n/a
705n/a self.assertRaises(OverflowError, lambda: -timedelta.max)
706n/a
707n/a day = timedelta(1)
708n/a self.assertRaises(OverflowError, day.__mul__, 10**9)
709n/a self.assertRaises(OverflowError, day.__mul__, 1e9)
710n/a self.assertRaises(OverflowError, day.__truediv__, 1e-20)
711n/a self.assertRaises(OverflowError, day.__truediv__, 1e-10)
712n/a self.assertRaises(OverflowError, day.__truediv__, 9e-10)
713n/a
714n/a @support.requires_IEEE_754
715n/a def _test_overflow_special(self):
716n/a day = timedelta(1)
717n/a self.assertRaises(OverflowError, day.__mul__, INF)
718n/a self.assertRaises(OverflowError, day.__mul__, -INF)
719n/a
720n/a def test_microsecond_rounding(self):
721n/a td = timedelta
722n/a eq = self.assertEqual
723n/a
724n/a # Single-field rounding.
725n/a eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
726n/a eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
727n/a eq(td(milliseconds=0.5/1000), td(microseconds=0))
728n/a eq(td(milliseconds=-0.5/1000), td(microseconds=-0))
729n/a eq(td(milliseconds=0.6/1000), td(microseconds=1))
730n/a eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
731n/a eq(td(milliseconds=1.5/1000), td(microseconds=2))
732n/a eq(td(milliseconds=-1.5/1000), td(microseconds=-2))
733n/a eq(td(seconds=0.5/10**6), td(microseconds=0))
734n/a eq(td(seconds=-0.5/10**6), td(microseconds=-0))
735n/a eq(td(seconds=1/2**7), td(microseconds=7812))
736n/a eq(td(seconds=-1/2**7), td(microseconds=-7812))
737n/a
738n/a # Rounding due to contributions from more than one field.
739n/a us_per_hour = 3600e6
740n/a us_per_day = us_per_hour * 24
741n/a eq(td(days=.4/us_per_day), td(0))
742n/a eq(td(hours=.2/us_per_hour), td(0))
743n/a eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
744n/a
745n/a eq(td(days=-.4/us_per_day), td(0))
746n/a eq(td(hours=-.2/us_per_hour), td(0))
747n/a eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
748n/a
749n/a # Test for a patch in Issue 8860
750n/a eq(td(microseconds=0.5), 0.5*td(microseconds=1.0))
751n/a eq(td(microseconds=0.5)//td.resolution, 0.5*td.resolution//td.resolution)
752n/a
753n/a def test_massive_normalization(self):
754n/a td = timedelta(microseconds=-1)
755n/a self.assertEqual((td.days, td.seconds, td.microseconds),
756n/a (-1, 24*3600-1, 999999))
757n/a
758n/a def test_bool(self):
759n/a self.assertTrue(timedelta(1))
760n/a self.assertTrue(timedelta(0, 1))
761n/a self.assertTrue(timedelta(0, 0, 1))
762n/a self.assertTrue(timedelta(microseconds=1))
763n/a self.assertFalse(timedelta(0))
764n/a
765n/a def test_subclass_timedelta(self):
766n/a
767n/a class T(timedelta):
768n/a @staticmethod
769n/a def from_td(td):
770n/a return T(td.days, td.seconds, td.microseconds)
771n/a
772n/a def as_hours(self):
773n/a sum = (self.days * 24 +
774n/a self.seconds / 3600.0 +
775n/a self.microseconds / 3600e6)
776n/a return round(sum)
777n/a
778n/a t1 = T(days=1)
779n/a self.assertIs(type(t1), T)
780n/a self.assertEqual(t1.as_hours(), 24)
781n/a
782n/a t2 = T(days=-1, seconds=-3600)
783n/a self.assertIs(type(t2), T)
784n/a self.assertEqual(t2.as_hours(), -25)
785n/a
786n/a t3 = t1 + t2
787n/a self.assertIs(type(t3), timedelta)
788n/a t4 = T.from_td(t3)
789n/a self.assertIs(type(t4), T)
790n/a self.assertEqual(t3.days, t4.days)
791n/a self.assertEqual(t3.seconds, t4.seconds)
792n/a self.assertEqual(t3.microseconds, t4.microseconds)
793n/a self.assertEqual(str(t3), str(t4))
794n/a self.assertEqual(t4.as_hours(), -1)
795n/a
796n/a def test_division(self):
797n/a t = timedelta(hours=1, minutes=24, seconds=19)
798n/a second = timedelta(seconds=1)
799n/a self.assertEqual(t / second, 5059.0)
800n/a self.assertEqual(t // second, 5059)
801n/a
802n/a t = timedelta(minutes=2, seconds=30)
803n/a minute = timedelta(minutes=1)
804n/a self.assertEqual(t / minute, 2.5)
805n/a self.assertEqual(t // minute, 2)
806n/a
807n/a zerotd = timedelta(0)
808n/a self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
809n/a self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
810n/a
811n/a # self.assertRaises(TypeError, truediv, t, 2)
812n/a # note: floor division of a timedelta by an integer *is*
813n/a # currently permitted.
814n/a
815n/a def test_remainder(self):
816n/a t = timedelta(minutes=2, seconds=30)
817n/a minute = timedelta(minutes=1)
818n/a r = t % minute
819n/a self.assertEqual(r, timedelta(seconds=30))
820n/a
821n/a t = timedelta(minutes=-2, seconds=30)
822n/a r = t % minute
823n/a self.assertEqual(r, timedelta(seconds=30))
824n/a
825n/a zerotd = timedelta(0)
826n/a self.assertRaises(ZeroDivisionError, mod, t, zerotd)
827n/a
828n/a self.assertRaises(TypeError, mod, t, 10)
829n/a
830n/a def test_divmod(self):
831n/a t = timedelta(minutes=2, seconds=30)
832n/a minute = timedelta(minutes=1)
833n/a q, r = divmod(t, minute)
834n/a self.assertEqual(q, 2)
835n/a self.assertEqual(r, timedelta(seconds=30))
836n/a
837n/a t = timedelta(minutes=-2, seconds=30)
838n/a q, r = divmod(t, minute)
839n/a self.assertEqual(q, -2)
840n/a self.assertEqual(r, timedelta(seconds=30))
841n/a
842n/a zerotd = timedelta(0)
843n/a self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
844n/a
845n/a self.assertRaises(TypeError, divmod, t, 10)
846n/a
847n/a
848n/a#############################################################################
849n/a# date tests
850n/a
851n/aclass TestDateOnly(unittest.TestCase):
852n/a # Tests here won't pass if also run on datetime objects, so don't
853n/a # subclass this to test datetimes too.
854n/a
855n/a def test_delta_non_days_ignored(self):
856n/a dt = date(2000, 1, 2)
857n/a delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
858n/a microseconds=5)
859n/a days = timedelta(delta.days)
860n/a self.assertEqual(days, timedelta(1))
861n/a
862n/a dt2 = dt + delta
863n/a self.assertEqual(dt2, dt + days)
864n/a
865n/a dt2 = delta + dt
866n/a self.assertEqual(dt2, dt + days)
867n/a
868n/a dt2 = dt - delta
869n/a self.assertEqual(dt2, dt - days)
870n/a
871n/a delta = -delta
872n/a days = timedelta(delta.days)
873n/a self.assertEqual(days, timedelta(-2))
874n/a
875n/a dt2 = dt + delta
876n/a self.assertEqual(dt2, dt + days)
877n/a
878n/a dt2 = delta + dt
879n/a self.assertEqual(dt2, dt + days)
880n/a
881n/a dt2 = dt - delta
882n/a self.assertEqual(dt2, dt - days)
883n/a
884n/aclass SubclassDate(date):
885n/a sub_var = 1
886n/a
887n/aclass TestDate(HarmlessMixedComparison, unittest.TestCase):
888n/a # Tests here should pass for both dates and datetimes, except for a
889n/a # few tests that TestDateTime overrides.
890n/a
891n/a theclass = date
892n/a
893n/a def test_basic_attributes(self):
894n/a dt = self.theclass(2002, 3, 1)
895n/a self.assertEqual(dt.year, 2002)
896n/a self.assertEqual(dt.month, 3)
897n/a self.assertEqual(dt.day, 1)
898n/a
899n/a def test_roundtrip(self):
900n/a for dt in (self.theclass(1, 2, 3),
901n/a self.theclass.today()):
902n/a # Verify dt -> string -> date identity.
903n/a s = repr(dt)
904n/a self.assertTrue(s.startswith('datetime.'))
905n/a s = s[9:]
906n/a dt2 = eval(s)
907n/a self.assertEqual(dt, dt2)
908n/a
909n/a # Verify identity via reconstructing from pieces.
910n/a dt2 = self.theclass(dt.year, dt.month, dt.day)
911n/a self.assertEqual(dt, dt2)
912n/a
913n/a def test_ordinal_conversions(self):
914n/a # Check some fixed values.
915n/a for y, m, d, n in [(1, 1, 1, 1), # calendar origin
916n/a (1, 12, 31, 365),
917n/a (2, 1, 1, 366),
918n/a # first example from "Calendrical Calculations"
919n/a (1945, 11, 12, 710347)]:
920n/a d = self.theclass(y, m, d)
921n/a self.assertEqual(n, d.toordinal())
922n/a fromord = self.theclass.fromordinal(n)
923n/a self.assertEqual(d, fromord)
924n/a if hasattr(fromord, "hour"):
925n/a # if we're checking something fancier than a date, verify
926n/a # the extra fields have been zeroed out
927n/a self.assertEqual(fromord.hour, 0)
928n/a self.assertEqual(fromord.minute, 0)
929n/a self.assertEqual(fromord.second, 0)
930n/a self.assertEqual(fromord.microsecond, 0)
931n/a
932n/a # Check first and last days of year spottily across the whole
933n/a # range of years supported.
934n/a for year in range(MINYEAR, MAXYEAR+1, 7):
935n/a # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
936n/a d = self.theclass(year, 1, 1)
937n/a n = d.toordinal()
938n/a d2 = self.theclass.fromordinal(n)
939n/a self.assertEqual(d, d2)
940n/a # Verify that moving back a day gets to the end of year-1.
941n/a if year > 1:
942n/a d = self.theclass.fromordinal(n-1)
943n/a d2 = self.theclass(year-1, 12, 31)
944n/a self.assertEqual(d, d2)
945n/a self.assertEqual(d2.toordinal(), n-1)
946n/a
947n/a # Test every day in a leap-year and a non-leap year.
948n/a dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
949n/a for year, isleap in (2000, True), (2002, False):
950n/a n = self.theclass(year, 1, 1).toordinal()
951n/a for month, maxday in zip(range(1, 13), dim):
952n/a if month == 2 and isleap:
953n/a maxday += 1
954n/a for day in range(1, maxday+1):
955n/a d = self.theclass(year, month, day)
956n/a self.assertEqual(d.toordinal(), n)
957n/a self.assertEqual(d, self.theclass.fromordinal(n))
958n/a n += 1
959n/a
960n/a def test_extreme_ordinals(self):
961n/a a = self.theclass.min
962n/a a = self.theclass(a.year, a.month, a.day) # get rid of time parts
963n/a aord = a.toordinal()
964n/a b = a.fromordinal(aord)
965n/a self.assertEqual(a, b)
966n/a
967n/a self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
968n/a
969n/a b = a + timedelta(days=1)
970n/a self.assertEqual(b.toordinal(), aord + 1)
971n/a self.assertEqual(b, self.theclass.fromordinal(aord + 1))
972n/a
973n/a a = self.theclass.max
974n/a a = self.theclass(a.year, a.month, a.day) # get rid of time parts
975n/a aord = a.toordinal()
976n/a b = a.fromordinal(aord)
977n/a self.assertEqual(a, b)
978n/a
979n/a self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
980n/a
981n/a b = a - timedelta(days=1)
982n/a self.assertEqual(b.toordinal(), aord - 1)
983n/a self.assertEqual(b, self.theclass.fromordinal(aord - 1))
984n/a
985n/a def test_bad_constructor_arguments(self):
986n/a # bad years
987n/a self.theclass(MINYEAR, 1, 1) # no exception
988n/a self.theclass(MAXYEAR, 1, 1) # no exception
989n/a self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
990n/a self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
991n/a # bad months
992n/a self.theclass(2000, 1, 1) # no exception
993n/a self.theclass(2000, 12, 1) # no exception
994n/a self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
995n/a self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
996n/a # bad days
997n/a self.theclass(2000, 2, 29) # no exception
998n/a self.theclass(2004, 2, 29) # no exception
999n/a self.theclass(2400, 2, 29) # no exception
1000n/a self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1001n/a self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1002n/a self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1003n/a self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1004n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1005n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1006n/a
1007n/a def test_hash_equality(self):
1008n/a d = self.theclass(2000, 12, 31)
1009n/a # same thing
1010n/a e = self.theclass(2000, 12, 31)
1011n/a self.assertEqual(d, e)
1012n/a self.assertEqual(hash(d), hash(e))
1013n/a
1014n/a dic = {d: 1}
1015n/a dic[e] = 2
1016n/a self.assertEqual(len(dic), 1)
1017n/a self.assertEqual(dic[d], 2)
1018n/a self.assertEqual(dic[e], 2)
1019n/a
1020n/a d = self.theclass(2001, 1, 1)
1021n/a # same thing
1022n/a e = self.theclass(2001, 1, 1)
1023n/a self.assertEqual(d, e)
1024n/a self.assertEqual(hash(d), hash(e))
1025n/a
1026n/a dic = {d: 1}
1027n/a dic[e] = 2
1028n/a self.assertEqual(len(dic), 1)
1029n/a self.assertEqual(dic[d], 2)
1030n/a self.assertEqual(dic[e], 2)
1031n/a
1032n/a def test_computations(self):
1033n/a a = self.theclass(2002, 1, 31)
1034n/a b = self.theclass(1956, 1, 31)
1035n/a c = self.theclass(2001,2,1)
1036n/a
1037n/a diff = a-b
1038n/a self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1039n/a self.assertEqual(diff.seconds, 0)
1040n/a self.assertEqual(diff.microseconds, 0)
1041n/a
1042n/a day = timedelta(1)
1043n/a week = timedelta(7)
1044n/a a = self.theclass(2002, 3, 2)
1045n/a self.assertEqual(a + day, self.theclass(2002, 3, 3))
1046n/a self.assertEqual(day + a, self.theclass(2002, 3, 3))
1047n/a self.assertEqual(a - day, self.theclass(2002, 3, 1))
1048n/a self.assertEqual(-day + a, self.theclass(2002, 3, 1))
1049n/a self.assertEqual(a + week, self.theclass(2002, 3, 9))
1050n/a self.assertEqual(a - week, self.theclass(2002, 2, 23))
1051n/a self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
1052n/a self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
1053n/a self.assertEqual((a + week) - a, week)
1054n/a self.assertEqual((a + day) - a, day)
1055n/a self.assertEqual((a - week) - a, -week)
1056n/a self.assertEqual((a - day) - a, -day)
1057n/a self.assertEqual(a - (a + week), -week)
1058n/a self.assertEqual(a - (a + day), -day)
1059n/a self.assertEqual(a - (a - week), week)
1060n/a self.assertEqual(a - (a - day), day)
1061n/a self.assertEqual(c - (c - day), day)
1062n/a
1063n/a # Add/sub ints or floats should be illegal
1064n/a for i in 1, 1.0:
1065n/a self.assertRaises(TypeError, lambda: a+i)
1066n/a self.assertRaises(TypeError, lambda: a-i)
1067n/a self.assertRaises(TypeError, lambda: i+a)
1068n/a self.assertRaises(TypeError, lambda: i-a)
1069n/a
1070n/a # delta - date is senseless.
1071n/a self.assertRaises(TypeError, lambda: day - a)
1072n/a # mixing date and (delta or date) via * or // is senseless
1073n/a self.assertRaises(TypeError, lambda: day * a)
1074n/a self.assertRaises(TypeError, lambda: a * day)
1075n/a self.assertRaises(TypeError, lambda: day // a)
1076n/a self.assertRaises(TypeError, lambda: a // day)
1077n/a self.assertRaises(TypeError, lambda: a * a)
1078n/a self.assertRaises(TypeError, lambda: a // a)
1079n/a # date + date is senseless
1080n/a self.assertRaises(TypeError, lambda: a + a)
1081n/a
1082n/a def test_overflow(self):
1083n/a tiny = self.theclass.resolution
1084n/a
1085n/a for delta in [tiny, timedelta(1), timedelta(2)]:
1086n/a dt = self.theclass.min + delta
1087n/a dt -= delta # no problem
1088n/a self.assertRaises(OverflowError, dt.__sub__, delta)
1089n/a self.assertRaises(OverflowError, dt.__add__, -delta)
1090n/a
1091n/a dt = self.theclass.max - delta
1092n/a dt += delta # no problem
1093n/a self.assertRaises(OverflowError, dt.__add__, delta)
1094n/a self.assertRaises(OverflowError, dt.__sub__, -delta)
1095n/a
1096n/a def test_fromtimestamp(self):
1097n/a import time
1098n/a
1099n/a # Try an arbitrary fixed value.
1100n/a year, month, day = 1999, 9, 19
1101n/a ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
1102n/a d = self.theclass.fromtimestamp(ts)
1103n/a self.assertEqual(d.year, year)
1104n/a self.assertEqual(d.month, month)
1105n/a self.assertEqual(d.day, day)
1106n/a
1107n/a def test_insane_fromtimestamp(self):
1108n/a # It's possible that some platform maps time_t to double,
1109n/a # and that this test will fail there. This test should
1110n/a # exempt such platforms (provided they return reasonable
1111n/a # results!).
1112n/a for insane in -1e200, 1e200:
1113n/a self.assertRaises(OverflowError, self.theclass.fromtimestamp,
1114n/a insane)
1115n/a
1116n/a def test_today(self):
1117n/a import time
1118n/a
1119n/a # We claim that today() is like fromtimestamp(time.time()), so
1120n/a # prove it.
1121n/a for dummy in range(3):
1122n/a today = self.theclass.today()
1123n/a ts = time.time()
1124n/a todayagain = self.theclass.fromtimestamp(ts)
1125n/a if today == todayagain:
1126n/a break
1127n/a # There are several legit reasons that could fail:
1128n/a # 1. It recently became midnight, between the today() and the
1129n/a # time() calls.
1130n/a # 2. The platform time() has such fine resolution that we'll
1131n/a # never get the same value twice.
1132n/a # 3. The platform time() has poor resolution, and we just
1133n/a # happened to call today() right before a resolution quantum
1134n/a # boundary.
1135n/a # 4. The system clock got fiddled between calls.
1136n/a # In any case, wait a little while and try again.
1137n/a time.sleep(0.1)
1138n/a
1139n/a # It worked or it didn't. If it didn't, assume it's reason #2, and
1140n/a # let the test pass if they're within half a second of each other.
1141n/a if today != todayagain:
1142n/a self.assertAlmostEqual(todayagain, today,
1143n/a delta=timedelta(seconds=0.5))
1144n/a
1145n/a def test_weekday(self):
1146n/a for i in range(7):
1147n/a # March 4, 2002 is a Monday
1148n/a self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
1149n/a self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
1150n/a # January 2, 1956 is a Monday
1151n/a self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
1152n/a self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
1153n/a
1154n/a def test_isocalendar(self):
1155n/a # Check examples from
1156n/a # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1157n/a for i in range(7):
1158n/a d = self.theclass(2003, 12, 22+i)
1159n/a self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1160n/a d = self.theclass(2003, 12, 29) + timedelta(i)
1161n/a self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1162n/a d = self.theclass(2004, 1, 5+i)
1163n/a self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1164n/a d = self.theclass(2009, 12, 21+i)
1165n/a self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1166n/a d = self.theclass(2009, 12, 28) + timedelta(i)
1167n/a self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1168n/a d = self.theclass(2010, 1, 4+i)
1169n/a self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1170n/a
1171n/a def test_iso_long_years(self):
1172n/a # Calculate long ISO years and compare to table from
1173n/a # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1174n/a ISO_LONG_YEARS_TABLE = """
1175n/a 4 32 60 88
1176n/a 9 37 65 93
1177n/a 15 43 71 99
1178n/a 20 48 76
1179n/a 26 54 82
1180n/a
1181n/a 105 133 161 189
1182n/a 111 139 167 195
1183n/a 116 144 172
1184n/a 122 150 178
1185n/a 128 156 184
1186n/a
1187n/a 201 229 257 285
1188n/a 207 235 263 291
1189n/a 212 240 268 296
1190n/a 218 246 274
1191n/a 224 252 280
1192n/a
1193n/a 303 331 359 387
1194n/a 308 336 364 392
1195n/a 314 342 370 398
1196n/a 320 348 376
1197n/a 325 353 381
1198n/a """
1199n/a iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
1200n/a L = []
1201n/a for i in range(400):
1202n/a d = self.theclass(2000+i, 12, 31)
1203n/a d1 = self.theclass(1600+i, 12, 31)
1204n/a self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1205n/a if d.isocalendar()[1] == 53:
1206n/a L.append(i)
1207n/a self.assertEqual(L, iso_long_years)
1208n/a
1209n/a def test_isoformat(self):
1210n/a t = self.theclass(2, 3, 2)
1211n/a self.assertEqual(t.isoformat(), "0002-03-02")
1212n/a
1213n/a def test_ctime(self):
1214n/a t = self.theclass(2002, 3, 2)
1215n/a self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1216n/a
1217n/a def test_strftime(self):
1218n/a t = self.theclass(2005, 3, 2)
1219n/a self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
1220n/a self.assertEqual(t.strftime(""), "") # SF bug #761337
1221n/a self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
1222n/a
1223n/a self.assertRaises(TypeError, t.strftime) # needs an arg
1224n/a self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1225n/a self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1226n/a
1227n/a # test that unicode input is allowed (issue 2782)
1228n/a self.assertEqual(t.strftime("%m"), "03")
1229n/a
1230n/a # A naive object replaces %z and %Z w/ empty strings.
1231n/a self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1232n/a
1233n/a #make sure that invalid format specifiers are handled correctly
1234n/a #self.assertRaises(ValueError, t.strftime, "%e")
1235n/a #self.assertRaises(ValueError, t.strftime, "%")
1236n/a #self.assertRaises(ValueError, t.strftime, "%#")
1237n/a
1238n/a #oh well, some systems just ignore those invalid ones.
1239n/a #at least, exercise them to make sure that no crashes
1240n/a #are generated
1241n/a for f in ["%e", "%", "%#"]:
1242n/a try:
1243n/a t.strftime(f)
1244n/a except ValueError:
1245n/a pass
1246n/a
1247n/a #check that this standard extension works
1248n/a t.strftime("%f")
1249n/a
1250n/a def test_format(self):
1251n/a dt = self.theclass(2007, 9, 10)
1252n/a self.assertEqual(dt.__format__(''), str(dt))
1253n/a
1254n/a with self.assertRaisesRegex(TypeError, 'must be str, not int'):
1255n/a dt.__format__(123)
1256n/a
1257n/a # check that a derived class's __str__() gets called
1258n/a class A(self.theclass):
1259n/a def __str__(self):
1260n/a return 'A'
1261n/a a = A(2007, 9, 10)
1262n/a self.assertEqual(a.__format__(''), 'A')
1263n/a
1264n/a # check that a derived class's strftime gets called
1265n/a class B(self.theclass):
1266n/a def strftime(self, format_spec):
1267n/a return 'B'
1268n/a b = B(2007, 9, 10)
1269n/a self.assertEqual(b.__format__(''), str(dt))
1270n/a
1271n/a for fmt in ["m:%m d:%d y:%y",
1272n/a "m:%m d:%d y:%y H:%H M:%M S:%S",
1273n/a "%z %Z",
1274n/a ]:
1275n/a self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1276n/a self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1277n/a self.assertEqual(b.__format__(fmt), 'B')
1278n/a
1279n/a def test_resolution_info(self):
1280n/a # XXX: Should min and max respect subclassing?
1281n/a if issubclass(self.theclass, datetime):
1282n/a expected_class = datetime
1283n/a else:
1284n/a expected_class = date
1285n/a self.assertIsInstance(self.theclass.min, expected_class)
1286n/a self.assertIsInstance(self.theclass.max, expected_class)
1287n/a self.assertIsInstance(self.theclass.resolution, timedelta)
1288n/a self.assertTrue(self.theclass.max > self.theclass.min)
1289n/a
1290n/a def test_extreme_timedelta(self):
1291n/a big = self.theclass.max - self.theclass.min
1292n/a # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1293n/a n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1294n/a # n == 315537897599999999 ~= 2**58.13
1295n/a justasbig = timedelta(0, 0, n)
1296n/a self.assertEqual(big, justasbig)
1297n/a self.assertEqual(self.theclass.min + big, self.theclass.max)
1298n/a self.assertEqual(self.theclass.max - big, self.theclass.min)
1299n/a
1300n/a def test_timetuple(self):
1301n/a for i in range(7):
1302n/a # January 2, 1956 is a Monday (0)
1303n/a d = self.theclass(1956, 1, 2+i)
1304n/a t = d.timetuple()
1305n/a self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1306n/a # February 1, 1956 is a Wednesday (2)
1307n/a d = self.theclass(1956, 2, 1+i)
1308n/a t = d.timetuple()
1309n/a self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1310n/a # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1311n/a # of the year.
1312n/a d = self.theclass(1956, 3, 1+i)
1313n/a t = d.timetuple()
1314n/a self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1315n/a self.assertEqual(t.tm_year, 1956)
1316n/a self.assertEqual(t.tm_mon, 3)
1317n/a self.assertEqual(t.tm_mday, 1+i)
1318n/a self.assertEqual(t.tm_hour, 0)
1319n/a self.assertEqual(t.tm_min, 0)
1320n/a self.assertEqual(t.tm_sec, 0)
1321n/a self.assertEqual(t.tm_wday, (3+i)%7)
1322n/a self.assertEqual(t.tm_yday, 61+i)
1323n/a self.assertEqual(t.tm_isdst, -1)
1324n/a
1325n/a def test_pickling(self):
1326n/a args = 6, 7, 23
1327n/a orig = self.theclass(*args)
1328n/a for pickler, unpickler, proto in pickle_choices:
1329n/a green = pickler.dumps(orig, proto)
1330n/a derived = unpickler.loads(green)
1331n/a self.assertEqual(orig, derived)
1332n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
1333n/a
1334n/a def test_compare(self):
1335n/a t1 = self.theclass(2, 3, 4)
1336n/a t2 = self.theclass(2, 3, 4)
1337n/a self.assertEqual(t1, t2)
1338n/a self.assertTrue(t1 <= t2)
1339n/a self.assertTrue(t1 >= t2)
1340n/a self.assertFalse(t1 != t2)
1341n/a self.assertFalse(t1 < t2)
1342n/a self.assertFalse(t1 > t2)
1343n/a
1344n/a for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1345n/a t2 = self.theclass(*args) # this is larger than t1
1346n/a self.assertTrue(t1 < t2)
1347n/a self.assertTrue(t2 > t1)
1348n/a self.assertTrue(t1 <= t2)
1349n/a self.assertTrue(t2 >= t1)
1350n/a self.assertTrue(t1 != t2)
1351n/a self.assertTrue(t2 != t1)
1352n/a self.assertFalse(t1 == t2)
1353n/a self.assertFalse(t2 == t1)
1354n/a self.assertFalse(t1 > t2)
1355n/a self.assertFalse(t2 < t1)
1356n/a self.assertFalse(t1 >= t2)
1357n/a self.assertFalse(t2 <= t1)
1358n/a
1359n/a for badarg in OTHERSTUFF:
1360n/a self.assertEqual(t1 == badarg, False)
1361n/a self.assertEqual(t1 != badarg, True)
1362n/a self.assertEqual(badarg == t1, False)
1363n/a self.assertEqual(badarg != t1, True)
1364n/a
1365n/a self.assertRaises(TypeError, lambda: t1 < badarg)
1366n/a self.assertRaises(TypeError, lambda: t1 > badarg)
1367n/a self.assertRaises(TypeError, lambda: t1 >= badarg)
1368n/a self.assertRaises(TypeError, lambda: badarg <= t1)
1369n/a self.assertRaises(TypeError, lambda: badarg < t1)
1370n/a self.assertRaises(TypeError, lambda: badarg > t1)
1371n/a self.assertRaises(TypeError, lambda: badarg >= t1)
1372n/a
1373n/a def test_mixed_compare(self):
1374n/a our = self.theclass(2000, 4, 5)
1375n/a
1376n/a # Our class can be compared for equality to other classes
1377n/a self.assertEqual(our == 1, False)
1378n/a self.assertEqual(1 == our, False)
1379n/a self.assertEqual(our != 1, True)
1380n/a self.assertEqual(1 != our, True)
1381n/a
1382n/a # But the ordering is undefined
1383n/a self.assertRaises(TypeError, lambda: our < 1)
1384n/a self.assertRaises(TypeError, lambda: 1 < our)
1385n/a
1386n/a # Repeat those tests with a different class
1387n/a
1388n/a class SomeClass:
1389n/a pass
1390n/a
1391n/a their = SomeClass()
1392n/a self.assertEqual(our == their, False)
1393n/a self.assertEqual(their == our, False)
1394n/a self.assertEqual(our != their, True)
1395n/a self.assertEqual(their != our, True)
1396n/a self.assertRaises(TypeError, lambda: our < their)
1397n/a self.assertRaises(TypeError, lambda: their < our)
1398n/a
1399n/a # However, if the other class explicitly defines ordering
1400n/a # relative to our class, it is allowed to do so
1401n/a
1402n/a class LargerThanAnything:
1403n/a def __lt__(self, other):
1404n/a return False
1405n/a def __le__(self, other):
1406n/a return isinstance(other, LargerThanAnything)
1407n/a def __eq__(self, other):
1408n/a return isinstance(other, LargerThanAnything)
1409n/a def __gt__(self, other):
1410n/a return not isinstance(other, LargerThanAnything)
1411n/a def __ge__(self, other):
1412n/a return True
1413n/a
1414n/a their = LargerThanAnything()
1415n/a self.assertEqual(our == their, False)
1416n/a self.assertEqual(their == our, False)
1417n/a self.assertEqual(our != their, True)
1418n/a self.assertEqual(their != our, True)
1419n/a self.assertEqual(our < their, True)
1420n/a self.assertEqual(their < our, False)
1421n/a
1422n/a def test_bool(self):
1423n/a # All dates are considered true.
1424n/a self.assertTrue(self.theclass.min)
1425n/a self.assertTrue(self.theclass.max)
1426n/a
1427n/a def test_strftime_y2k(self):
1428n/a for y in (1, 49, 70, 99, 100, 999, 1000, 1970):
1429n/a d = self.theclass(y, 1, 1)
1430n/a # Issue 13305: For years < 1000, the value is not always
1431n/a # padded to 4 digits across platforms. The C standard
1432n/a # assumes year >= 1900, so it does not specify the number
1433n/a # of digits.
1434n/a if d.strftime("%Y") != '%04d' % y:
1435n/a # Year 42 returns '42', not padded
1436n/a self.assertEqual(d.strftime("%Y"), '%d' % y)
1437n/a # '0042' is obtained anyway
1438n/a self.assertEqual(d.strftime("%4Y"), '%04d' % y)
1439n/a
1440n/a def test_replace(self):
1441n/a cls = self.theclass
1442n/a args = [1, 2, 3]
1443n/a base = cls(*args)
1444n/a self.assertEqual(base, base.replace())
1445n/a
1446n/a i = 0
1447n/a for name, newval in (("year", 2),
1448n/a ("month", 3),
1449n/a ("day", 4)):
1450n/a newargs = args[:]
1451n/a newargs[i] = newval
1452n/a expected = cls(*newargs)
1453n/a got = base.replace(**{name: newval})
1454n/a self.assertEqual(expected, got)
1455n/a i += 1
1456n/a
1457n/a # Out of bounds.
1458n/a base = cls(2000, 2, 29)
1459n/a self.assertRaises(ValueError, base.replace, year=2001)
1460n/a
1461n/a def test_subclass_date(self):
1462n/a
1463n/a class C(self.theclass):
1464n/a theAnswer = 42
1465n/a
1466n/a def __new__(cls, *args, **kws):
1467n/a temp = kws.copy()
1468n/a extra = temp.pop('extra')
1469n/a result = self.theclass.__new__(cls, *args, **temp)
1470n/a result.extra = extra
1471n/a return result
1472n/a
1473n/a def newmeth(self, start):
1474n/a return start + self.year + self.month
1475n/a
1476n/a args = 2003, 4, 14
1477n/a
1478n/a dt1 = self.theclass(*args)
1479n/a dt2 = C(*args, **{'extra': 7})
1480n/a
1481n/a self.assertEqual(dt2.__class__, C)
1482n/a self.assertEqual(dt2.theAnswer, 42)
1483n/a self.assertEqual(dt2.extra, 7)
1484n/a self.assertEqual(dt1.toordinal(), dt2.toordinal())
1485n/a self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1486n/a
1487n/a def test_pickling_subclass_date(self):
1488n/a
1489n/a args = 6, 7, 23
1490n/a orig = SubclassDate(*args)
1491n/a for pickler, unpickler, proto in pickle_choices:
1492n/a green = pickler.dumps(orig, proto)
1493n/a derived = unpickler.loads(green)
1494n/a self.assertEqual(orig, derived)
1495n/a
1496n/a def test_backdoor_resistance(self):
1497n/a # For fast unpickling, the constructor accepts a pickle byte string.
1498n/a # This is a low-overhead backdoor. A user can (by intent or
1499n/a # mistake) pass a string directly, which (if it's the right length)
1500n/a # will get treated like a pickle, and bypass the normal sanity
1501n/a # checks in the constructor. This can create insane objects.
1502n/a # The constructor doesn't want to burn the time to validate all
1503n/a # fields, but does check the month field. This stops, e.g.,
1504n/a # datetime.datetime('1995-03-25') from yielding an insane object.
1505n/a base = b'1995-03-25'
1506n/a if not issubclass(self.theclass, datetime):
1507n/a base = base[:4]
1508n/a for month_byte in b'9', b'\0', b'\r', b'\xff':
1509n/a self.assertRaises(TypeError, self.theclass,
1510n/a base[:2] + month_byte + base[3:])
1511n/a if issubclass(self.theclass, datetime):
1512n/a # Good bytes, but bad tzinfo:
1513n/a with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
1514n/a self.theclass(bytes([1] * len(base)), 'EST')
1515n/a
1516n/a for ord_byte in range(1, 13):
1517n/a # This shouldn't blow up because of the month byte alone. If
1518n/a # the implementation changes to do more-careful checking, it may
1519n/a # blow up because other fields are insane.
1520n/a self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
1521n/a
1522n/a#############################################################################
1523n/a# datetime tests
1524n/a
1525n/aclass SubclassDatetime(datetime):
1526n/a sub_var = 1
1527n/a
1528n/aclass TestDateTime(TestDate):
1529n/a
1530n/a theclass = datetime
1531n/a
1532n/a def test_basic_attributes(self):
1533n/a dt = self.theclass(2002, 3, 1, 12, 0)
1534n/a self.assertEqual(dt.year, 2002)
1535n/a self.assertEqual(dt.month, 3)
1536n/a self.assertEqual(dt.day, 1)
1537n/a self.assertEqual(dt.hour, 12)
1538n/a self.assertEqual(dt.minute, 0)
1539n/a self.assertEqual(dt.second, 0)
1540n/a self.assertEqual(dt.microsecond, 0)
1541n/a
1542n/a def test_basic_attributes_nonzero(self):
1543n/a # Make sure all attributes are non-zero so bugs in
1544n/a # bit-shifting access show up.
1545n/a dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1546n/a self.assertEqual(dt.year, 2002)
1547n/a self.assertEqual(dt.month, 3)
1548n/a self.assertEqual(dt.day, 1)
1549n/a self.assertEqual(dt.hour, 12)
1550n/a self.assertEqual(dt.minute, 59)
1551n/a self.assertEqual(dt.second, 59)
1552n/a self.assertEqual(dt.microsecond, 8000)
1553n/a
1554n/a def test_roundtrip(self):
1555n/a for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1556n/a self.theclass.now()):
1557n/a # Verify dt -> string -> datetime identity.
1558n/a s = repr(dt)
1559n/a self.assertTrue(s.startswith('datetime.'))
1560n/a s = s[9:]
1561n/a dt2 = eval(s)
1562n/a self.assertEqual(dt, dt2)
1563n/a
1564n/a # Verify identity via reconstructing from pieces.
1565n/a dt2 = self.theclass(dt.year, dt.month, dt.day,
1566n/a dt.hour, dt.minute, dt.second,
1567n/a dt.microsecond)
1568n/a self.assertEqual(dt, dt2)
1569n/a
1570n/a def test_isoformat(self):
1571n/a t = self.theclass(1, 2, 3, 4, 5, 1, 123)
1572n/a self.assertEqual(t.isoformat(), "0001-02-03T04:05:01.000123")
1573n/a self.assertEqual(t.isoformat('T'), "0001-02-03T04:05:01.000123")
1574n/a self.assertEqual(t.isoformat(' '), "0001-02-03 04:05:01.000123")
1575n/a self.assertEqual(t.isoformat('\x00'), "0001-02-03\x0004:05:01.000123")
1576n/a self.assertEqual(t.isoformat(timespec='hours'), "0001-02-03T04")
1577n/a self.assertEqual(t.isoformat(timespec='minutes'), "0001-02-03T04:05")
1578n/a self.assertEqual(t.isoformat(timespec='seconds'), "0001-02-03T04:05:01")
1579n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.000")
1580n/a self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05:01.000123")
1581n/a self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01.000123")
1582n/a self.assertEqual(t.isoformat(sep=' ', timespec='minutes'), "0001-02-03 04:05")
1583n/a self.assertRaises(ValueError, t.isoformat, timespec='foo')
1584n/a # str is ISO format with the separator forced to a blank.
1585n/a self.assertEqual(str(t), "0001-02-03 04:05:01.000123")
1586n/a
1587n/a t = self.theclass(1, 2, 3, 4, 5, 1, 999500, tzinfo=timezone.utc)
1588n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999+00:00")
1589n/a
1590n/a t = self.theclass(1, 2, 3, 4, 5, 1, 999500)
1591n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999")
1592n/a
1593n/a t = self.theclass(1, 2, 3, 4, 5, 1)
1594n/a self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01")
1595n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.000")
1596n/a self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05:01.000000")
1597n/a
1598n/a t = self.theclass(2, 3, 2)
1599n/a self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1600n/a self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1601n/a self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1602n/a # str is ISO format with the separator forced to a blank.
1603n/a self.assertEqual(str(t), "0002-03-02 00:00:00")
1604n/a # ISO format with timezone
1605n/a tz = FixedOffset(timedelta(seconds=16), 'XXX')
1606n/a t = self.theclass(2, 3, 2, tzinfo=tz)
1607n/a self.assertEqual(t.isoformat(), "0002-03-02T00:00:00+00:00:16")
1608n/a
1609n/a def test_format(self):
1610n/a dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1611n/a self.assertEqual(dt.__format__(''), str(dt))
1612n/a
1613n/a with self.assertRaisesRegex(TypeError, 'must be str, not int'):
1614n/a dt.__format__(123)
1615n/a
1616n/a # check that a derived class's __str__() gets called
1617n/a class A(self.theclass):
1618n/a def __str__(self):
1619n/a return 'A'
1620n/a a = A(2007, 9, 10, 4, 5, 1, 123)
1621n/a self.assertEqual(a.__format__(''), 'A')
1622n/a
1623n/a # check that a derived class's strftime gets called
1624n/a class B(self.theclass):
1625n/a def strftime(self, format_spec):
1626n/a return 'B'
1627n/a b = B(2007, 9, 10, 4, 5, 1, 123)
1628n/a self.assertEqual(b.__format__(''), str(dt))
1629n/a
1630n/a for fmt in ["m:%m d:%d y:%y",
1631n/a "m:%m d:%d y:%y H:%H M:%M S:%S",
1632n/a "%z %Z",
1633n/a ]:
1634n/a self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1635n/a self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1636n/a self.assertEqual(b.__format__(fmt), 'B')
1637n/a
1638n/a def test_more_ctime(self):
1639n/a # Test fields that TestDate doesn't touch.
1640n/a import time
1641n/a
1642n/a t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1643n/a self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1644n/a # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1645n/a # out. The difference is that t.ctime() produces " 2" for the day,
1646n/a # but platform ctime() produces "02" for the day. According to
1647n/a # C99, t.ctime() is correct here.
1648n/a # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1649n/a
1650n/a # So test a case where that difference doesn't matter.
1651n/a t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1652n/a self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1653n/a
1654n/a def test_tz_independent_comparing(self):
1655n/a dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1656n/a dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1657n/a dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1658n/a self.assertEqual(dt1, dt3)
1659n/a self.assertTrue(dt2 > dt3)
1660n/a
1661n/a # Make sure comparison doesn't forget microseconds, and isn't done
1662n/a # via comparing a float timestamp (an IEEE double doesn't have enough
1663n/a # precision to span microsecond resolution across years 1 thru 9999,
1664n/a # so comparing via timestamp necessarily calls some distinct values
1665n/a # equal).
1666n/a dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1667n/a us = timedelta(microseconds=1)
1668n/a dt2 = dt1 + us
1669n/a self.assertEqual(dt2 - dt1, us)
1670n/a self.assertTrue(dt1 < dt2)
1671n/a
1672n/a def test_strftime_with_bad_tzname_replace(self):
1673n/a # verify ok if tzinfo.tzname().replace() returns a non-string
1674n/a class MyTzInfo(FixedOffset):
1675n/a def tzname(self, dt):
1676n/a class MyStr(str):
1677n/a def replace(self, *args):
1678n/a return None
1679n/a return MyStr('name')
1680n/a t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1681n/a self.assertRaises(TypeError, t.strftime, '%Z')
1682n/a
1683n/a def test_bad_constructor_arguments(self):
1684n/a # bad years
1685n/a self.theclass(MINYEAR, 1, 1) # no exception
1686n/a self.theclass(MAXYEAR, 1, 1) # no exception
1687n/a self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1688n/a self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1689n/a # bad months
1690n/a self.theclass(2000, 1, 1) # no exception
1691n/a self.theclass(2000, 12, 1) # no exception
1692n/a self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1693n/a self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1694n/a # bad days
1695n/a self.theclass(2000, 2, 29) # no exception
1696n/a self.theclass(2004, 2, 29) # no exception
1697n/a self.theclass(2400, 2, 29) # no exception
1698n/a self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1699n/a self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1700n/a self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1701n/a self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1702n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1703n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1704n/a # bad hours
1705n/a self.theclass(2000, 1, 31, 0) # no exception
1706n/a self.theclass(2000, 1, 31, 23) # no exception
1707n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1708n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1709n/a # bad minutes
1710n/a self.theclass(2000, 1, 31, 23, 0) # no exception
1711n/a self.theclass(2000, 1, 31, 23, 59) # no exception
1712n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1713n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1714n/a # bad seconds
1715n/a self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1716n/a self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1717n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1718n/a self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1719n/a # bad microseconds
1720n/a self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1721n/a self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1722n/a self.assertRaises(ValueError, self.theclass,
1723n/a 2000, 1, 31, 23, 59, 59, -1)
1724n/a self.assertRaises(ValueError, self.theclass,
1725n/a 2000, 1, 31, 23, 59, 59,
1726n/a 1000000)
1727n/a # bad fold
1728n/a self.assertRaises(ValueError, self.theclass,
1729n/a 2000, 1, 31, fold=-1)
1730n/a self.assertRaises(ValueError, self.theclass,
1731n/a 2000, 1, 31, fold=2)
1732n/a # Positional fold:
1733n/a self.assertRaises(TypeError, self.theclass,
1734n/a 2000, 1, 31, 23, 59, 59, 0, None, 1)
1735n/a
1736n/a def test_hash_equality(self):
1737n/a d = self.theclass(2000, 12, 31, 23, 30, 17)
1738n/a e = self.theclass(2000, 12, 31, 23, 30, 17)
1739n/a self.assertEqual(d, e)
1740n/a self.assertEqual(hash(d), hash(e))
1741n/a
1742n/a dic = {d: 1}
1743n/a dic[e] = 2
1744n/a self.assertEqual(len(dic), 1)
1745n/a self.assertEqual(dic[d], 2)
1746n/a self.assertEqual(dic[e], 2)
1747n/a
1748n/a d = self.theclass(2001, 1, 1, 0, 5, 17)
1749n/a e = self.theclass(2001, 1, 1, 0, 5, 17)
1750n/a self.assertEqual(d, e)
1751n/a self.assertEqual(hash(d), hash(e))
1752n/a
1753n/a dic = {d: 1}
1754n/a dic[e] = 2
1755n/a self.assertEqual(len(dic), 1)
1756n/a self.assertEqual(dic[d], 2)
1757n/a self.assertEqual(dic[e], 2)
1758n/a
1759n/a def test_computations(self):
1760n/a a = self.theclass(2002, 1, 31)
1761n/a b = self.theclass(1956, 1, 31)
1762n/a diff = a-b
1763n/a self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1764n/a self.assertEqual(diff.seconds, 0)
1765n/a self.assertEqual(diff.microseconds, 0)
1766n/a a = self.theclass(2002, 3, 2, 17, 6)
1767n/a millisec = timedelta(0, 0, 1000)
1768n/a hour = timedelta(0, 3600)
1769n/a day = timedelta(1)
1770n/a week = timedelta(7)
1771n/a self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1772n/a self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1773n/a self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1774n/a self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1775n/a self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1776n/a self.assertEqual(a - hour, a + -hour)
1777n/a self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1778n/a self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1779n/a self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1780n/a self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1781n/a self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1782n/a self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1783n/a self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1784n/a self.assertEqual((a + week) - a, week)
1785n/a self.assertEqual((a + day) - a, day)
1786n/a self.assertEqual((a + hour) - a, hour)
1787n/a self.assertEqual((a + millisec) - a, millisec)
1788n/a self.assertEqual((a - week) - a, -week)
1789n/a self.assertEqual((a - day) - a, -day)
1790n/a self.assertEqual((a - hour) - a, -hour)
1791n/a self.assertEqual((a - millisec) - a, -millisec)
1792n/a self.assertEqual(a - (a + week), -week)
1793n/a self.assertEqual(a - (a + day), -day)
1794n/a self.assertEqual(a - (a + hour), -hour)
1795n/a self.assertEqual(a - (a + millisec), -millisec)
1796n/a self.assertEqual(a - (a - week), week)
1797n/a self.assertEqual(a - (a - day), day)
1798n/a self.assertEqual(a - (a - hour), hour)
1799n/a self.assertEqual(a - (a - millisec), millisec)
1800n/a self.assertEqual(a + (week + day + hour + millisec),
1801n/a self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1802n/a self.assertEqual(a + (week + day + hour + millisec),
1803n/a (((a + week) + day) + hour) + millisec)
1804n/a self.assertEqual(a - (week + day + hour + millisec),
1805n/a self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1806n/a self.assertEqual(a - (week + day + hour + millisec),
1807n/a (((a - week) - day) - hour) - millisec)
1808n/a # Add/sub ints or floats should be illegal
1809n/a for i in 1, 1.0:
1810n/a self.assertRaises(TypeError, lambda: a+i)
1811n/a self.assertRaises(TypeError, lambda: a-i)
1812n/a self.assertRaises(TypeError, lambda: i+a)
1813n/a self.assertRaises(TypeError, lambda: i-a)
1814n/a
1815n/a # delta - datetime is senseless.
1816n/a self.assertRaises(TypeError, lambda: day - a)
1817n/a # mixing datetime and (delta or datetime) via * or // is senseless
1818n/a self.assertRaises(TypeError, lambda: day * a)
1819n/a self.assertRaises(TypeError, lambda: a * day)
1820n/a self.assertRaises(TypeError, lambda: day // a)
1821n/a self.assertRaises(TypeError, lambda: a // day)
1822n/a self.assertRaises(TypeError, lambda: a * a)
1823n/a self.assertRaises(TypeError, lambda: a // a)
1824n/a # datetime + datetime is senseless
1825n/a self.assertRaises(TypeError, lambda: a + a)
1826n/a
1827n/a def test_pickling(self):
1828n/a args = 6, 7, 23, 20, 59, 1, 64**2
1829n/a orig = self.theclass(*args)
1830n/a for pickler, unpickler, proto in pickle_choices:
1831n/a green = pickler.dumps(orig, proto)
1832n/a derived = unpickler.loads(green)
1833n/a self.assertEqual(orig, derived)
1834n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
1835n/a
1836n/a def test_more_pickling(self):
1837n/a a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1838n/a for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1839n/a s = pickle.dumps(a, proto)
1840n/a b = pickle.loads(s)
1841n/a self.assertEqual(b.year, 2003)
1842n/a self.assertEqual(b.month, 2)
1843n/a self.assertEqual(b.day, 7)
1844n/a
1845n/a def test_pickling_subclass_datetime(self):
1846n/a args = 6, 7, 23, 20, 59, 1, 64**2
1847n/a orig = SubclassDatetime(*args)
1848n/a for pickler, unpickler, proto in pickle_choices:
1849n/a green = pickler.dumps(orig, proto)
1850n/a derived = unpickler.loads(green)
1851n/a self.assertEqual(orig, derived)
1852n/a
1853n/a def test_more_compare(self):
1854n/a # The test_compare() inherited from TestDate covers the error cases.
1855n/a # We just want to test lexicographic ordering on the members datetime
1856n/a # has that date lacks.
1857n/a args = [2000, 11, 29, 20, 58, 16, 999998]
1858n/a t1 = self.theclass(*args)
1859n/a t2 = self.theclass(*args)
1860n/a self.assertEqual(t1, t2)
1861n/a self.assertTrue(t1 <= t2)
1862n/a self.assertTrue(t1 >= t2)
1863n/a self.assertFalse(t1 != t2)
1864n/a self.assertFalse(t1 < t2)
1865n/a self.assertFalse(t1 > t2)
1866n/a
1867n/a for i in range(len(args)):
1868n/a newargs = args[:]
1869n/a newargs[i] = args[i] + 1
1870n/a t2 = self.theclass(*newargs) # this is larger than t1
1871n/a self.assertTrue(t1 < t2)
1872n/a self.assertTrue(t2 > t1)
1873n/a self.assertTrue(t1 <= t2)
1874n/a self.assertTrue(t2 >= t1)
1875n/a self.assertTrue(t1 != t2)
1876n/a self.assertTrue(t2 != t1)
1877n/a self.assertFalse(t1 == t2)
1878n/a self.assertFalse(t2 == t1)
1879n/a self.assertFalse(t1 > t2)
1880n/a self.assertFalse(t2 < t1)
1881n/a self.assertFalse(t1 >= t2)
1882n/a self.assertFalse(t2 <= t1)
1883n/a
1884n/a
1885n/a # A helper for timestamp constructor tests.
1886n/a def verify_field_equality(self, expected, got):
1887n/a self.assertEqual(expected.tm_year, got.year)
1888n/a self.assertEqual(expected.tm_mon, got.month)
1889n/a self.assertEqual(expected.tm_mday, got.day)
1890n/a self.assertEqual(expected.tm_hour, got.hour)
1891n/a self.assertEqual(expected.tm_min, got.minute)
1892n/a self.assertEqual(expected.tm_sec, got.second)
1893n/a
1894n/a def test_fromtimestamp(self):
1895n/a import time
1896n/a
1897n/a ts = time.time()
1898n/a expected = time.localtime(ts)
1899n/a got = self.theclass.fromtimestamp(ts)
1900n/a self.verify_field_equality(expected, got)
1901n/a
1902n/a def test_utcfromtimestamp(self):
1903n/a import time
1904n/a
1905n/a ts = time.time()
1906n/a expected = time.gmtime(ts)
1907n/a got = self.theclass.utcfromtimestamp(ts)
1908n/a self.verify_field_equality(expected, got)
1909n/a
1910n/a # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
1911n/a # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
1912n/a @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
1913n/a def test_timestamp_naive(self):
1914n/a t = self.theclass(1970, 1, 1)
1915n/a self.assertEqual(t.timestamp(), 18000.0)
1916n/a t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
1917n/a self.assertEqual(t.timestamp(),
1918n/a 18000.0 + 3600 + 2*60 + 3 + 4*1e-6)
1919n/a # Missing hour
1920n/a t0 = self.theclass(2012, 3, 11, 2, 30)
1921n/a t1 = t0.replace(fold=1)
1922n/a self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()),
1923n/a t0 - timedelta(hours=1))
1924n/a self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()),
1925n/a t1 + timedelta(hours=1))
1926n/a # Ambiguous hour defaults to DST
1927n/a t = self.theclass(2012, 11, 4, 1, 30)
1928n/a self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)
1929n/a
1930n/a # Timestamp may raise an overflow error on some platforms
1931n/a # XXX: Do we care to support the first and last year?
1932n/a for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]:
1933n/a try:
1934n/a s = t.timestamp()
1935n/a except OverflowError:
1936n/a pass
1937n/a else:
1938n/a self.assertEqual(self.theclass.fromtimestamp(s), t)
1939n/a
1940n/a def test_timestamp_aware(self):
1941n/a t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)
1942n/a self.assertEqual(t.timestamp(), 0.0)
1943n/a t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)
1944n/a self.assertEqual(t.timestamp(),
1945n/a 3600 + 2*60 + 3 + 4*1e-6)
1946n/a t = self.theclass(1970, 1, 1, 1, 2, 3, 4,
1947n/a tzinfo=timezone(timedelta(hours=-5), 'EST'))
1948n/a self.assertEqual(t.timestamp(),
1949n/a 18000 + 3600 + 2*60 + 3 + 4*1e-6)
1950n/a
1951n/a @support.run_with_tz('MSK-03') # Something east of Greenwich
1952n/a def test_microsecond_rounding(self):
1953n/a for fts in [self.theclass.fromtimestamp,
1954n/a self.theclass.utcfromtimestamp]:
1955n/a zero = fts(0)
1956n/a self.assertEqual(zero.second, 0)
1957n/a self.assertEqual(zero.microsecond, 0)
1958n/a one = fts(1e-6)
1959n/a try:
1960n/a minus_one = fts(-1e-6)
1961n/a except OSError:
1962n/a # localtime(-1) and gmtime(-1) is not supported on Windows
1963n/a pass
1964n/a else:
1965n/a self.assertEqual(minus_one.second, 59)
1966n/a self.assertEqual(minus_one.microsecond, 999999)
1967n/a
1968n/a t = fts(-1e-8)
1969n/a self.assertEqual(t, zero)
1970n/a t = fts(-9e-7)
1971n/a self.assertEqual(t, minus_one)
1972n/a t = fts(-1e-7)
1973n/a self.assertEqual(t, zero)
1974n/a t = fts(-1/2**7)
1975n/a self.assertEqual(t.second, 59)
1976n/a self.assertEqual(t.microsecond, 992188)
1977n/a
1978n/a t = fts(1e-7)
1979n/a self.assertEqual(t, zero)
1980n/a t = fts(9e-7)
1981n/a self.assertEqual(t, one)
1982n/a t = fts(0.99999949)
1983n/a self.assertEqual(t.second, 0)
1984n/a self.assertEqual(t.microsecond, 999999)
1985n/a t = fts(0.9999999)
1986n/a self.assertEqual(t.second, 1)
1987n/a self.assertEqual(t.microsecond, 0)
1988n/a t = fts(1/2**7)
1989n/a self.assertEqual(t.second, 0)
1990n/a self.assertEqual(t.microsecond, 7812)
1991n/a
1992n/a def test_timestamp_limits(self):
1993n/a # minimum timestamp
1994n/a min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
1995n/a min_ts = min_dt.timestamp()
1996n/a try:
1997n/a # date 0001-01-01 00:00:00+00:00: timestamp=-62135596800
1998n/a self.assertEqual(self.theclass.fromtimestamp(min_ts, tz=timezone.utc),
1999n/a min_dt)
2000n/a except (OverflowError, OSError) as exc:
2001n/a # the date 0001-01-01 doesn't fit into 32-bit time_t,
2002n/a # or platform doesn't support such very old date
2003n/a self.skipTest(str(exc))
2004n/a
2005n/a # maximum timestamp: set seconds to zero to avoid rounding issues
2006n/a max_dt = self.theclass.max.replace(tzinfo=timezone.utc,
2007n/a second=0, microsecond=0)
2008n/a max_ts = max_dt.timestamp()
2009n/a # date 9999-12-31 23:59:00+00:00: timestamp 253402300740
2010n/a self.assertEqual(self.theclass.fromtimestamp(max_ts, tz=timezone.utc),
2011n/a max_dt)
2012n/a
2013n/a # number of seconds greater than 1 year: make sure that the new date
2014n/a # is not valid in datetime.datetime limits
2015n/a delta = 3600 * 24 * 400
2016n/a
2017n/a # too small
2018n/a ts = min_ts - delta
2019n/a # converting a Python int to C time_t can raise a OverflowError,
2020n/a # especially on 32-bit platforms.
2021n/a with self.assertRaises((ValueError, OverflowError)):
2022n/a self.theclass.fromtimestamp(ts)
2023n/a with self.assertRaises((ValueError, OverflowError)):
2024n/a self.theclass.utcfromtimestamp(ts)
2025n/a
2026n/a # too big
2027n/a ts = max_dt.timestamp() + delta
2028n/a with self.assertRaises((ValueError, OverflowError)):
2029n/a self.theclass.fromtimestamp(ts)
2030n/a with self.assertRaises((ValueError, OverflowError)):
2031n/a self.theclass.utcfromtimestamp(ts)
2032n/a
2033n/a def test_insane_fromtimestamp(self):
2034n/a # It's possible that some platform maps time_t to double,
2035n/a # and that this test will fail there. This test should
2036n/a # exempt such platforms (provided they return reasonable
2037n/a # results!).
2038n/a for insane in -1e200, 1e200:
2039n/a self.assertRaises(OverflowError, self.theclass.fromtimestamp,
2040n/a insane)
2041n/a
2042n/a def test_insane_utcfromtimestamp(self):
2043n/a # It's possible that some platform maps time_t to double,
2044n/a # and that this test will fail there. This test should
2045n/a # exempt such platforms (provided they return reasonable
2046n/a # results!).
2047n/a for insane in -1e200, 1e200:
2048n/a self.assertRaises(OverflowError, self.theclass.utcfromtimestamp,
2049n/a insane)
2050n/a
2051n/a @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
2052n/a def test_negative_float_fromtimestamp(self):
2053n/a # The result is tz-dependent; at least test that this doesn't
2054n/a # fail (like it did before bug 1646728 was fixed).
2055n/a self.theclass.fromtimestamp(-1.05)
2056n/a
2057n/a @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
2058n/a def test_negative_float_utcfromtimestamp(self):
2059n/a d = self.theclass.utcfromtimestamp(-1.05)
2060n/a self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
2061n/a
2062n/a def test_utcnow(self):
2063n/a import time
2064n/a
2065n/a # Call it a success if utcnow() and utcfromtimestamp() are within
2066n/a # a second of each other.
2067n/a tolerance = timedelta(seconds=1)
2068n/a for dummy in range(3):
2069n/a from_now = self.theclass.utcnow()
2070n/a from_timestamp = self.theclass.utcfromtimestamp(time.time())
2071n/a if abs(from_timestamp - from_now) <= tolerance:
2072n/a break
2073n/a # Else try again a few times.
2074n/a self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
2075n/a
2076n/a def test_strptime(self):
2077n/a string = '2004-12-01 13:02:47.197'
2078n/a format = '%Y-%m-%d %H:%M:%S.%f'
2079n/a expected = _strptime._strptime_datetime(self.theclass, string, format)
2080n/a got = self.theclass.strptime(string, format)
2081n/a self.assertEqual(expected, got)
2082n/a self.assertIs(type(expected), self.theclass)
2083n/a self.assertIs(type(got), self.theclass)
2084n/a
2085n/a strptime = self.theclass.strptime
2086n/a self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
2087n/a self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
2088n/a # Only local timezone and UTC are supported
2089n/a for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
2090n/a (-_time.timezone, _time.tzname[0])):
2091n/a if tzseconds < 0:
2092n/a sign = '-'
2093n/a seconds = -tzseconds
2094n/a else:
2095n/a sign ='+'
2096n/a seconds = tzseconds
2097n/a hours, minutes = divmod(seconds//60, 60)
2098n/a dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
2099n/a dt = strptime(dtstr, "%z %Z")
2100n/a self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
2101n/a self.assertEqual(dt.tzname(), tzname)
2102n/a # Can produce inconsistent datetime
2103n/a dtstr, fmt = "+1234 UTC", "%z %Z"
2104n/a dt = strptime(dtstr, fmt)
2105n/a self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
2106n/a self.assertEqual(dt.tzname(), 'UTC')
2107n/a # yet will roundtrip
2108n/a self.assertEqual(dt.strftime(fmt), dtstr)
2109n/a
2110n/a # Produce naive datetime if no %z is provided
2111n/a self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
2112n/a
2113n/a with self.assertRaises(ValueError): strptime("-2400", "%z")
2114n/a with self.assertRaises(ValueError): strptime("-000", "%z")
2115n/a
2116n/a def test_more_timetuple(self):
2117n/a # This tests fields beyond those tested by the TestDate.test_timetuple.
2118n/a t = self.theclass(2004, 12, 31, 6, 22, 33)
2119n/a self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
2120n/a self.assertEqual(t.timetuple(),
2121n/a (t.year, t.month, t.day,
2122n/a t.hour, t.minute, t.second,
2123n/a t.weekday(),
2124n/a t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
2125n/a -1))
2126n/a tt = t.timetuple()
2127n/a self.assertEqual(tt.tm_year, t.year)
2128n/a self.assertEqual(tt.tm_mon, t.month)
2129n/a self.assertEqual(tt.tm_mday, t.day)
2130n/a self.assertEqual(tt.tm_hour, t.hour)
2131n/a self.assertEqual(tt.tm_min, t.minute)
2132n/a self.assertEqual(tt.tm_sec, t.second)
2133n/a self.assertEqual(tt.tm_wday, t.weekday())
2134n/a self.assertEqual(tt.tm_yday, t.toordinal() -
2135n/a date(t.year, 1, 1).toordinal() + 1)
2136n/a self.assertEqual(tt.tm_isdst, -1)
2137n/a
2138n/a def test_more_strftime(self):
2139n/a # This tests fields beyond those tested by the TestDate.test_strftime.
2140n/a t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
2141n/a self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
2142n/a "12 31 04 000047 33 22 06 366")
2143n/a
2144n/a def test_extract(self):
2145n/a dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
2146n/a self.assertEqual(dt.date(), date(2002, 3, 4))
2147n/a self.assertEqual(dt.time(), time(18, 45, 3, 1234))
2148n/a
2149n/a def test_combine(self):
2150n/a d = date(2002, 3, 4)
2151n/a t = time(18, 45, 3, 1234)
2152n/a expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
2153n/a combine = self.theclass.combine
2154n/a dt = combine(d, t)
2155n/a self.assertEqual(dt, expected)
2156n/a
2157n/a dt = combine(time=t, date=d)
2158n/a self.assertEqual(dt, expected)
2159n/a
2160n/a self.assertEqual(d, dt.date())
2161n/a self.assertEqual(t, dt.time())
2162n/a self.assertEqual(dt, combine(dt.date(), dt.time()))
2163n/a
2164n/a self.assertRaises(TypeError, combine) # need an arg
2165n/a self.assertRaises(TypeError, combine, d) # need two args
2166n/a self.assertRaises(TypeError, combine, t, d) # args reversed
2167n/a self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type
2168n/a self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args
2169n/a self.assertRaises(TypeError, combine, "date", "time") # wrong types
2170n/a self.assertRaises(TypeError, combine, d, "time") # wrong type
2171n/a self.assertRaises(TypeError, combine, "date", t) # wrong type
2172n/a
2173n/a # tzinfo= argument
2174n/a dt = combine(d, t, timezone.utc)
2175n/a self.assertIs(dt.tzinfo, timezone.utc)
2176n/a dt = combine(d, t, tzinfo=timezone.utc)
2177n/a self.assertIs(dt.tzinfo, timezone.utc)
2178n/a t = time()
2179n/a dt = combine(dt, t)
2180n/a self.assertEqual(dt.date(), d)
2181n/a self.assertEqual(dt.time(), t)
2182n/a
2183n/a def test_replace(self):
2184n/a cls = self.theclass
2185n/a args = [1, 2, 3, 4, 5, 6, 7]
2186n/a base = cls(*args)
2187n/a self.assertEqual(base, base.replace())
2188n/a
2189n/a i = 0
2190n/a for name, newval in (("year", 2),
2191n/a ("month", 3),
2192n/a ("day", 4),
2193n/a ("hour", 5),
2194n/a ("minute", 6),
2195n/a ("second", 7),
2196n/a ("microsecond", 8)):
2197n/a newargs = args[:]
2198n/a newargs[i] = newval
2199n/a expected = cls(*newargs)
2200n/a got = base.replace(**{name: newval})
2201n/a self.assertEqual(expected, got)
2202n/a i += 1
2203n/a
2204n/a # Out of bounds.
2205n/a base = cls(2000, 2, 29)
2206n/a self.assertRaises(ValueError, base.replace, year=2001)
2207n/a
2208n/a def test_astimezone(self):
2209n/a return # The rest is no longer applicable
2210n/a # Pretty boring! The TZ test is more interesting here. astimezone()
2211n/a # simply can't be applied to a naive object.
2212n/a dt = self.theclass.now()
2213n/a f = FixedOffset(44, "")
2214n/a self.assertRaises(ValueError, dt.astimezone) # naive
2215n/a self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
2216n/a self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
2217n/a self.assertRaises(ValueError, dt.astimezone, f) # naive
2218n/a self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
2219n/a
2220n/a class Bogus(tzinfo):
2221n/a def utcoffset(self, dt): return None
2222n/a def dst(self, dt): return timedelta(0)
2223n/a bog = Bogus()
2224n/a self.assertRaises(ValueError, dt.astimezone, bog) # naive
2225n/a self.assertRaises(ValueError,
2226n/a dt.replace(tzinfo=bog).astimezone, f)
2227n/a
2228n/a class AlsoBogus(tzinfo):
2229n/a def utcoffset(self, dt): return timedelta(0)
2230n/a def dst(self, dt): return None
2231n/a alsobog = AlsoBogus()
2232n/a self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
2233n/a
2234n/a def test_subclass_datetime(self):
2235n/a
2236n/a class C(self.theclass):
2237n/a theAnswer = 42
2238n/a
2239n/a def __new__(cls, *args, **kws):
2240n/a temp = kws.copy()
2241n/a extra = temp.pop('extra')
2242n/a result = self.theclass.__new__(cls, *args, **temp)
2243n/a result.extra = extra
2244n/a return result
2245n/a
2246n/a def newmeth(self, start):
2247n/a return start + self.year + self.month + self.second
2248n/a
2249n/a args = 2003, 4, 14, 12, 13, 41
2250n/a
2251n/a dt1 = self.theclass(*args)
2252n/a dt2 = C(*args, **{'extra': 7})
2253n/a
2254n/a self.assertEqual(dt2.__class__, C)
2255n/a self.assertEqual(dt2.theAnswer, 42)
2256n/a self.assertEqual(dt2.extra, 7)
2257n/a self.assertEqual(dt1.toordinal(), dt2.toordinal())
2258n/a self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
2259n/a dt1.second - 7)
2260n/a
2261n/aclass TestSubclassDateTime(TestDateTime):
2262n/a theclass = SubclassDatetime
2263n/a # Override tests not designed for subclass
2264n/a @unittest.skip('not appropriate for subclasses')
2265n/a def test_roundtrip(self):
2266n/a pass
2267n/a
2268n/aclass SubclassTime(time):
2269n/a sub_var = 1
2270n/a
2271n/aclass TestTime(HarmlessMixedComparison, unittest.TestCase):
2272n/a
2273n/a theclass = time
2274n/a
2275n/a def test_basic_attributes(self):
2276n/a t = self.theclass(12, 0)
2277n/a self.assertEqual(t.hour, 12)
2278n/a self.assertEqual(t.minute, 0)
2279n/a self.assertEqual(t.second, 0)
2280n/a self.assertEqual(t.microsecond, 0)
2281n/a
2282n/a def test_basic_attributes_nonzero(self):
2283n/a # Make sure all attributes are non-zero so bugs in
2284n/a # bit-shifting access show up.
2285n/a t = self.theclass(12, 59, 59, 8000)
2286n/a self.assertEqual(t.hour, 12)
2287n/a self.assertEqual(t.minute, 59)
2288n/a self.assertEqual(t.second, 59)
2289n/a self.assertEqual(t.microsecond, 8000)
2290n/a
2291n/a def test_roundtrip(self):
2292n/a t = self.theclass(1, 2, 3, 4)
2293n/a
2294n/a # Verify t -> string -> time identity.
2295n/a s = repr(t)
2296n/a self.assertTrue(s.startswith('datetime.'))
2297n/a s = s[9:]
2298n/a t2 = eval(s)
2299n/a self.assertEqual(t, t2)
2300n/a
2301n/a # Verify identity via reconstructing from pieces.
2302n/a t2 = self.theclass(t.hour, t.minute, t.second,
2303n/a t.microsecond)
2304n/a self.assertEqual(t, t2)
2305n/a
2306n/a def test_comparing(self):
2307n/a args = [1, 2, 3, 4]
2308n/a t1 = self.theclass(*args)
2309n/a t2 = self.theclass(*args)
2310n/a self.assertEqual(t1, t2)
2311n/a self.assertTrue(t1 <= t2)
2312n/a self.assertTrue(t1 >= t2)
2313n/a self.assertFalse(t1 != t2)
2314n/a self.assertFalse(t1 < t2)
2315n/a self.assertFalse(t1 > t2)
2316n/a
2317n/a for i in range(len(args)):
2318n/a newargs = args[:]
2319n/a newargs[i] = args[i] + 1
2320n/a t2 = self.theclass(*newargs) # this is larger than t1
2321n/a self.assertTrue(t1 < t2)
2322n/a self.assertTrue(t2 > t1)
2323n/a self.assertTrue(t1 <= t2)
2324n/a self.assertTrue(t2 >= t1)
2325n/a self.assertTrue(t1 != t2)
2326n/a self.assertTrue(t2 != t1)
2327n/a self.assertFalse(t1 == t2)
2328n/a self.assertFalse(t2 == t1)
2329n/a self.assertFalse(t1 > t2)
2330n/a self.assertFalse(t2 < t1)
2331n/a self.assertFalse(t1 >= t2)
2332n/a self.assertFalse(t2 <= t1)
2333n/a
2334n/a for badarg in OTHERSTUFF:
2335n/a self.assertEqual(t1 == badarg, False)
2336n/a self.assertEqual(t1 != badarg, True)
2337n/a self.assertEqual(badarg == t1, False)
2338n/a self.assertEqual(badarg != t1, True)
2339n/a
2340n/a self.assertRaises(TypeError, lambda: t1 <= badarg)
2341n/a self.assertRaises(TypeError, lambda: t1 < badarg)
2342n/a self.assertRaises(TypeError, lambda: t1 > badarg)
2343n/a self.assertRaises(TypeError, lambda: t1 >= badarg)
2344n/a self.assertRaises(TypeError, lambda: badarg <= t1)
2345n/a self.assertRaises(TypeError, lambda: badarg < t1)
2346n/a self.assertRaises(TypeError, lambda: badarg > t1)
2347n/a self.assertRaises(TypeError, lambda: badarg >= t1)
2348n/a
2349n/a def test_bad_constructor_arguments(self):
2350n/a # bad hours
2351n/a self.theclass(0, 0) # no exception
2352n/a self.theclass(23, 0) # no exception
2353n/a self.assertRaises(ValueError, self.theclass, -1, 0)
2354n/a self.assertRaises(ValueError, self.theclass, 24, 0)
2355n/a # bad minutes
2356n/a self.theclass(23, 0) # no exception
2357n/a self.theclass(23, 59) # no exception
2358n/a self.assertRaises(ValueError, self.theclass, 23, -1)
2359n/a self.assertRaises(ValueError, self.theclass, 23, 60)
2360n/a # bad seconds
2361n/a self.theclass(23, 59, 0) # no exception
2362n/a self.theclass(23, 59, 59) # no exception
2363n/a self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2364n/a self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2365n/a # bad microseconds
2366n/a self.theclass(23, 59, 59, 0) # no exception
2367n/a self.theclass(23, 59, 59, 999999) # no exception
2368n/a self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2369n/a self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2370n/a
2371n/a def test_hash_equality(self):
2372n/a d = self.theclass(23, 30, 17)
2373n/a e = self.theclass(23, 30, 17)
2374n/a self.assertEqual(d, e)
2375n/a self.assertEqual(hash(d), hash(e))
2376n/a
2377n/a dic = {d: 1}
2378n/a dic[e] = 2
2379n/a self.assertEqual(len(dic), 1)
2380n/a self.assertEqual(dic[d], 2)
2381n/a self.assertEqual(dic[e], 2)
2382n/a
2383n/a d = self.theclass(0, 5, 17)
2384n/a e = self.theclass(0, 5, 17)
2385n/a self.assertEqual(d, e)
2386n/a self.assertEqual(hash(d), hash(e))
2387n/a
2388n/a dic = {d: 1}
2389n/a dic[e] = 2
2390n/a self.assertEqual(len(dic), 1)
2391n/a self.assertEqual(dic[d], 2)
2392n/a self.assertEqual(dic[e], 2)
2393n/a
2394n/a def test_isoformat(self):
2395n/a t = self.theclass(4, 5, 1, 123)
2396n/a self.assertEqual(t.isoformat(), "04:05:01.000123")
2397n/a self.assertEqual(t.isoformat(), str(t))
2398n/a
2399n/a t = self.theclass()
2400n/a self.assertEqual(t.isoformat(), "00:00:00")
2401n/a self.assertEqual(t.isoformat(), str(t))
2402n/a
2403n/a t = self.theclass(microsecond=1)
2404n/a self.assertEqual(t.isoformat(), "00:00:00.000001")
2405n/a self.assertEqual(t.isoformat(), str(t))
2406n/a
2407n/a t = self.theclass(microsecond=10)
2408n/a self.assertEqual(t.isoformat(), "00:00:00.000010")
2409n/a self.assertEqual(t.isoformat(), str(t))
2410n/a
2411n/a t = self.theclass(microsecond=100)
2412n/a self.assertEqual(t.isoformat(), "00:00:00.000100")
2413n/a self.assertEqual(t.isoformat(), str(t))
2414n/a
2415n/a t = self.theclass(microsecond=1000)
2416n/a self.assertEqual(t.isoformat(), "00:00:00.001000")
2417n/a self.assertEqual(t.isoformat(), str(t))
2418n/a
2419n/a t = self.theclass(microsecond=10000)
2420n/a self.assertEqual(t.isoformat(), "00:00:00.010000")
2421n/a self.assertEqual(t.isoformat(), str(t))
2422n/a
2423n/a t = self.theclass(microsecond=100000)
2424n/a self.assertEqual(t.isoformat(), "00:00:00.100000")
2425n/a self.assertEqual(t.isoformat(), str(t))
2426n/a
2427n/a t = self.theclass(hour=12, minute=34, second=56, microsecond=123456)
2428n/a self.assertEqual(t.isoformat(timespec='hours'), "12")
2429n/a self.assertEqual(t.isoformat(timespec='minutes'), "12:34")
2430n/a self.assertEqual(t.isoformat(timespec='seconds'), "12:34:56")
2431n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.123")
2432n/a self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.123456")
2433n/a self.assertEqual(t.isoformat(timespec='auto'), "12:34:56.123456")
2434n/a self.assertRaises(ValueError, t.isoformat, timespec='monkey')
2435n/a
2436n/a t = self.theclass(hour=12, minute=34, second=56, microsecond=999500)
2437n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.999")
2438n/a
2439n/a t = self.theclass(hour=12, minute=34, second=56, microsecond=0)
2440n/a self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.000")
2441n/a self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.000000")
2442n/a self.assertEqual(t.isoformat(timespec='auto'), "12:34:56")
2443n/a
2444n/a def test_1653736(self):
2445n/a # verify it doesn't accept extra keyword arguments
2446n/a t = self.theclass(second=1)
2447n/a self.assertRaises(TypeError, t.isoformat, foo=3)
2448n/a
2449n/a def test_strftime(self):
2450n/a t = self.theclass(1, 2, 3, 4)
2451n/a self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
2452n/a # A naive object replaces %z and %Z with empty strings.
2453n/a self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2454n/a
2455n/a def test_format(self):
2456n/a t = self.theclass(1, 2, 3, 4)
2457n/a self.assertEqual(t.__format__(''), str(t))
2458n/a
2459n/a with self.assertRaisesRegex(TypeError, 'must be str, not int'):
2460n/a t.__format__(123)
2461n/a
2462n/a # check that a derived class's __str__() gets called
2463n/a class A(self.theclass):
2464n/a def __str__(self):
2465n/a return 'A'
2466n/a a = A(1, 2, 3, 4)
2467n/a self.assertEqual(a.__format__(''), 'A')
2468n/a
2469n/a # check that a derived class's strftime gets called
2470n/a class B(self.theclass):
2471n/a def strftime(self, format_spec):
2472n/a return 'B'
2473n/a b = B(1, 2, 3, 4)
2474n/a self.assertEqual(b.__format__(''), str(t))
2475n/a
2476n/a for fmt in ['%H %M %S',
2477n/a ]:
2478n/a self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2479n/a self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2480n/a self.assertEqual(b.__format__(fmt), 'B')
2481n/a
2482n/a def test_str(self):
2483n/a self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2484n/a self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2485n/a self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2486n/a self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2487n/a self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2488n/a
2489n/a def test_repr(self):
2490n/a name = 'datetime.' + self.theclass.__name__
2491n/a self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2492n/a "%s(1, 2, 3, 4)" % name)
2493n/a self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2494n/a "%s(10, 2, 3, 4000)" % name)
2495n/a self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2496n/a "%s(0, 2, 3, 400000)" % name)
2497n/a self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2498n/a "%s(12, 2, 3)" % name)
2499n/a self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2500n/a "%s(23, 15)" % name)
2501n/a
2502n/a def test_resolution_info(self):
2503n/a self.assertIsInstance(self.theclass.min, self.theclass)
2504n/a self.assertIsInstance(self.theclass.max, self.theclass)
2505n/a self.assertIsInstance(self.theclass.resolution, timedelta)
2506n/a self.assertTrue(self.theclass.max > self.theclass.min)
2507n/a
2508n/a def test_pickling(self):
2509n/a args = 20, 59, 16, 64**2
2510n/a orig = self.theclass(*args)
2511n/a for pickler, unpickler, proto in pickle_choices:
2512n/a green = pickler.dumps(orig, proto)
2513n/a derived = unpickler.loads(green)
2514n/a self.assertEqual(orig, derived)
2515n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
2516n/a
2517n/a def test_pickling_subclass_time(self):
2518n/a args = 20, 59, 16, 64**2
2519n/a orig = SubclassTime(*args)
2520n/a for pickler, unpickler, proto in pickle_choices:
2521n/a green = pickler.dumps(orig, proto)
2522n/a derived = unpickler.loads(green)
2523n/a self.assertEqual(orig, derived)
2524n/a
2525n/a def test_bool(self):
2526n/a # time is always True.
2527n/a cls = self.theclass
2528n/a self.assertTrue(cls(1))
2529n/a self.assertTrue(cls(0, 1))
2530n/a self.assertTrue(cls(0, 0, 1))
2531n/a self.assertTrue(cls(0, 0, 0, 1))
2532n/a self.assertTrue(cls(0))
2533n/a self.assertTrue(cls())
2534n/a
2535n/a def test_replace(self):
2536n/a cls = self.theclass
2537n/a args = [1, 2, 3, 4]
2538n/a base = cls(*args)
2539n/a self.assertEqual(base, base.replace())
2540n/a
2541n/a i = 0
2542n/a for name, newval in (("hour", 5),
2543n/a ("minute", 6),
2544n/a ("second", 7),
2545n/a ("microsecond", 8)):
2546n/a newargs = args[:]
2547n/a newargs[i] = newval
2548n/a expected = cls(*newargs)
2549n/a got = base.replace(**{name: newval})
2550n/a self.assertEqual(expected, got)
2551n/a i += 1
2552n/a
2553n/a # Out of bounds.
2554n/a base = cls(1)
2555n/a self.assertRaises(ValueError, base.replace, hour=24)
2556n/a self.assertRaises(ValueError, base.replace, minute=-1)
2557n/a self.assertRaises(ValueError, base.replace, second=100)
2558n/a self.assertRaises(ValueError, base.replace, microsecond=1000000)
2559n/a
2560n/a def test_subclass_time(self):
2561n/a
2562n/a class C(self.theclass):
2563n/a theAnswer = 42
2564n/a
2565n/a def __new__(cls, *args, **kws):
2566n/a temp = kws.copy()
2567n/a extra = temp.pop('extra')
2568n/a result = self.theclass.__new__(cls, *args, **temp)
2569n/a result.extra = extra
2570n/a return result
2571n/a
2572n/a def newmeth(self, start):
2573n/a return start + self.hour + self.second
2574n/a
2575n/a args = 4, 5, 6
2576n/a
2577n/a dt1 = self.theclass(*args)
2578n/a dt2 = C(*args, **{'extra': 7})
2579n/a
2580n/a self.assertEqual(dt2.__class__, C)
2581n/a self.assertEqual(dt2.theAnswer, 42)
2582n/a self.assertEqual(dt2.extra, 7)
2583n/a self.assertEqual(dt1.isoformat(), dt2.isoformat())
2584n/a self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2585n/a
2586n/a def test_backdoor_resistance(self):
2587n/a # see TestDate.test_backdoor_resistance().
2588n/a base = '2:59.0'
2589n/a for hour_byte in ' ', '9', chr(24), '\xff':
2590n/a self.assertRaises(TypeError, self.theclass,
2591n/a hour_byte + base[1:])
2592n/a # Good bytes, but bad tzinfo:
2593n/a with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
2594n/a self.theclass(bytes([1] * len(base)), 'EST')
2595n/a
2596n/a# A mixin for classes with a tzinfo= argument. Subclasses must define
2597n/a# theclass as a class attribute, and theclass(1, 1, 1, tzinfo=whatever)
2598n/a# must be legit (which is true for time and datetime).
2599n/aclass TZInfoBase:
2600n/a
2601n/a def test_argument_passing(self):
2602n/a cls = self.theclass
2603n/a # A datetime passes itself on, a time passes None.
2604n/a class introspective(tzinfo):
2605n/a def tzname(self, dt): return dt and "real" or "none"
2606n/a def utcoffset(self, dt):
2607n/a return timedelta(minutes = dt and 42 or -42)
2608n/a dst = utcoffset
2609n/a
2610n/a obj = cls(1, 2, 3, tzinfo=introspective())
2611n/a
2612n/a expected = cls is time and "none" or "real"
2613n/a self.assertEqual(obj.tzname(), expected)
2614n/a
2615n/a expected = timedelta(minutes=(cls is time and -42 or 42))
2616n/a self.assertEqual(obj.utcoffset(), expected)
2617n/a self.assertEqual(obj.dst(), expected)
2618n/a
2619n/a def test_bad_tzinfo_classes(self):
2620n/a cls = self.theclass
2621n/a self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
2622n/a
2623n/a class NiceTry(object):
2624n/a def __init__(self): pass
2625n/a def utcoffset(self, dt): pass
2626n/a self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2627n/a
2628n/a class BetterTry(tzinfo):
2629n/a def __init__(self): pass
2630n/a def utcoffset(self, dt): pass
2631n/a b = BetterTry()
2632n/a t = cls(1, 1, 1, tzinfo=b)
2633n/a self.assertIs(t.tzinfo, b)
2634n/a
2635n/a def test_utc_offset_out_of_bounds(self):
2636n/a class Edgy(tzinfo):
2637n/a def __init__(self, offset):
2638n/a self.offset = timedelta(minutes=offset)
2639n/a def utcoffset(self, dt):
2640n/a return self.offset
2641n/a
2642n/a cls = self.theclass
2643n/a for offset, legit in ((-1440, False),
2644n/a (-1439, True),
2645n/a (1439, True),
2646n/a (1440, False)):
2647n/a if cls is time:
2648n/a t = cls(1, 2, 3, tzinfo=Edgy(offset))
2649n/a elif cls is datetime:
2650n/a t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
2651n/a else:
2652n/a assert 0, "impossible"
2653n/a if legit:
2654n/a aofs = abs(offset)
2655n/a h, m = divmod(aofs, 60)
2656n/a tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
2657n/a if isinstance(t, datetime):
2658n/a t = t.timetz()
2659n/a self.assertEqual(str(t), "01:02:03" + tag)
2660n/a else:
2661n/a self.assertRaises(ValueError, str, t)
2662n/a
2663n/a def test_tzinfo_classes(self):
2664n/a cls = self.theclass
2665n/a class C1(tzinfo):
2666n/a def utcoffset(self, dt): return None
2667n/a def dst(self, dt): return None
2668n/a def tzname(self, dt): return None
2669n/a for t in (cls(1, 1, 1),
2670n/a cls(1, 1, 1, tzinfo=None),
2671n/a cls(1, 1, 1, tzinfo=C1())):
2672n/a self.assertIsNone(t.utcoffset())
2673n/a self.assertIsNone(t.dst())
2674n/a self.assertIsNone(t.tzname())
2675n/a
2676n/a class C3(tzinfo):
2677n/a def utcoffset(self, dt): return timedelta(minutes=-1439)
2678n/a def dst(self, dt): return timedelta(minutes=1439)
2679n/a def tzname(self, dt): return "aname"
2680n/a t = cls(1, 1, 1, tzinfo=C3())
2681n/a self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2682n/a self.assertEqual(t.dst(), timedelta(minutes=1439))
2683n/a self.assertEqual(t.tzname(), "aname")
2684n/a
2685n/a # Wrong types.
2686n/a class C4(tzinfo):
2687n/a def utcoffset(self, dt): return "aname"
2688n/a def dst(self, dt): return 7
2689n/a def tzname(self, dt): return 0
2690n/a t = cls(1, 1, 1, tzinfo=C4())
2691n/a self.assertRaises(TypeError, t.utcoffset)
2692n/a self.assertRaises(TypeError, t.dst)
2693n/a self.assertRaises(TypeError, t.tzname)
2694n/a
2695n/a # Offset out of range.
2696n/a class C6(tzinfo):
2697n/a def utcoffset(self, dt): return timedelta(hours=-24)
2698n/a def dst(self, dt): return timedelta(hours=24)
2699n/a t = cls(1, 1, 1, tzinfo=C6())
2700n/a self.assertRaises(ValueError, t.utcoffset)
2701n/a self.assertRaises(ValueError, t.dst)
2702n/a
2703n/a # Not a whole number of seconds.
2704n/a class C7(tzinfo):
2705n/a def utcoffset(self, dt): return timedelta(microseconds=61)
2706n/a def dst(self, dt): return timedelta(microseconds=-81)
2707n/a t = cls(1, 1, 1, tzinfo=C7())
2708n/a self.assertRaises(ValueError, t.utcoffset)
2709n/a self.assertRaises(ValueError, t.dst)
2710n/a
2711n/a def test_aware_compare(self):
2712n/a cls = self.theclass
2713n/a
2714n/a # Ensure that utcoffset() gets ignored if the comparands have
2715n/a # the same tzinfo member.
2716n/a class OperandDependentOffset(tzinfo):
2717n/a def utcoffset(self, t):
2718n/a if t.minute < 10:
2719n/a # d0 and d1 equal after adjustment
2720n/a return timedelta(minutes=t.minute)
2721n/a else:
2722n/a # d2 off in the weeds
2723n/a return timedelta(minutes=59)
2724n/a
2725n/a base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2726n/a d0 = base.replace(minute=3)
2727n/a d1 = base.replace(minute=9)
2728n/a d2 = base.replace(minute=11)
2729n/a for x in d0, d1, d2:
2730n/a for y in d0, d1, d2:
2731n/a for op in lt, le, gt, ge, eq, ne:
2732n/a got = op(x, y)
2733n/a expected = op(x.minute, y.minute)
2734n/a self.assertEqual(got, expected)
2735n/a
2736n/a # However, if they're different members, uctoffset is not ignored.
2737n/a # Note that a time can't actually have an operand-depedent offset,
2738n/a # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2739n/a # so skip this test for time.
2740n/a if cls is not time:
2741n/a d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2742n/a d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2743n/a d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2744n/a for x in d0, d1, d2:
2745n/a for y in d0, d1, d2:
2746n/a got = (x > y) - (x < y)
2747n/a if (x is d0 or x is d1) and (y is d0 or y is d1):
2748n/a expected = 0
2749n/a elif x is y is d2:
2750n/a expected = 0
2751n/a elif x is d2:
2752n/a expected = -1
2753n/a else:
2754n/a assert y is d2
2755n/a expected = 1
2756n/a self.assertEqual(got, expected)
2757n/a
2758n/a
2759n/a# Testing time objects with a non-None tzinfo.
2760n/aclass TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
2761n/a theclass = time
2762n/a
2763n/a def test_empty(self):
2764n/a t = self.theclass()
2765n/a self.assertEqual(t.hour, 0)
2766n/a self.assertEqual(t.minute, 0)
2767n/a self.assertEqual(t.second, 0)
2768n/a self.assertEqual(t.microsecond, 0)
2769n/a self.assertIsNone(t.tzinfo)
2770n/a
2771n/a def test_zones(self):
2772n/a est = FixedOffset(-300, "EST", 1)
2773n/a utc = FixedOffset(0, "UTC", -2)
2774n/a met = FixedOffset(60, "MET", 3)
2775n/a t1 = time( 7, 47, tzinfo=est)
2776n/a t2 = time(12, 47, tzinfo=utc)
2777n/a t3 = time(13, 47, tzinfo=met)
2778n/a t4 = time(microsecond=40)
2779n/a t5 = time(microsecond=40, tzinfo=utc)
2780n/a
2781n/a self.assertEqual(t1.tzinfo, est)
2782n/a self.assertEqual(t2.tzinfo, utc)
2783n/a self.assertEqual(t3.tzinfo, met)
2784n/a self.assertIsNone(t4.tzinfo)
2785n/a self.assertEqual(t5.tzinfo, utc)
2786n/a
2787n/a self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2788n/a self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2789n/a self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
2790n/a self.assertIsNone(t4.utcoffset())
2791n/a self.assertRaises(TypeError, t1.utcoffset, "no args")
2792n/a
2793n/a self.assertEqual(t1.tzname(), "EST")
2794n/a self.assertEqual(t2.tzname(), "UTC")
2795n/a self.assertEqual(t3.tzname(), "MET")
2796n/a self.assertIsNone(t4.tzname())
2797n/a self.assertRaises(TypeError, t1.tzname, "no args")
2798n/a
2799n/a self.assertEqual(t1.dst(), timedelta(minutes=1))
2800n/a self.assertEqual(t2.dst(), timedelta(minutes=-2))
2801n/a self.assertEqual(t3.dst(), timedelta(minutes=3))
2802n/a self.assertIsNone(t4.dst())
2803n/a self.assertRaises(TypeError, t1.dst, "no args")
2804n/a
2805n/a self.assertEqual(hash(t1), hash(t2))
2806n/a self.assertEqual(hash(t1), hash(t3))
2807n/a self.assertEqual(hash(t2), hash(t3))
2808n/a
2809n/a self.assertEqual(t1, t2)
2810n/a self.assertEqual(t1, t3)
2811n/a self.assertEqual(t2, t3)
2812n/a self.assertNotEqual(t4, t5) # mixed tz-aware & naive
2813n/a self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2814n/a self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2815n/a
2816n/a self.assertEqual(str(t1), "07:47:00-05:00")
2817n/a self.assertEqual(str(t2), "12:47:00+00:00")
2818n/a self.assertEqual(str(t3), "13:47:00+01:00")
2819n/a self.assertEqual(str(t4), "00:00:00.000040")
2820n/a self.assertEqual(str(t5), "00:00:00.000040+00:00")
2821n/a
2822n/a self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2823n/a self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2824n/a self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2825n/a self.assertEqual(t4.isoformat(), "00:00:00.000040")
2826n/a self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2827n/a
2828n/a d = 'datetime.time'
2829n/a self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2830n/a self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2831n/a self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2832n/a self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2833n/a self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2834n/a
2835n/a self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2836n/a "07:47:00 %Z=EST %z=-0500")
2837n/a self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2838n/a self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2839n/a
2840n/a yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
2841n/a t1 = time(23, 59, tzinfo=yuck)
2842n/a self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2843n/a "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2844n/a
2845n/a # Check that an invalid tzname result raises an exception.
2846n/a class Badtzname(tzinfo):
2847n/a tz = 42
2848n/a def tzname(self, dt): return self.tz
2849n/a t = time(2, 3, 4, tzinfo=Badtzname())
2850n/a self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2851n/a self.assertRaises(TypeError, t.strftime, "%Z")
2852n/a
2853n/a # Issue #6697:
2854n/a if '_Fast' in str(self):
2855n/a Badtzname.tz = '\ud800'
2856n/a self.assertRaises(ValueError, t.strftime, "%Z")
2857n/a
2858n/a def test_hash_edge_cases(self):
2859n/a # Offsets that overflow a basic time.
2860n/a t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2861n/a t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2862n/a self.assertEqual(hash(t1), hash(t2))
2863n/a
2864n/a t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2865n/a t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2866n/a self.assertEqual(hash(t1), hash(t2))
2867n/a
2868n/a def test_pickling(self):
2869n/a # Try one without a tzinfo.
2870n/a args = 20, 59, 16, 64**2
2871n/a orig = self.theclass(*args)
2872n/a for pickler, unpickler, proto in pickle_choices:
2873n/a green = pickler.dumps(orig, proto)
2874n/a derived = unpickler.loads(green)
2875n/a self.assertEqual(orig, derived)
2876n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
2877n/a
2878n/a # Try one with a tzinfo.
2879n/a tinfo = PicklableFixedOffset(-300, 'cookie')
2880n/a orig = self.theclass(5, 6, 7, tzinfo=tinfo)
2881n/a for pickler, unpickler, proto in pickle_choices:
2882n/a green = pickler.dumps(orig, proto)
2883n/a derived = unpickler.loads(green)
2884n/a self.assertEqual(orig, derived)
2885n/a self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
2886n/a self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2887n/a self.assertEqual(derived.tzname(), 'cookie')
2888n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
2889n/a
2890n/a def test_more_bool(self):
2891n/a # time is always True.
2892n/a cls = self.theclass
2893n/a
2894n/a t = cls(0, tzinfo=FixedOffset(-300, ""))
2895n/a self.assertTrue(t)
2896n/a
2897n/a t = cls(5, tzinfo=FixedOffset(-300, ""))
2898n/a self.assertTrue(t)
2899n/a
2900n/a t = cls(5, tzinfo=FixedOffset(300, ""))
2901n/a self.assertTrue(t)
2902n/a
2903n/a t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2904n/a self.assertTrue(t)
2905n/a
2906n/a def test_replace(self):
2907n/a cls = self.theclass
2908n/a z100 = FixedOffset(100, "+100")
2909n/a zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2910n/a args = [1, 2, 3, 4, z100]
2911n/a base = cls(*args)
2912n/a self.assertEqual(base, base.replace())
2913n/a
2914n/a i = 0
2915n/a for name, newval in (("hour", 5),
2916n/a ("minute", 6),
2917n/a ("second", 7),
2918n/a ("microsecond", 8),
2919n/a ("tzinfo", zm200)):
2920n/a newargs = args[:]
2921n/a newargs[i] = newval
2922n/a expected = cls(*newargs)
2923n/a got = base.replace(**{name: newval})
2924n/a self.assertEqual(expected, got)
2925n/a i += 1
2926n/a
2927n/a # Ensure we can get rid of a tzinfo.
2928n/a self.assertEqual(base.tzname(), "+100")
2929n/a base2 = base.replace(tzinfo=None)
2930n/a self.assertIsNone(base2.tzinfo)
2931n/a self.assertIsNone(base2.tzname())
2932n/a
2933n/a # Ensure we can add one.
2934n/a base3 = base2.replace(tzinfo=z100)
2935n/a self.assertEqual(base, base3)
2936n/a self.assertIs(base.tzinfo, base3.tzinfo)
2937n/a
2938n/a # Out of bounds.
2939n/a base = cls(1)
2940n/a self.assertRaises(ValueError, base.replace, hour=24)
2941n/a self.assertRaises(ValueError, base.replace, minute=-1)
2942n/a self.assertRaises(ValueError, base.replace, second=100)
2943n/a self.assertRaises(ValueError, base.replace, microsecond=1000000)
2944n/a
2945n/a def test_mixed_compare(self):
2946n/a t1 = time(1, 2, 3)
2947n/a t2 = time(1, 2, 3)
2948n/a self.assertEqual(t1, t2)
2949n/a t2 = t2.replace(tzinfo=None)
2950n/a self.assertEqual(t1, t2)
2951n/a t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2952n/a self.assertEqual(t1, t2)
2953n/a t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2954n/a self.assertNotEqual(t1, t2)
2955n/a
2956n/a # In time w/ identical tzinfo objects, utcoffset is ignored.
2957n/a class Varies(tzinfo):
2958n/a def __init__(self):
2959n/a self.offset = timedelta(minutes=22)
2960n/a def utcoffset(self, t):
2961n/a self.offset += timedelta(minutes=1)
2962n/a return self.offset
2963n/a
2964n/a v = Varies()
2965n/a t1 = t2.replace(tzinfo=v)
2966n/a t2 = t2.replace(tzinfo=v)
2967n/a self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2968n/a self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2969n/a self.assertEqual(t1, t2)
2970n/a
2971n/a # But if they're not identical, it isn't ignored.
2972n/a t2 = t2.replace(tzinfo=Varies())
2973n/a self.assertTrue(t1 < t2) # t1's offset counter still going up
2974n/a
2975n/a def test_subclass_timetz(self):
2976n/a
2977n/a class C(self.theclass):
2978n/a theAnswer = 42
2979n/a
2980n/a def __new__(cls, *args, **kws):
2981n/a temp = kws.copy()
2982n/a extra = temp.pop('extra')
2983n/a result = self.theclass.__new__(cls, *args, **temp)
2984n/a result.extra = extra
2985n/a return result
2986n/a
2987n/a def newmeth(self, start):
2988n/a return start + self.hour + self.second
2989n/a
2990n/a args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2991n/a
2992n/a dt1 = self.theclass(*args)
2993n/a dt2 = C(*args, **{'extra': 7})
2994n/a
2995n/a self.assertEqual(dt2.__class__, C)
2996n/a self.assertEqual(dt2.theAnswer, 42)
2997n/a self.assertEqual(dt2.extra, 7)
2998n/a self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2999n/a self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
3000n/a
3001n/a
3002n/a# Testing datetime objects with a non-None tzinfo.
3003n/a
3004n/aclass TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
3005n/a theclass = datetime
3006n/a
3007n/a def test_trivial(self):
3008n/a dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
3009n/a self.assertEqual(dt.year, 1)
3010n/a self.assertEqual(dt.month, 2)
3011n/a self.assertEqual(dt.day, 3)
3012n/a self.assertEqual(dt.hour, 4)
3013n/a self.assertEqual(dt.minute, 5)
3014n/a self.assertEqual(dt.second, 6)
3015n/a self.assertEqual(dt.microsecond, 7)
3016n/a self.assertEqual(dt.tzinfo, None)
3017n/a
3018n/a def test_even_more_compare(self):
3019n/a # The test_compare() and test_more_compare() inherited from TestDate
3020n/a # and TestDateTime covered non-tzinfo cases.
3021n/a
3022n/a # Smallest possible after UTC adjustment.
3023n/a t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
3024n/a # Largest possible after UTC adjustment.
3025n/a t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
3026n/a tzinfo=FixedOffset(-1439, ""))
3027n/a
3028n/a # Make sure those compare correctly, and w/o overflow.
3029n/a self.assertTrue(t1 < t2)
3030n/a self.assertTrue(t1 != t2)
3031n/a self.assertTrue(t2 > t1)
3032n/a
3033n/a self.assertEqual(t1, t1)
3034n/a self.assertEqual(t2, t2)
3035n/a
3036n/a # Equal afer adjustment.
3037n/a t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
3038n/a t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
3039n/a self.assertEqual(t1, t2)
3040n/a
3041n/a # Change t1 not to subtract a minute, and t1 should be larger.
3042n/a t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
3043n/a self.assertTrue(t1 > t2)
3044n/a
3045n/a # Change t1 to subtract 2 minutes, and t1 should be smaller.
3046n/a t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
3047n/a self.assertTrue(t1 < t2)
3048n/a
3049n/a # Back to the original t1, but make seconds resolve it.
3050n/a t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
3051n/a second=1)
3052n/a self.assertTrue(t1 > t2)
3053n/a
3054n/a # Likewise, but make microseconds resolve it.
3055n/a t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
3056n/a microsecond=1)
3057n/a self.assertTrue(t1 > t2)
3058n/a
3059n/a # Make t2 naive and it should differ.
3060n/a t2 = self.theclass.min
3061n/a self.assertNotEqual(t1, t2)
3062n/a self.assertEqual(t2, t2)
3063n/a
3064n/a # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
3065n/a class Naive(tzinfo):
3066n/a def utcoffset(self, dt): return None
3067n/a t2 = self.theclass(5, 6, 7, tzinfo=Naive())
3068n/a self.assertNotEqual(t1, t2)
3069n/a self.assertEqual(t2, t2)
3070n/a
3071n/a # OTOH, it's OK to compare two of these mixing the two ways of being
3072n/a # naive.
3073n/a t1 = self.theclass(5, 6, 7)
3074n/a self.assertEqual(t1, t2)
3075n/a
3076n/a # Try a bogus uctoffset.
3077n/a class Bogus(tzinfo):
3078n/a def utcoffset(self, dt):
3079n/a return timedelta(minutes=1440) # out of bounds
3080n/a t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
3081n/a t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
3082n/a self.assertRaises(ValueError, lambda: t1 == t2)
3083n/a
3084n/a def test_pickling(self):
3085n/a # Try one without a tzinfo.
3086n/a args = 6, 7, 23, 20, 59, 1, 64**2
3087n/a orig = self.theclass(*args)
3088n/a for pickler, unpickler, proto in pickle_choices:
3089n/a green = pickler.dumps(orig, proto)
3090n/a derived = unpickler.loads(green)
3091n/a self.assertEqual(orig, derived)
3092n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
3093n/a
3094n/a # Try one with a tzinfo.
3095n/a tinfo = PicklableFixedOffset(-300, 'cookie')
3096n/a orig = self.theclass(*args, **{'tzinfo': tinfo})
3097n/a derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
3098n/a for pickler, unpickler, proto in pickle_choices:
3099n/a green = pickler.dumps(orig, proto)
3100n/a derived = unpickler.loads(green)
3101n/a self.assertEqual(orig, derived)
3102n/a self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
3103n/a self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
3104n/a self.assertEqual(derived.tzname(), 'cookie')
3105n/a self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
3106n/a
3107n/a def test_extreme_hashes(self):
3108n/a # If an attempt is made to hash these via subtracting the offset
3109n/a # then hashing a datetime object, OverflowError results. The
3110n/a # Python implementation used to blow up here.
3111n/a t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
3112n/a hash(t)
3113n/a t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
3114n/a tzinfo=FixedOffset(-1439, ""))
3115n/a hash(t)
3116n/a
3117n/a # OTOH, an OOB offset should blow up.
3118n/a t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
3119n/a self.assertRaises(ValueError, hash, t)
3120n/a
3121n/a def test_zones(self):
3122n/a est = FixedOffset(-300, "EST")
3123n/a utc = FixedOffset(0, "UTC")
3124n/a met = FixedOffset(60, "MET")
3125n/a t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
3126n/a t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
3127n/a t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
3128n/a self.assertEqual(t1.tzinfo, est)
3129n/a self.assertEqual(t2.tzinfo, utc)
3130n/a self.assertEqual(t3.tzinfo, met)
3131n/a self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
3132n/a self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
3133n/a self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
3134n/a self.assertEqual(t1.tzname(), "EST")
3135n/a self.assertEqual(t2.tzname(), "UTC")
3136n/a self.assertEqual(t3.tzname(), "MET")
3137n/a self.assertEqual(hash(t1), hash(t2))
3138n/a self.assertEqual(hash(t1), hash(t3))
3139n/a self.assertEqual(hash(t2), hash(t3))
3140n/a self.assertEqual(t1, t2)
3141n/a self.assertEqual(t1, t3)
3142n/a self.assertEqual(t2, t3)
3143n/a self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
3144n/a self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
3145n/a self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
3146n/a d = 'datetime.datetime(2002, 3, 19, '
3147n/a self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
3148n/a self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
3149n/a self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
3150n/a
3151n/a def test_combine(self):
3152n/a met = FixedOffset(60, "MET")
3153n/a d = date(2002, 3, 4)
3154n/a tz = time(18, 45, 3, 1234, tzinfo=met)
3155n/a dt = datetime.combine(d, tz)
3156n/a self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
3157n/a tzinfo=met))
3158n/a
3159n/a def test_extract(self):
3160n/a met = FixedOffset(60, "MET")
3161n/a dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
3162n/a self.assertEqual(dt.date(), date(2002, 3, 4))
3163n/a self.assertEqual(dt.time(), time(18, 45, 3, 1234))
3164n/a self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
3165n/a
3166n/a def test_tz_aware_arithmetic(self):
3167n/a import random
3168n/a
3169n/a now = self.theclass.now()
3170n/a tz55 = FixedOffset(-330, "west 5:30")
3171n/a timeaware = now.time().replace(tzinfo=tz55)
3172n/a nowaware = self.theclass.combine(now.date(), timeaware)
3173n/a self.assertIs(nowaware.tzinfo, tz55)
3174n/a self.assertEqual(nowaware.timetz(), timeaware)
3175n/a
3176n/a # Can't mix aware and non-aware.
3177n/a self.assertRaises(TypeError, lambda: now - nowaware)
3178n/a self.assertRaises(TypeError, lambda: nowaware - now)
3179n/a
3180n/a # And adding datetime's doesn't make sense, aware or not.
3181n/a self.assertRaises(TypeError, lambda: now + nowaware)
3182n/a self.assertRaises(TypeError, lambda: nowaware + now)
3183n/a self.assertRaises(TypeError, lambda: nowaware + nowaware)
3184n/a
3185n/a # Subtracting should yield 0.
3186n/a self.assertEqual(now - now, timedelta(0))
3187n/a self.assertEqual(nowaware - nowaware, timedelta(0))
3188n/a
3189n/a # Adding a delta should preserve tzinfo.
3190n/a delta = timedelta(weeks=1, minutes=12, microseconds=5678)
3191n/a nowawareplus = nowaware + delta
3192n/a self.assertIs(nowaware.tzinfo, tz55)
3193n/a nowawareplus2 = delta + nowaware
3194n/a self.assertIs(nowawareplus2.tzinfo, tz55)
3195n/a self.assertEqual(nowawareplus, nowawareplus2)
3196n/a
3197n/a # that - delta should be what we started with, and that - what we
3198n/a # started with should be delta.
3199n/a diff = nowawareplus - delta
3200n/a self.assertIs(diff.tzinfo, tz55)
3201n/a self.assertEqual(nowaware, diff)
3202n/a self.assertRaises(TypeError, lambda: delta - nowawareplus)
3203n/a self.assertEqual(nowawareplus - nowaware, delta)
3204n/a
3205n/a # Make up a random timezone.
3206n/a tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
3207n/a # Attach it to nowawareplus.
3208n/a nowawareplus = nowawareplus.replace(tzinfo=tzr)
3209n/a self.assertIs(nowawareplus.tzinfo, tzr)
3210n/a # Make sure the difference takes the timezone adjustments into account.
3211n/a got = nowaware - nowawareplus
3212n/a # Expected: (nowaware base - nowaware offset) -
3213n/a # (nowawareplus base - nowawareplus offset) =
3214n/a # (nowaware base - nowawareplus base) +
3215n/a # (nowawareplus offset - nowaware offset) =
3216n/a # -delta + nowawareplus offset - nowaware offset
3217n/a expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
3218n/a self.assertEqual(got, expected)
3219n/a
3220n/a # Try max possible difference.
3221n/a min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
3222n/a max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
3223n/a tzinfo=FixedOffset(-1439, "max"))
3224n/a maxdiff = max - min
3225n/a self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
3226n/a timedelta(minutes=2*1439))
3227n/a # Different tzinfo, but the same offset
3228n/a tza = timezone(HOUR, 'A')
3229n/a tzb = timezone(HOUR, 'B')
3230n/a delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
3231n/a self.assertEqual(delta, self.theclass.min - self.theclass.max)
3232n/a
3233n/a def test_tzinfo_now(self):
3234n/a meth = self.theclass.now
3235n/a # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3236n/a base = meth()
3237n/a # Try with and without naming the keyword.
3238n/a off42 = FixedOffset(42, "42")
3239n/a another = meth(off42)
3240n/a again = meth(tz=off42)
3241n/a self.assertIs(another.tzinfo, again.tzinfo)
3242n/a self.assertEqual(another.utcoffset(), timedelta(minutes=42))
3243n/a # Bad argument with and w/o naming the keyword.
3244n/a self.assertRaises(TypeError, meth, 16)
3245n/a self.assertRaises(TypeError, meth, tzinfo=16)
3246n/a # Bad keyword name.
3247n/a self.assertRaises(TypeError, meth, tinfo=off42)
3248n/a # Too many args.
3249n/a self.assertRaises(TypeError, meth, off42, off42)
3250n/a
3251n/a # We don't know which time zone we're in, and don't have a tzinfo
3252n/a # class to represent it, so seeing whether a tz argument actually
3253n/a # does a conversion is tricky.
3254n/a utc = FixedOffset(0, "utc", 0)
3255n/a for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
3256n/a timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
3257n/a for dummy in range(3):
3258n/a now = datetime.now(weirdtz)
3259n/a self.assertIs(now.tzinfo, weirdtz)
3260n/a utcnow = datetime.utcnow().replace(tzinfo=utc)
3261n/a now2 = utcnow.astimezone(weirdtz)
3262n/a if abs(now - now2) < timedelta(seconds=30):
3263n/a break
3264n/a # Else the code is broken, or more than 30 seconds passed between
3265n/a # calls; assuming the latter, just try again.
3266n/a else:
3267n/a # Three strikes and we're out.
3268n/a self.fail("utcnow(), now(tz), or astimezone() may be broken")
3269n/a
3270n/a def test_tzinfo_fromtimestamp(self):
3271n/a import time
3272n/a meth = self.theclass.fromtimestamp
3273n/a ts = time.time()
3274n/a # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3275n/a base = meth(ts)
3276n/a # Try with and without naming the keyword.
3277n/a off42 = FixedOffset(42, "42")
3278n/a another = meth(ts, off42)
3279n/a again = meth(ts, tz=off42)
3280n/a self.assertIs(another.tzinfo, again.tzinfo)
3281n/a self.assertEqual(another.utcoffset(), timedelta(minutes=42))
3282n/a # Bad argument with and w/o naming the keyword.
3283n/a self.assertRaises(TypeError, meth, ts, 16)
3284n/a self.assertRaises(TypeError, meth, ts, tzinfo=16)
3285n/a # Bad keyword name.
3286n/a self.assertRaises(TypeError, meth, ts, tinfo=off42)
3287n/a # Too many args.
3288n/a self.assertRaises(TypeError, meth, ts, off42, off42)
3289n/a # Too few args.
3290n/a self.assertRaises(TypeError, meth)
3291n/a
3292n/a # Try to make sure tz= actually does some conversion.
3293n/a timestamp = 1000000000
3294n/a utcdatetime = datetime.utcfromtimestamp(timestamp)
3295n/a # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
3296n/a # But on some flavor of Mac, it's nowhere near that. So we can't have
3297n/a # any idea here what time that actually is, we can only test that
3298n/a # relative changes match.
3299n/a utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
3300n/a tz = FixedOffset(utcoffset, "tz", 0)
3301n/a expected = utcdatetime + utcoffset
3302n/a got = datetime.fromtimestamp(timestamp, tz)
3303n/a self.assertEqual(expected, got.replace(tzinfo=None))
3304n/a
3305n/a def test_tzinfo_utcnow(self):
3306n/a meth = self.theclass.utcnow
3307n/a # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3308n/a base = meth()
3309n/a # Try with and without naming the keyword; for whatever reason,
3310n/a # utcnow() doesn't accept a tzinfo argument.
3311n/a off42 = FixedOffset(42, "42")
3312n/a self.assertRaises(TypeError, meth, off42)
3313n/a self.assertRaises(TypeError, meth, tzinfo=off42)
3314n/a
3315n/a def test_tzinfo_utcfromtimestamp(self):
3316n/a import time
3317n/a meth = self.theclass.utcfromtimestamp
3318n/a ts = time.time()
3319n/a # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3320n/a base = meth(ts)
3321n/a # Try with and without naming the keyword; for whatever reason,
3322n/a # utcfromtimestamp() doesn't accept a tzinfo argument.
3323n/a off42 = FixedOffset(42, "42")
3324n/a self.assertRaises(TypeError, meth, ts, off42)
3325n/a self.assertRaises(TypeError, meth, ts, tzinfo=off42)
3326n/a
3327n/a def test_tzinfo_timetuple(self):
3328n/a # TestDateTime tested most of this. datetime adds a twist to the
3329n/a # DST flag.
3330n/a class DST(tzinfo):
3331n/a def __init__(self, dstvalue):
3332n/a if isinstance(dstvalue, int):
3333n/a dstvalue = timedelta(minutes=dstvalue)
3334n/a self.dstvalue = dstvalue
3335n/a def dst(self, dt):
3336n/a return self.dstvalue
3337n/a
3338n/a cls = self.theclass
3339n/a for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
3340n/a d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
3341n/a t = d.timetuple()
3342n/a self.assertEqual(1, t.tm_year)
3343n/a self.assertEqual(1, t.tm_mon)
3344n/a self.assertEqual(1, t.tm_mday)
3345n/a self.assertEqual(10, t.tm_hour)
3346n/a self.assertEqual(20, t.tm_min)
3347n/a self.assertEqual(30, t.tm_sec)
3348n/a self.assertEqual(0, t.tm_wday)
3349n/a self.assertEqual(1, t.tm_yday)
3350n/a self.assertEqual(flag, t.tm_isdst)
3351n/a
3352n/a # dst() returns wrong type.
3353n/a self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
3354n/a
3355n/a # dst() at the edge.
3356n/a self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
3357n/a self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
3358n/a
3359n/a # dst() out of range.
3360n/a self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
3361n/a self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
3362n/a
3363n/a def test_utctimetuple(self):
3364n/a class DST(tzinfo):
3365n/a def __init__(self, dstvalue=0):
3366n/a if isinstance(dstvalue, int):
3367n/a dstvalue = timedelta(minutes=dstvalue)
3368n/a self.dstvalue = dstvalue
3369n/a def dst(self, dt):
3370n/a return self.dstvalue
3371n/a
3372n/a cls = self.theclass
3373n/a # This can't work: DST didn't implement utcoffset.
3374n/a self.assertRaises(NotImplementedError,
3375n/a cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3376n/a
3377n/a class UOFS(DST):
3378n/a def __init__(self, uofs, dofs=None):
3379n/a DST.__init__(self, dofs)
3380n/a self.uofs = timedelta(minutes=uofs)
3381n/a def utcoffset(self, dt):
3382n/a return self.uofs
3383n/a
3384n/a for dstvalue in -33, 33, 0, None:
3385n/a d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3386n/a t = d.utctimetuple()
3387n/a self.assertEqual(d.year, t.tm_year)
3388n/a self.assertEqual(d.month, t.tm_mon)
3389n/a self.assertEqual(d.day, t.tm_mday)
3390n/a self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3391n/a self.assertEqual(13, t.tm_min)
3392n/a self.assertEqual(d.second, t.tm_sec)
3393n/a self.assertEqual(d.weekday(), t.tm_wday)
3394n/a self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3395n/a t.tm_yday)
3396n/a # Ensure tm_isdst is 0 regardless of what dst() says: DST
3397n/a # is never in effect for a UTC time.
3398n/a self.assertEqual(0, t.tm_isdst)
3399n/a
3400n/a # For naive datetime, utctimetuple == timetuple except for isdst
3401n/a d = cls(1, 2, 3, 10, 20, 30, 40)
3402n/a t = d.utctimetuple()
3403n/a self.assertEqual(t[:-1], d.timetuple()[:-1])
3404n/a self.assertEqual(0, t.tm_isdst)
3405n/a # Same if utcoffset is None
3406n/a class NOFS(DST):
3407n/a def utcoffset(self, dt):
3408n/a return None
3409n/a d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
3410n/a t = d.utctimetuple()
3411n/a self.assertEqual(t[:-1], d.timetuple()[:-1])
3412n/a self.assertEqual(0, t.tm_isdst)
3413n/a # Check that bad tzinfo is detected
3414n/a class BOFS(DST):
3415n/a def utcoffset(self, dt):
3416n/a return "EST"
3417n/a d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
3418n/a self.assertRaises(TypeError, d.utctimetuple)
3419n/a
3420n/a # Check that utctimetuple() is the same as
3421n/a # astimezone(utc).timetuple()
3422n/a d = cls(2010, 11, 13, 14, 15, 16, 171819)
3423n/a for tz in [timezone.min, timezone.utc, timezone.max]:
3424n/a dtz = d.replace(tzinfo=tz)
3425n/a self.assertEqual(dtz.utctimetuple()[:-1],
3426n/a dtz.astimezone(timezone.utc).timetuple()[:-1])
3427n/a # At the edges, UTC adjustment can produce years out-of-range
3428n/a # for a datetime object. Ensure that an OverflowError is
3429n/a # raised.
3430n/a tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3431n/a # That goes back 1 minute less than a full day.
3432n/a self.assertRaises(OverflowError, tiny.utctimetuple)
3433n/a
3434n/a huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3435n/a # That goes forward 1 minute less than a full day.
3436n/a self.assertRaises(OverflowError, huge.utctimetuple)
3437n/a # More overflow cases
3438n/a tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3439n/a self.assertRaises(OverflowError, tiny.utctimetuple)
3440n/a huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3441n/a self.assertRaises(OverflowError, huge.utctimetuple)
3442n/a
3443n/a def test_tzinfo_isoformat(self):
3444n/a zero = FixedOffset(0, "+00:00")
3445n/a plus = FixedOffset(220, "+03:40")
3446n/a minus = FixedOffset(-231, "-03:51")
3447n/a unknown = FixedOffset(None, "")
3448n/a
3449n/a cls = self.theclass
3450n/a datestr = '0001-02-03'
3451n/a for ofs in None, zero, plus, minus, unknown:
3452n/a for us in 0, 987001:
3453n/a d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3454n/a timestr = '04:05:59' + (us and '.987001' or '')
3455n/a ofsstr = ofs is not None and d.tzname() or ''
3456n/a tailstr = timestr + ofsstr
3457n/a iso = d.isoformat()
3458n/a self.assertEqual(iso, datestr + 'T' + tailstr)
3459n/a self.assertEqual(iso, d.isoformat('T'))
3460n/a self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
3461n/a self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
3462n/a self.assertEqual(str(d), datestr + ' ' + tailstr)
3463n/a
3464n/a def test_replace(self):
3465n/a cls = self.theclass
3466n/a z100 = FixedOffset(100, "+100")
3467n/a zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3468n/a args = [1, 2, 3, 4, 5, 6, 7, z100]
3469n/a base = cls(*args)
3470n/a self.assertEqual(base, base.replace())
3471n/a
3472n/a i = 0
3473n/a for name, newval in (("year", 2),
3474n/a ("month", 3),
3475n/a ("day", 4),
3476n/a ("hour", 5),
3477n/a ("minute", 6),
3478n/a ("second", 7),
3479n/a ("microsecond", 8),
3480n/a ("tzinfo", zm200)):
3481n/a newargs = args[:]
3482n/a newargs[i] = newval
3483n/a expected = cls(*newargs)
3484n/a got = base.replace(**{name: newval})
3485n/a self.assertEqual(expected, got)
3486n/a i += 1
3487n/a
3488n/a # Ensure we can get rid of a tzinfo.
3489n/a self.assertEqual(base.tzname(), "+100")
3490n/a base2 = base.replace(tzinfo=None)
3491n/a self.assertIsNone(base2.tzinfo)
3492n/a self.assertIsNone(base2.tzname())
3493n/a
3494n/a # Ensure we can add one.
3495n/a base3 = base2.replace(tzinfo=z100)
3496n/a self.assertEqual(base, base3)
3497n/a self.assertIs(base.tzinfo, base3.tzinfo)
3498n/a
3499n/a # Out of bounds.
3500n/a base = cls(2000, 2, 29)
3501n/a self.assertRaises(ValueError, base.replace, year=2001)
3502n/a
3503n/a def test_more_astimezone(self):
3504n/a # The inherited test_astimezone covered some trivial and error cases.
3505n/a fnone = FixedOffset(None, "None")
3506n/a f44m = FixedOffset(44, "44")
3507n/a fm5h = FixedOffset(-timedelta(hours=5), "m300")
3508n/a
3509n/a dt = self.theclass.now(tz=f44m)
3510n/a self.assertIs(dt.tzinfo, f44m)
3511n/a # Replacing with degenerate tzinfo raises an exception.
3512n/a self.assertRaises(ValueError, dt.astimezone, fnone)
3513n/a # Replacing with same tzinfo makes no change.
3514n/a x = dt.astimezone(dt.tzinfo)
3515n/a self.assertIs(x.tzinfo, f44m)
3516n/a self.assertEqual(x.date(), dt.date())
3517n/a self.assertEqual(x.time(), dt.time())
3518n/a
3519n/a # Replacing with different tzinfo does adjust.
3520n/a got = dt.astimezone(fm5h)
3521n/a self.assertIs(got.tzinfo, fm5h)
3522n/a self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3523n/a expected = dt - dt.utcoffset() # in effect, convert to UTC
3524n/a expected += fm5h.utcoffset(dt) # and from there to local time
3525n/a expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3526n/a self.assertEqual(got.date(), expected.date())
3527n/a self.assertEqual(got.time(), expected.time())
3528n/a self.assertEqual(got.timetz(), expected.timetz())
3529n/a self.assertIs(got.tzinfo, expected.tzinfo)
3530n/a self.assertEqual(got, expected)
3531n/a
3532n/a @support.run_with_tz('UTC')
3533n/a def test_astimezone_default_utc(self):
3534n/a dt = self.theclass.now(timezone.utc)
3535n/a self.assertEqual(dt.astimezone(None), dt)
3536n/a self.assertEqual(dt.astimezone(), dt)
3537n/a
3538n/a # Note that offset in TZ variable has the opposite sign to that
3539n/a # produced by %z directive.
3540n/a @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
3541n/a def test_astimezone_default_eastern(self):
3542n/a dt = self.theclass(2012, 11, 4, 6, 30, tzinfo=timezone.utc)
3543n/a local = dt.astimezone()
3544n/a self.assertEqual(dt, local)
3545n/a self.assertEqual(local.strftime("%z %Z"), "-0500 EST")
3546n/a dt = self.theclass(2012, 11, 4, 5, 30, tzinfo=timezone.utc)
3547n/a local = dt.astimezone()
3548n/a self.assertEqual(dt, local)
3549n/a self.assertEqual(local.strftime("%z %Z"), "-0400 EDT")
3550n/a
3551n/a @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
3552n/a def test_astimezone_default_near_fold(self):
3553n/a # Issue #26616.
3554n/a u = datetime(2015, 11, 1, 5, tzinfo=timezone.utc)
3555n/a t = u.astimezone()
3556n/a s = t.astimezone()
3557n/a self.assertEqual(t.tzinfo, s.tzinfo)
3558n/a
3559n/a def test_aware_subtract(self):
3560n/a cls = self.theclass
3561n/a
3562n/a # Ensure that utcoffset() is ignored when the operands have the
3563n/a # same tzinfo member.
3564n/a class OperandDependentOffset(tzinfo):
3565n/a def utcoffset(self, t):
3566n/a if t.minute < 10:
3567n/a # d0 and d1 equal after adjustment
3568n/a return timedelta(minutes=t.minute)
3569n/a else:
3570n/a # d2 off in the weeds
3571n/a return timedelta(minutes=59)
3572n/a
3573n/a base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3574n/a d0 = base.replace(minute=3)
3575n/a d1 = base.replace(minute=9)
3576n/a d2 = base.replace(minute=11)
3577n/a for x in d0, d1, d2:
3578n/a for y in d0, d1, d2:
3579n/a got = x - y
3580n/a expected = timedelta(minutes=x.minute - y.minute)
3581n/a self.assertEqual(got, expected)
3582n/a
3583n/a # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3584n/a # ignored.
3585n/a base = cls(8, 9, 10, 11, 12, 13, 14)
3586n/a d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3587n/a d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3588n/a d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3589n/a for x in d0, d1, d2:
3590n/a for y in d0, d1, d2:
3591n/a got = x - y
3592n/a if (x is d0 or x is d1) and (y is d0 or y is d1):
3593n/a expected = timedelta(0)
3594n/a elif x is y is d2:
3595n/a expected = timedelta(0)
3596n/a elif x is d2:
3597n/a expected = timedelta(minutes=(11-59)-0)
3598n/a else:
3599n/a assert y is d2
3600n/a expected = timedelta(minutes=0-(11-59))
3601n/a self.assertEqual(got, expected)
3602n/a
3603n/a def test_mixed_compare(self):
3604n/a t1 = datetime(1, 2, 3, 4, 5, 6, 7)
3605n/a t2 = datetime(1, 2, 3, 4, 5, 6, 7)
3606n/a self.assertEqual(t1, t2)
3607n/a t2 = t2.replace(tzinfo=None)
3608n/a self.assertEqual(t1, t2)
3609n/a t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3610n/a self.assertEqual(t1, t2)
3611n/a t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3612n/a self.assertNotEqual(t1, t2)
3613n/a
3614n/a # In datetime w/ identical tzinfo objects, utcoffset is ignored.
3615n/a class Varies(tzinfo):
3616n/a def __init__(self):
3617n/a self.offset = timedelta(minutes=22)
3618n/a def utcoffset(self, t):
3619n/a self.offset += timedelta(minutes=1)
3620n/a return self.offset
3621n/a
3622n/a v = Varies()
3623n/a t1 = t2.replace(tzinfo=v)
3624n/a t2 = t2.replace(tzinfo=v)
3625n/a self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3626n/a self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3627n/a self.assertEqual(t1, t2)
3628n/a
3629n/a # But if they're not identical, it isn't ignored.
3630n/a t2 = t2.replace(tzinfo=Varies())
3631n/a self.assertTrue(t1 < t2) # t1's offset counter still going up
3632n/a
3633n/a def test_subclass_datetimetz(self):
3634n/a
3635n/a class C(self.theclass):
3636n/a theAnswer = 42
3637n/a
3638n/a def __new__(cls, *args, **kws):
3639n/a temp = kws.copy()
3640n/a extra = temp.pop('extra')
3641n/a result = self.theclass.__new__(cls, *args, **temp)
3642n/a result.extra = extra
3643n/a return result
3644n/a
3645n/a def newmeth(self, start):
3646n/a return start + self.hour + self.year
3647n/a
3648n/a args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3649n/a
3650n/a dt1 = self.theclass(*args)
3651n/a dt2 = C(*args, **{'extra': 7})
3652n/a
3653n/a self.assertEqual(dt2.__class__, C)
3654n/a self.assertEqual(dt2.theAnswer, 42)
3655n/a self.assertEqual(dt2.extra, 7)
3656n/a self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3657n/a self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3658n/a
3659n/a# Pain to set up DST-aware tzinfo classes.
3660n/a
3661n/adef first_sunday_on_or_after(dt):
3662n/a days_to_go = 6 - dt.weekday()
3663n/a if days_to_go:
3664n/a dt += timedelta(days_to_go)
3665n/a return dt
3666n/a
3667n/aZERO = timedelta(0)
3668n/aMINUTE = timedelta(minutes=1)
3669n/aHOUR = timedelta(hours=1)
3670n/aDAY = timedelta(days=1)
3671n/a# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3672n/aDSTSTART = datetime(1, 4, 1, 2)
3673n/a# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
3674n/a# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3675n/a# being standard time on that day, there is no spelling in local time of
3676n/a# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3677n/aDSTEND = datetime(1, 10, 25, 1)
3678n/a
3679n/aclass USTimeZone(tzinfo):
3680n/a
3681n/a def __init__(self, hours, reprname, stdname, dstname):
3682n/a self.stdoffset = timedelta(hours=hours)
3683n/a self.reprname = reprname
3684n/a self.stdname = stdname
3685n/a self.dstname = dstname
3686n/a
3687n/a def __repr__(self):
3688n/a return self.reprname
3689n/a
3690n/a def tzname(self, dt):
3691n/a if self.dst(dt):
3692n/a return self.dstname
3693n/a else:
3694n/a return self.stdname
3695n/a
3696n/a def utcoffset(self, dt):
3697n/a return self.stdoffset + self.dst(dt)
3698n/a
3699n/a def dst(self, dt):
3700n/a if dt is None or dt.tzinfo is None:
3701n/a # An exception instead may be sensible here, in one or more of
3702n/a # the cases.
3703n/a return ZERO
3704n/a assert dt.tzinfo is self
3705n/a
3706n/a # Find first Sunday in April.
3707n/a start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3708n/a assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3709n/a
3710n/a # Find last Sunday in October.
3711n/a end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3712n/a assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3713n/a
3714n/a # Can't compare naive to aware objects, so strip the timezone from
3715n/a # dt first.
3716n/a if start <= dt.replace(tzinfo=None) < end:
3717n/a return HOUR
3718n/a else:
3719n/a return ZERO
3720n/a
3721n/aEastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3722n/aCentral = USTimeZone(-6, "Central", "CST", "CDT")
3723n/aMountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3724n/aPacific = USTimeZone(-8, "Pacific", "PST", "PDT")
3725n/autc_real = FixedOffset(0, "UTC", 0)
3726n/a# For better test coverage, we want another flavor of UTC that's west of
3727n/a# the Eastern and Pacific timezones.
3728n/autc_fake = FixedOffset(-12*60, "UTCfake", 0)
3729n/a
3730n/aclass TestTimezoneConversions(unittest.TestCase):
3731n/a # The DST switch times for 2002, in std time.
3732n/a dston = datetime(2002, 4, 7, 2)
3733n/a dstoff = datetime(2002, 10, 27, 1)
3734n/a
3735n/a theclass = datetime
3736n/a
3737n/a # Check a time that's inside DST.
3738n/a def checkinside(self, dt, tz, utc, dston, dstoff):
3739n/a self.assertEqual(dt.dst(), HOUR)
3740n/a
3741n/a # Conversion to our own timezone is always an identity.
3742n/a self.assertEqual(dt.astimezone(tz), dt)
3743n/a
3744n/a asutc = dt.astimezone(utc)
3745n/a there_and_back = asutc.astimezone(tz)
3746n/a
3747n/a # Conversion to UTC and back isn't always an identity here,
3748n/a # because there are redundant spellings (in local time) of
3749n/a # UTC time when DST begins: the clock jumps from 1:59:59
3750n/a # to 3:00:00, and a local time of 2:MM:SS doesn't really
3751n/a # make sense then. The classes above treat 2:MM:SS as
3752n/a # daylight time then (it's "after 2am"), really an alias
3753n/a # for 1:MM:SS standard time. The latter form is what
3754n/a # conversion back from UTC produces.
3755n/a if dt.date() == dston.date() and dt.hour == 2:
3756n/a # We're in the redundant hour, and coming back from
3757n/a # UTC gives the 1:MM:SS standard-time spelling.
3758n/a self.assertEqual(there_and_back + HOUR, dt)
3759n/a # Although during was considered to be in daylight
3760n/a # time, there_and_back is not.
3761n/a self.assertEqual(there_and_back.dst(), ZERO)
3762n/a # They're the same times in UTC.
3763n/a self.assertEqual(there_and_back.astimezone(utc),
3764n/a dt.astimezone(utc))
3765n/a else:
3766n/a # We're not in the redundant hour.
3767n/a self.assertEqual(dt, there_and_back)
3768n/a
3769n/a # Because we have a redundant spelling when DST begins, there is
3770n/a # (unfortunately) an hour when DST ends that can't be spelled at all in
3771n/a # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3772n/a # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3773n/a # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3774n/a # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3775n/a # expressed in local time. Nevertheless, we want conversion back
3776n/a # from UTC to mimic the local clock's "repeat an hour" behavior.
3777n/a nexthour_utc = asutc + HOUR
3778n/a nexthour_tz = nexthour_utc.astimezone(tz)
3779n/a if dt.date() == dstoff.date() and dt.hour == 0:
3780n/a # We're in the hour before the last DST hour. The last DST hour
3781n/a # is ineffable. We want the conversion back to repeat 1:MM.
3782n/a self.assertEqual(nexthour_tz, dt.replace(hour=1))
3783n/a nexthour_utc += HOUR
3784n/a nexthour_tz = nexthour_utc.astimezone(tz)
3785n/a self.assertEqual(nexthour_tz, dt.replace(hour=1))
3786n/a else:
3787n/a self.assertEqual(nexthour_tz - dt, HOUR)
3788n/a
3789n/a # Check a time that's outside DST.
3790n/a def checkoutside(self, dt, tz, utc):
3791n/a self.assertEqual(dt.dst(), ZERO)
3792n/a
3793n/a # Conversion to our own timezone is always an identity.
3794n/a self.assertEqual(dt.astimezone(tz), dt)
3795n/a
3796n/a # Converting to UTC and back is an identity too.
3797n/a asutc = dt.astimezone(utc)
3798n/a there_and_back = asutc.astimezone(tz)
3799n/a self.assertEqual(dt, there_and_back)
3800n/a
3801n/a def convert_between_tz_and_utc(self, tz, utc):
3802n/a dston = self.dston.replace(tzinfo=tz)
3803n/a # Because 1:MM on the day DST ends is taken as being standard time,
3804n/a # there is no spelling in tz for the last hour of daylight time.
3805n/a # For purposes of the test, the last hour of DST is 0:MM, which is
3806n/a # taken as being daylight time (and 1:MM is taken as being standard
3807n/a # time).
3808n/a dstoff = self.dstoff.replace(tzinfo=tz)
3809n/a for delta in (timedelta(weeks=13),
3810n/a DAY,
3811n/a HOUR,
3812n/a timedelta(minutes=1),
3813n/a timedelta(microseconds=1)):
3814n/a
3815n/a self.checkinside(dston, tz, utc, dston, dstoff)
3816n/a for during in dston + delta, dstoff - delta:
3817n/a self.checkinside(during, tz, utc, dston, dstoff)
3818n/a
3819n/a self.checkoutside(dstoff, tz, utc)
3820n/a for outside in dston - delta, dstoff + delta:
3821n/a self.checkoutside(outside, tz, utc)
3822n/a
3823n/a def test_easy(self):
3824n/a # Despite the name of this test, the endcases are excruciating.
3825n/a self.convert_between_tz_and_utc(Eastern, utc_real)
3826n/a self.convert_between_tz_and_utc(Pacific, utc_real)
3827n/a self.convert_between_tz_and_utc(Eastern, utc_fake)
3828n/a self.convert_between_tz_and_utc(Pacific, utc_fake)
3829n/a # The next is really dancing near the edge. It works because
3830n/a # Pacific and Eastern are far enough apart that their "problem
3831n/a # hours" don't overlap.
3832n/a self.convert_between_tz_and_utc(Eastern, Pacific)
3833n/a self.convert_between_tz_and_utc(Pacific, Eastern)
3834n/a # OTOH, these fail! Don't enable them. The difficulty is that
3835n/a # the edge case tests assume that every hour is representable in
3836n/a # the "utc" class. This is always true for a fixed-offset tzinfo
3837n/a # class (lke utc_real and utc_fake), but not for Eastern or Central.
3838n/a # For these adjacent DST-aware time zones, the range of time offsets
3839n/a # tested ends up creating hours in the one that aren't representable
3840n/a # in the other. For the same reason, we would see failures in the
3841n/a # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3842n/a # offset deltas in convert_between_tz_and_utc().
3843n/a #
3844n/a # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3845n/a # self.convert_between_tz_and_utc(Central, Eastern) # can't work
3846n/a
3847n/a def test_tricky(self):
3848n/a # 22:00 on day before daylight starts.
3849n/a fourback = self.dston - timedelta(hours=4)
3850n/a ninewest = FixedOffset(-9*60, "-0900", 0)
3851n/a fourback = fourback.replace(tzinfo=ninewest)
3852n/a # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3853n/a # 2", we should get the 3 spelling.
3854n/a # If we plug 22:00 the day before into Eastern, it "looks like std
3855n/a # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3856n/a # to 22:00 lands on 2:00, which makes no sense in local time (the
3857n/a # local clock jumps from 1 to 3). The point here is to make sure we
3858n/a # get the 3 spelling.
3859n/a expected = self.dston.replace(hour=3)
3860n/a got = fourback.astimezone(Eastern).replace(tzinfo=None)
3861n/a self.assertEqual(expected, got)
3862n/a
3863n/a # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3864n/a # case we want the 1:00 spelling.
3865n/a sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
3866n/a # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3867n/a # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3868n/a # spelling.
3869n/a expected = self.dston.replace(hour=1)
3870n/a got = sixutc.astimezone(Eastern).replace(tzinfo=None)
3871n/a self.assertEqual(expected, got)
3872n/a
3873n/a # Now on the day DST ends, we want "repeat an hour" behavior.
3874n/a # UTC 4:MM 5:MM 6:MM 7:MM checking these
3875n/a # EST 23:MM 0:MM 1:MM 2:MM
3876n/a # EDT 0:MM 1:MM 2:MM 3:MM
3877n/a # wall 0:MM 1:MM 1:MM 2:MM against these
3878n/a for utc in utc_real, utc_fake:
3879n/a for tz in Eastern, Pacific:
3880n/a first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
3881n/a # Convert that to UTC.
3882n/a first_std_hour -= tz.utcoffset(None)
3883n/a # Adjust for possibly fake UTC.
3884n/a asutc = first_std_hour + utc.utcoffset(None)
3885n/a # First UTC hour to convert; this is 4:00 when utc=utc_real &
3886n/a # tz=Eastern.
3887n/a asutcbase = asutc.replace(tzinfo=utc)
3888n/a for tzhour in (0, 1, 1, 2):
3889n/a expectedbase = self.dstoff.replace(hour=tzhour)
3890n/a for minute in 0, 30, 59:
3891n/a expected = expectedbase.replace(minute=minute)
3892n/a asutc = asutcbase.replace(minute=minute)
3893n/a astz = asutc.astimezone(tz)
3894n/a self.assertEqual(astz.replace(tzinfo=None), expected)
3895n/a asutcbase += HOUR
3896n/a
3897n/a
3898n/a def test_bogus_dst(self):
3899n/a class ok(tzinfo):
3900n/a def utcoffset(self, dt): return HOUR
3901n/a def dst(self, dt): return HOUR
3902n/a
3903n/a now = self.theclass.now().replace(tzinfo=utc_real)
3904n/a # Doesn't blow up.
3905n/a now.astimezone(ok())
3906n/a
3907n/a # Does blow up.
3908n/a class notok(ok):
3909n/a def dst(self, dt): return None
3910n/a self.assertRaises(ValueError, now.astimezone, notok())
3911n/a
3912n/a # Sometimes blow up. In the following, tzinfo.dst()
3913n/a # implementation may return None or not None depending on
3914n/a # whether DST is assumed to be in effect. In this situation,
3915n/a # a ValueError should be raised by astimezone().
3916n/a class tricky_notok(ok):
3917n/a def dst(self, dt):
3918n/a if dt.year == 2000:
3919n/a return None
3920n/a else:
3921n/a return 10*HOUR
3922n/a dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3923n/a self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3924n/a
3925n/a def test_fromutc(self):
3926n/a self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3927n/a now = datetime.utcnow().replace(tzinfo=utc_real)
3928n/a self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3929n/a now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3930n/a enow = Eastern.fromutc(now) # doesn't blow up
3931n/a self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3932n/a self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3933n/a self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3934n/a
3935n/a # Always converts UTC to standard time.
3936n/a class FauxUSTimeZone(USTimeZone):
3937n/a def fromutc(self, dt):
3938n/a return dt + self.stdoffset
3939n/a FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3940n/a
3941n/a # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3942n/a # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3943n/a # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3944n/a
3945n/a # Check around DST start.
3946n/a start = self.dston.replace(hour=4, tzinfo=Eastern)
3947n/a fstart = start.replace(tzinfo=FEastern)
3948n/a for wall in 23, 0, 1, 3, 4, 5:
3949n/a expected = start.replace(hour=wall)
3950n/a if wall == 23:
3951n/a expected -= timedelta(days=1)
3952n/a got = Eastern.fromutc(start)
3953n/a self.assertEqual(expected, got)
3954n/a
3955n/a expected = fstart + FEastern.stdoffset
3956n/a got = FEastern.fromutc(fstart)
3957n/a self.assertEqual(expected, got)
3958n/a
3959n/a # Ensure astimezone() calls fromutc() too.
3960n/a got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3961n/a self.assertEqual(expected, got)
3962n/a
3963n/a start += HOUR
3964n/a fstart += HOUR
3965n/a
3966n/a # Check around DST end.
3967n/a start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3968n/a fstart = start.replace(tzinfo=FEastern)
3969n/a for wall in 0, 1, 1, 2, 3, 4:
3970n/a expected = start.replace(hour=wall)
3971n/a got = Eastern.fromutc(start)
3972n/a self.assertEqual(expected, got)
3973n/a
3974n/a expected = fstart + FEastern.stdoffset
3975n/a got = FEastern.fromutc(fstart)
3976n/a self.assertEqual(expected, got)
3977n/a
3978n/a # Ensure astimezone() calls fromutc() too.
3979n/a got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3980n/a self.assertEqual(expected, got)
3981n/a
3982n/a start += HOUR
3983n/a fstart += HOUR
3984n/a
3985n/a
3986n/a#############################################################################
3987n/a# oddballs
3988n/a
3989n/aclass Oddballs(unittest.TestCase):
3990n/a
3991n/a def test_bug_1028306(self):
3992n/a # Trying to compare a date to a datetime should act like a mixed-
3993n/a # type comparison, despite that datetime is a subclass of date.
3994n/a as_date = date.today()
3995n/a as_datetime = datetime.combine(as_date, time())
3996n/a self.assertTrue(as_date != as_datetime)
3997n/a self.assertTrue(as_datetime != as_date)
3998n/a self.assertFalse(as_date == as_datetime)
3999n/a self.assertFalse(as_datetime == as_date)
4000n/a self.assertRaises(TypeError, lambda: as_date < as_datetime)
4001n/a self.assertRaises(TypeError, lambda: as_datetime < as_date)
4002n/a self.assertRaises(TypeError, lambda: as_date <= as_datetime)
4003n/a self.assertRaises(TypeError, lambda: as_datetime <= as_date)
4004n/a self.assertRaises(TypeError, lambda: as_date > as_datetime)
4005n/a self.assertRaises(TypeError, lambda: as_datetime > as_date)
4006n/a self.assertRaises(TypeError, lambda: as_date >= as_datetime)
4007n/a self.assertRaises(TypeError, lambda: as_datetime >= as_date)
4008n/a
4009n/a # Nevertheless, comparison should work with the base-class (date)
4010n/a # projection if use of a date method is forced.
4011n/a self.assertEqual(as_date.__eq__(as_datetime), True)
4012n/a different_day = (as_date.day + 1) % 20 + 1
4013n/a as_different = as_datetime.replace(day= different_day)
4014n/a self.assertEqual(as_date.__eq__(as_different), False)
4015n/a
4016n/a # And date should compare with other subclasses of date. If a
4017n/a # subclass wants to stop this, it's up to the subclass to do so.
4018n/a date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
4019n/a self.assertEqual(as_date, date_sc)
4020n/a self.assertEqual(date_sc, as_date)
4021n/a
4022n/a # Ditto for datetimes.
4023n/a datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
4024n/a as_date.day, 0, 0, 0)
4025n/a self.assertEqual(as_datetime, datetime_sc)
4026n/a self.assertEqual(datetime_sc, as_datetime)
4027n/a
4028n/a def test_extra_attributes(self):
4029n/a for x in [date.today(),
4030n/a time(),
4031n/a datetime.utcnow(),
4032n/a timedelta(),
4033n/a tzinfo(),
4034n/a timezone(timedelta())]:
4035n/a with self.assertRaises(AttributeError):
4036n/a x.abc = 1
4037n/a
4038n/a def test_check_arg_types(self):
4039n/a class Number:
4040n/a def __init__(self, value):
4041n/a self.value = value
4042n/a def __int__(self):
4043n/a return self.value
4044n/a
4045n/a for xx in [decimal.Decimal(10),
4046n/a decimal.Decimal('10.9'),
4047n/a Number(10)]:
4048n/a self.assertEqual(datetime(10, 10, 10, 10, 10, 10, 10),
4049n/a datetime(xx, xx, xx, xx, xx, xx, xx))
4050n/a
4051n/a with self.assertRaisesRegex(TypeError, '^an integer is required '
4052n/a r'\(got type str\)$'):
4053n/a datetime(10, 10, '10')
4054n/a
4055n/a f10 = Number(10.9)
4056n/a with self.assertRaisesRegex(TypeError, '^__int__ returned non-int '
4057n/a r'\(type float\)$'):
4058n/a datetime(10, 10, f10)
4059n/a
4060n/a class Float(float):
4061n/a pass
4062n/a s10 = Float(10.9)
4063n/a with self.assertRaisesRegex(TypeError, '^integer argument expected, '
4064n/a 'got float$'):
4065n/a datetime(10, 10, s10)
4066n/a
4067n/a with self.assertRaises(TypeError):
4068n/a datetime(10., 10, 10)
4069n/a with self.assertRaises(TypeError):
4070n/a datetime(10, 10., 10)
4071n/a with self.assertRaises(TypeError):
4072n/a datetime(10, 10, 10.)
4073n/a with self.assertRaises(TypeError):
4074n/a datetime(10, 10, 10, 10.)
4075n/a with self.assertRaises(TypeError):
4076n/a datetime(10, 10, 10, 10, 10.)
4077n/a with self.assertRaises(TypeError):
4078n/a datetime(10, 10, 10, 10, 10, 10.)
4079n/a with self.assertRaises(TypeError):
4080n/a datetime(10, 10, 10, 10, 10, 10, 10.)
4081n/a
4082n/a#############################################################################
4083n/a# Local Time Disambiguation
4084n/a
4085n/a# An experimental reimplementation of fromutc that respects the "fold" flag.
4086n/a
4087n/aclass tzinfo2(tzinfo):
4088n/a
4089n/a def fromutc(self, dt):
4090n/a "datetime in UTC -> datetime in local time."
4091n/a
4092n/a if not isinstance(dt, datetime):
4093n/a raise TypeError("fromutc() requires a datetime argument")
4094n/a if dt.tzinfo is not self:
4095n/a raise ValueError("dt.tzinfo is not self")
4096n/a # Returned value satisfies
4097n/a # dt + ldt.utcoffset() = ldt
4098n/a off0 = dt.replace(fold=0).utcoffset()
4099n/a off1 = dt.replace(fold=1).utcoffset()
4100n/a if off0 is None or off1 is None or dt.dst() is None:
4101n/a raise ValueError
4102n/a if off0 == off1:
4103n/a ldt = dt + off0
4104n/a off1 = ldt.utcoffset()
4105n/a if off0 == off1:
4106n/a return ldt
4107n/a # Now, we discovered both possible offsets, so
4108n/a # we can just try four possible solutions:
4109n/a for off in [off0, off1]:
4110n/a ldt = dt + off
4111n/a if ldt.utcoffset() == off:
4112n/a return ldt
4113n/a ldt = ldt.replace(fold=1)
4114n/a if ldt.utcoffset() == off:
4115n/a return ldt
4116n/a
4117n/a raise ValueError("No suitable local time found")
4118n/a
4119n/a# Reimplementing simplified US timezones to respect the "fold" flag:
4120n/a
4121n/aclass USTimeZone2(tzinfo2):
4122n/a
4123n/a def __init__(self, hours, reprname, stdname, dstname):
4124n/a self.stdoffset = timedelta(hours=hours)
4125n/a self.reprname = reprname
4126n/a self.stdname = stdname
4127n/a self.dstname = dstname
4128n/a
4129n/a def __repr__(self):
4130n/a return self.reprname
4131n/a
4132n/a def tzname(self, dt):
4133n/a if self.dst(dt):
4134n/a return self.dstname
4135n/a else:
4136n/a return self.stdname
4137n/a
4138n/a def utcoffset(self, dt):
4139n/a return self.stdoffset + self.dst(dt)
4140n/a
4141n/a def dst(self, dt):
4142n/a if dt is None or dt.tzinfo is None:
4143n/a # An exception instead may be sensible here, in one or more of
4144n/a # the cases.
4145n/a return ZERO
4146n/a assert dt.tzinfo is self
4147n/a
4148n/a # Find first Sunday in April.
4149n/a start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
4150n/a assert start.weekday() == 6 and start.month == 4 and start.day <= 7
4151n/a
4152n/a # Find last Sunday in October.
4153n/a end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
4154n/a assert end.weekday() == 6 and end.month == 10 and end.day >= 25
4155n/a
4156n/a # Can't compare naive to aware objects, so strip the timezone from
4157n/a # dt first.
4158n/a dt = dt.replace(tzinfo=None)
4159n/a if start + HOUR <= dt < end:
4160n/a # DST is in effect.
4161n/a return HOUR
4162n/a elif end <= dt < end + HOUR:
4163n/a # Fold (an ambiguous hour): use dt.fold to disambiguate.
4164n/a return ZERO if dt.fold else HOUR
4165n/a elif start <= dt < start + HOUR:
4166n/a # Gap (a non-existent hour): reverse the fold rule.
4167n/a return HOUR if dt.fold else ZERO
4168n/a else:
4169n/a # DST is off.
4170n/a return ZERO
4171n/a
4172n/aEastern2 = USTimeZone2(-5, "Eastern2", "EST", "EDT")
4173n/aCentral2 = USTimeZone2(-6, "Central2", "CST", "CDT")
4174n/aMountain2 = USTimeZone2(-7, "Mountain2", "MST", "MDT")
4175n/aPacific2 = USTimeZone2(-8, "Pacific2", "PST", "PDT")
4176n/a
4177n/a# Europe_Vilnius_1941 tzinfo implementation reproduces the following
4178n/a# 1941 transition from Olson's tzdist:
4179n/a#
4180n/a# Zone NAME GMTOFF RULES FORMAT [UNTIL]
4181n/a# ZoneEurope/Vilnius 1:00 - CET 1940 Aug 3
4182n/a# 3:00 - MSK 1941 Jun 24
4183n/a# 1:00 C-Eur CE%sT 1944 Aug
4184n/a#
4185n/a# $ zdump -v Europe/Vilnius | grep 1941
4186n/a# Europe/Vilnius Mon Jun 23 20:59:59 1941 UTC = Mon Jun 23 23:59:59 1941 MSK isdst=0 gmtoff=10800
4187n/a# Europe/Vilnius Mon Jun 23 21:00:00 1941 UTC = Mon Jun 23 23:00:00 1941 CEST isdst=1 gmtoff=7200
4188n/a
4189n/aclass Europe_Vilnius_1941(tzinfo):
4190n/a def _utc_fold(self):
4191n/a return [datetime(1941, 6, 23, 21, tzinfo=self), # Mon Jun 23 21:00:00 1941 UTC
4192n/a datetime(1941, 6, 23, 22, tzinfo=self)] # Mon Jun 23 22:00:00 1941 UTC
4193n/a
4194n/a def _loc_fold(self):
4195n/a return [datetime(1941, 6, 23, 23, tzinfo=self), # Mon Jun 23 23:00:00 1941 MSK / CEST
4196n/a datetime(1941, 6, 24, 0, tzinfo=self)] # Mon Jun 24 00:00:00 1941 CEST
4197n/a
4198n/a def utcoffset(self, dt):
4199n/a fold_start, fold_stop = self._loc_fold()
4200n/a if dt < fold_start:
4201n/a return 3 * HOUR
4202n/a if dt < fold_stop:
4203n/a return (2 if dt.fold else 3) * HOUR
4204n/a # if dt >= fold_stop
4205n/a return 2 * HOUR
4206n/a
4207n/a def dst(self, dt):
4208n/a fold_start, fold_stop = self._loc_fold()
4209n/a if dt < fold_start:
4210n/a return 0 * HOUR
4211n/a if dt < fold_stop:
4212n/a return (1 if dt.fold else 0) * HOUR
4213n/a # if dt >= fold_stop
4214n/a return 1 * HOUR
4215n/a
4216n/a def tzname(self, dt):
4217n/a fold_start, fold_stop = self._loc_fold()
4218n/a if dt < fold_start:
4219n/a return 'MSK'
4220n/a if dt < fold_stop:
4221n/a return ('MSK', 'CEST')[dt.fold]
4222n/a # if dt >= fold_stop
4223n/a return 'CEST'
4224n/a
4225n/a def fromutc(self, dt):
4226n/a assert dt.fold == 0
4227n/a assert dt.tzinfo is self
4228n/a if dt.year != 1941:
4229n/a raise NotImplementedError
4230n/a fold_start, fold_stop = self._utc_fold()
4231n/a if dt < fold_start:
4232n/a return dt + 3 * HOUR
4233n/a if dt < fold_stop:
4234n/a return (dt + 2 * HOUR).replace(fold=1)
4235n/a # if dt >= fold_stop
4236n/a return dt + 2 * HOUR
4237n/a
4238n/a
4239n/aclass TestLocalTimeDisambiguation(unittest.TestCase):
4240n/a
4241n/a def test_vilnius_1941_fromutc(self):
4242n/a Vilnius = Europe_Vilnius_1941()
4243n/a
4244n/a gdt = datetime(1941, 6, 23, 20, 59, 59, tzinfo=timezone.utc)
4245n/a ldt = gdt.astimezone(Vilnius)
4246n/a self.assertEqual(ldt.strftime("%c %Z%z"),
4247n/a 'Mon Jun 23 23:59:59 1941 MSK+0300')
4248n/a self.assertEqual(ldt.fold, 0)
4249n/a self.assertFalse(ldt.dst())
4250n/a
4251n/a gdt = datetime(1941, 6, 23, 21, tzinfo=timezone.utc)
4252n/a ldt = gdt.astimezone(Vilnius)
4253n/a self.assertEqual(ldt.strftime("%c %Z%z"),
4254n/a 'Mon Jun 23 23:00:00 1941 CEST+0200')
4255n/a self.assertEqual(ldt.fold, 1)
4256n/a self.assertTrue(ldt.dst())
4257n/a
4258n/a gdt = datetime(1941, 6, 23, 22, tzinfo=timezone.utc)
4259n/a ldt = gdt.astimezone(Vilnius)
4260n/a self.assertEqual(ldt.strftime("%c %Z%z"),
4261n/a 'Tue Jun 24 00:00:00 1941 CEST+0200')
4262n/a self.assertEqual(ldt.fold, 0)
4263n/a self.assertTrue(ldt.dst())
4264n/a
4265n/a def test_vilnius_1941_toutc(self):
4266n/a Vilnius = Europe_Vilnius_1941()
4267n/a
4268n/a ldt = datetime(1941, 6, 23, 22, 59, 59, tzinfo=Vilnius)
4269n/a gdt = ldt.astimezone(timezone.utc)
4270n/a self.assertEqual(gdt.strftime("%c %Z"),
4271n/a 'Mon Jun 23 19:59:59 1941 UTC')
4272n/a
4273n/a ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius)
4274n/a gdt = ldt.astimezone(timezone.utc)
4275n/a self.assertEqual(gdt.strftime("%c %Z"),
4276n/a 'Mon Jun 23 20:59:59 1941 UTC')
4277n/a
4278n/a ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius, fold=1)
4279n/a gdt = ldt.astimezone(timezone.utc)
4280n/a self.assertEqual(gdt.strftime("%c %Z"),
4281n/a 'Mon Jun 23 21:59:59 1941 UTC')
4282n/a
4283n/a ldt = datetime(1941, 6, 24, 0, tzinfo=Vilnius)
4284n/a gdt = ldt.astimezone(timezone.utc)
4285n/a self.assertEqual(gdt.strftime("%c %Z"),
4286n/a 'Mon Jun 23 22:00:00 1941 UTC')
4287n/a
4288n/a
4289n/a def test_constructors(self):
4290n/a t = time(0, fold=1)
4291n/a dt = datetime(1, 1, 1, fold=1)
4292n/a self.assertEqual(t.fold, 1)
4293n/a self.assertEqual(dt.fold, 1)
4294n/a with self.assertRaises(TypeError):
4295n/a time(0, 0, 0, 0, None, 0)
4296n/a
4297n/a def test_member(self):
4298n/a dt = datetime(1, 1, 1, fold=1)
4299n/a t = dt.time()
4300n/a self.assertEqual(t.fold, 1)
4301n/a t = dt.timetz()
4302n/a self.assertEqual(t.fold, 1)
4303n/a
4304n/a def test_replace(self):
4305n/a t = time(0)
4306n/a dt = datetime(1, 1, 1)
4307n/a self.assertEqual(t.replace(fold=1).fold, 1)
4308n/a self.assertEqual(dt.replace(fold=1).fold, 1)
4309n/a self.assertEqual(t.replace(fold=0).fold, 0)
4310n/a self.assertEqual(dt.replace(fold=0).fold, 0)
4311n/a # Check that replacement of other fields does not change "fold".
4312n/a t = t.replace(fold=1, tzinfo=Eastern)
4313n/a dt = dt.replace(fold=1, tzinfo=Eastern)
4314n/a self.assertEqual(t.replace(tzinfo=None).fold, 1)
4315n/a self.assertEqual(dt.replace(tzinfo=None).fold, 1)
4316n/a # Check that fold is a keyword-only argument
4317n/a with self.assertRaises(TypeError):
4318n/a t.replace(1, 1, 1, None, 1)
4319n/a with self.assertRaises(TypeError):
4320n/a dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1)
4321n/a
4322n/a def test_comparison(self):
4323n/a t = time(0)
4324n/a dt = datetime(1, 1, 1)
4325n/a self.assertEqual(t, t.replace(fold=1))
4326n/a self.assertEqual(dt, dt.replace(fold=1))
4327n/a
4328n/a def test_hash(self):
4329n/a t = time(0)
4330n/a dt = datetime(1, 1, 1)
4331n/a self.assertEqual(hash(t), hash(t.replace(fold=1)))
4332n/a self.assertEqual(hash(dt), hash(dt.replace(fold=1)))
4333n/a
4334n/a @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
4335n/a def test_fromtimestamp(self):
4336n/a s = 1414906200
4337n/a dt0 = datetime.fromtimestamp(s)
4338n/a dt1 = datetime.fromtimestamp(s + 3600)
4339n/a self.assertEqual(dt0.fold, 0)
4340n/a self.assertEqual(dt1.fold, 1)
4341n/a
4342n/a @support.run_with_tz('Australia/Lord_Howe')
4343n/a def test_fromtimestamp_lord_howe(self):
4344n/a tm = _time.localtime(1.4e9)
4345n/a if _time.strftime('%Z%z', tm) != 'LHST+1030':
4346n/a self.skipTest('Australia/Lord_Howe timezone is not supported on this platform')
4347n/a # $ TZ=Australia/Lord_Howe date -r 1428158700
4348n/a # Sun Apr 5 01:45:00 LHDT 2015
4349n/a # $ TZ=Australia/Lord_Howe date -r 1428160500
4350n/a # Sun Apr 5 01:45:00 LHST 2015
4351n/a s = 1428158700
4352n/a t0 = datetime.fromtimestamp(s)
4353n/a t1 = datetime.fromtimestamp(s + 1800)
4354n/a self.assertEqual(t0, t1)
4355n/a self.assertEqual(t0.fold, 0)
4356n/a self.assertEqual(t1.fold, 1)
4357n/a
4358n/a
4359n/a @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
4360n/a def test_timestamp(self):
4361n/a dt0 = datetime(2014, 11, 2, 1, 30)
4362n/a dt1 = dt0.replace(fold=1)
4363n/a self.assertEqual(dt0.timestamp() + 3600,
4364n/a dt1.timestamp())
4365n/a
4366n/a @support.run_with_tz('Australia/Lord_Howe')
4367n/a def test_timestamp_lord_howe(self):
4368n/a tm = _time.localtime(1.4e9)
4369n/a if _time.strftime('%Z%z', tm) != 'LHST+1030':
4370n/a self.skipTest('Australia/Lord_Howe timezone is not supported on this platform')
4371n/a t = datetime(2015, 4, 5, 1, 45)
4372n/a s0 = t.replace(fold=0).timestamp()
4373n/a s1 = t.replace(fold=1).timestamp()
4374n/a self.assertEqual(s0 + 1800, s1)
4375n/a
4376n/a
4377n/a @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
4378n/a def test_astimezone(self):
4379n/a dt0 = datetime(2014, 11, 2, 1, 30)
4380n/a dt1 = dt0.replace(fold=1)
4381n/a # Convert both naive instances to aware.
4382n/a adt0 = dt0.astimezone()
4383n/a adt1 = dt1.astimezone()
4384n/a # Check that the first instance in DST zone and the second in STD
4385n/a self.assertEqual(adt0.tzname(), 'EDT')
4386n/a self.assertEqual(adt1.tzname(), 'EST')
4387n/a self.assertEqual(adt0 + HOUR, adt1)
4388n/a # Aware instances with fixed offset tzinfo's always have fold=0
4389n/a self.assertEqual(adt0.fold, 0)
4390n/a self.assertEqual(adt1.fold, 0)
4391n/a
4392n/a
4393n/a def test_pickle_fold(self):
4394n/a t = time(fold=1)
4395n/a dt = datetime(1, 1, 1, fold=1)
4396n/a for pickler, unpickler, proto in pickle_choices:
4397n/a for x in [t, dt]:
4398n/a s = pickler.dumps(x, proto)
4399n/a y = unpickler.loads(s)
4400n/a self.assertEqual(x, y)
4401n/a self.assertEqual((0 if proto < 4 else x.fold), y.fold)
4402n/a
4403n/a def test_repr(self):
4404n/a t = time(fold=1)
4405n/a dt = datetime(1, 1, 1, fold=1)
4406n/a self.assertEqual(repr(t), 'datetime.time(0, 0, fold=1)')
4407n/a self.assertEqual(repr(dt),
4408n/a 'datetime.datetime(1, 1, 1, 0, 0, fold=1)')
4409n/a
4410n/a def test_dst(self):
4411n/a # Let's first establish that things work in regular times.
4412n/a dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution
4413n/a dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2)
4414n/a self.assertEqual(dt_summer.dst(), HOUR)
4415n/a self.assertEqual(dt_winter.dst(), ZERO)
4416n/a # The disambiguation flag is ignored
4417n/a self.assertEqual(dt_summer.replace(fold=1).dst(), HOUR)
4418n/a self.assertEqual(dt_winter.replace(fold=1).dst(), ZERO)
4419n/a
4420n/a # Pick local time in the fold.
4421n/a for minute in [0, 30, 59]:
4422n/a dt = datetime(2002, 10, 27, 1, minute, tzinfo=Eastern2)
4423n/a # With fold=0 (the default) it is in DST.
4424n/a self.assertEqual(dt.dst(), HOUR)
4425n/a # With fold=1 it is in STD.
4426n/a self.assertEqual(dt.replace(fold=1).dst(), ZERO)
4427n/a
4428n/a # Pick local time in the gap.
4429n/a for minute in [0, 30, 59]:
4430n/a dt = datetime(2002, 4, 7, 2, minute, tzinfo=Eastern2)
4431n/a # With fold=0 (the default) it is in STD.
4432n/a self.assertEqual(dt.dst(), ZERO)
4433n/a # With fold=1 it is in DST.
4434n/a self.assertEqual(dt.replace(fold=1).dst(), HOUR)
4435n/a
4436n/a
4437n/a def test_utcoffset(self):
4438n/a # Let's first establish that things work in regular times.
4439n/a dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution
4440n/a dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2)
4441n/a self.assertEqual(dt_summer.utcoffset(), -4 * HOUR)
4442n/a self.assertEqual(dt_winter.utcoffset(), -5 * HOUR)
4443n/a # The disambiguation flag is ignored
4444n/a self.assertEqual(dt_summer.replace(fold=1).utcoffset(), -4 * HOUR)
4445n/a self.assertEqual(dt_winter.replace(fold=1).utcoffset(), -5 * HOUR)
4446n/a
4447n/a def test_fromutc(self):
4448n/a # Let's first establish that things work in regular times.
4449n/a u_summer = datetime(2002, 10, 27, 6, tzinfo=Eastern2) - timedelta.resolution
4450n/a u_winter = datetime(2002, 10, 27, 7, tzinfo=Eastern2)
4451n/a t_summer = Eastern2.fromutc(u_summer)
4452n/a t_winter = Eastern2.fromutc(u_winter)
4453n/a self.assertEqual(t_summer, u_summer - 4 * HOUR)
4454n/a self.assertEqual(t_winter, u_winter - 5 * HOUR)
4455n/a self.assertEqual(t_summer.fold, 0)
4456n/a self.assertEqual(t_winter.fold, 0)
4457n/a
4458n/a # What happens in the fall-back fold?
4459n/a u = datetime(2002, 10, 27, 5, 30, tzinfo=Eastern2)
4460n/a t0 = Eastern2.fromutc(u)
4461n/a u += HOUR
4462n/a t1 = Eastern2.fromutc(u)
4463n/a self.assertEqual(t0, t1)
4464n/a self.assertEqual(t0.fold, 0)
4465n/a self.assertEqual(t1.fold, 1)
4466n/a # The tricky part is when u is in the local fold:
4467n/a u = datetime(2002, 10, 27, 1, 30, tzinfo=Eastern2)
4468n/a t = Eastern2.fromutc(u)
4469n/a self.assertEqual((t.day, t.hour), (26, 21))
4470n/a # .. or gets into the local fold after a standard time adjustment
4471n/a u = datetime(2002, 10, 27, 6, 30, tzinfo=Eastern2)
4472n/a t = Eastern2.fromutc(u)
4473n/a self.assertEqual((t.day, t.hour), (27, 1))
4474n/a
4475n/a # What happens in the spring-forward gap?
4476n/a u = datetime(2002, 4, 7, 2, 0, tzinfo=Eastern2)
4477n/a t = Eastern2.fromutc(u)
4478n/a self.assertEqual((t.day, t.hour), (6, 21))
4479n/a
4480n/a def test_mixed_compare_regular(self):
4481n/a t = datetime(2000, 1, 1, tzinfo=Eastern2)
4482n/a self.assertEqual(t, t.astimezone(timezone.utc))
4483n/a t = datetime(2000, 6, 1, tzinfo=Eastern2)
4484n/a self.assertEqual(t, t.astimezone(timezone.utc))
4485n/a
4486n/a def test_mixed_compare_fold(self):
4487n/a t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2)
4488n/a t_fold_utc = t_fold.astimezone(timezone.utc)
4489n/a self.assertNotEqual(t_fold, t_fold_utc)
4490n/a
4491n/a def test_mixed_compare_gap(self):
4492n/a t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2)
4493n/a t_gap_utc = t_gap.astimezone(timezone.utc)
4494n/a self.assertNotEqual(t_gap, t_gap_utc)
4495n/a
4496n/a def test_hash_aware(self):
4497n/a t = datetime(2000, 1, 1, tzinfo=Eastern2)
4498n/a self.assertEqual(hash(t), hash(t.replace(fold=1)))
4499n/a t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2)
4500n/a t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2)
4501n/a self.assertEqual(hash(t_fold), hash(t_fold.replace(fold=1)))
4502n/a self.assertEqual(hash(t_gap), hash(t_gap.replace(fold=1)))
4503n/a
4504n/aSEC = timedelta(0, 1)
4505n/a
4506n/adef pairs(iterable):
4507n/a a, b = itertools.tee(iterable)
4508n/a next(b, None)
4509n/a return zip(a, b)
4510n/a
4511n/aclass ZoneInfo(tzinfo):
4512n/a zoneroot = '/usr/share/zoneinfo'
4513n/a def __init__(self, ut, ti):
4514n/a """
4515n/a
4516n/a :param ut: array
4517n/a Array of transition point timestamps
4518n/a :param ti: list
4519n/a A list of (offset, isdst, abbr) tuples
4520n/a :return: None
4521n/a """
4522n/a self.ut = ut
4523n/a self.ti = ti
4524n/a self.lt = self.invert(ut, ti)
4525n/a
4526n/a @staticmethod
4527n/a def invert(ut, ti):
4528n/a lt = (array('q', ut), array('q', ut))
4529n/a if ut:
4530n/a offset = ti[0][0] // SEC
4531n/a lt[0][0] += offset
4532n/a lt[1][0] += offset
4533n/a for i in range(1, len(ut)):
4534n/a lt[0][i] += ti[i-1][0] // SEC
4535n/a lt[1][i] += ti[i][0] // SEC
4536n/a return lt
4537n/a
4538n/a @classmethod
4539n/a def fromfile(cls, fileobj):
4540n/a if fileobj.read(4).decode() != "TZif":
4541n/a raise ValueError("not a zoneinfo file")
4542n/a fileobj.seek(32)
4543n/a counts = array('i')
4544n/a counts.fromfile(fileobj, 3)
4545n/a if sys.byteorder != 'big':
4546n/a counts.byteswap()
4547n/a
4548n/a ut = array('i')
4549n/a ut.fromfile(fileobj, counts[0])
4550n/a if sys.byteorder != 'big':
4551n/a ut.byteswap()
4552n/a
4553n/a type_indices = array('B')
4554n/a type_indices.fromfile(fileobj, counts[0])
4555n/a
4556n/a ttis = []
4557n/a for i in range(counts[1]):
4558n/a ttis.append(struct.unpack(">lbb", fileobj.read(6)))
4559n/a
4560n/a abbrs = fileobj.read(counts[2])
4561n/a
4562n/a # Convert ttis
4563n/a for i, (gmtoff, isdst, abbrind) in enumerate(ttis):
4564n/a abbr = abbrs[abbrind:abbrs.find(0, abbrind)].decode()
4565n/a ttis[i] = (timedelta(0, gmtoff), isdst, abbr)
4566n/a
4567n/a ti = [None] * len(ut)
4568n/a for i, idx in enumerate(type_indices):
4569n/a ti[i] = ttis[idx]
4570n/a
4571n/a self = cls(ut, ti)
4572n/a
4573n/a return self
4574n/a
4575n/a @classmethod
4576n/a def fromname(cls, name):
4577n/a path = os.path.join(cls.zoneroot, name)
4578n/a with open(path, 'rb') as f:
4579n/a return cls.fromfile(f)
4580n/a
4581n/a EPOCHORDINAL = date(1970, 1, 1).toordinal()
4582n/a
4583n/a def fromutc(self, dt):
4584n/a """datetime in UTC -> datetime in local time."""
4585n/a
4586n/a if not isinstance(dt, datetime):
4587n/a raise TypeError("fromutc() requires a datetime argument")
4588n/a if dt.tzinfo is not self:
4589n/a raise ValueError("dt.tzinfo is not self")
4590n/a
4591n/a timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400
4592n/a + dt.hour * 3600
4593n/a + dt.minute * 60
4594n/a + dt.second)
4595n/a
4596n/a if timestamp < self.ut[1]:
4597n/a tti = self.ti[0]
4598n/a fold = 0
4599n/a else:
4600n/a idx = bisect.bisect_right(self.ut, timestamp)
4601n/a assert self.ut[idx-1] <= timestamp
4602n/a assert idx == len(self.ut) or timestamp < self.ut[idx]
4603n/a tti_prev, tti = self.ti[idx-2:idx]
4604n/a # Detect fold
4605n/a shift = tti_prev[0] - tti[0]
4606n/a fold = (shift > timedelta(0, timestamp - self.ut[idx-1]))
4607n/a dt += tti[0]
4608n/a if fold:
4609n/a return dt.replace(fold=1)
4610n/a else:
4611n/a return dt
4612n/a
4613n/a def _find_ti(self, dt, i):
4614n/a timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400
4615n/a + dt.hour * 3600
4616n/a + dt.minute * 60
4617n/a + dt.second)
4618n/a lt = self.lt[dt.fold]
4619n/a idx = bisect.bisect_right(lt, timestamp)
4620n/a
4621n/a return self.ti[max(0, idx - 1)][i]
4622n/a
4623n/a def utcoffset(self, dt):
4624n/a return self._find_ti(dt, 0)
4625n/a
4626n/a def dst(self, dt):
4627n/a isdst = self._find_ti(dt, 1)
4628n/a # XXX: We cannot accurately determine the "save" value,
4629n/a # so let's return 1h whenever DST is in effect. Since
4630n/a # we don't use dst() in fromutc(), it is unlikely that
4631n/a # it will be needed for anything more than bool(dst()).
4632n/a return ZERO if isdst else HOUR
4633n/a
4634n/a def tzname(self, dt):
4635n/a return self._find_ti(dt, 2)
4636n/a
4637n/a @classmethod
4638n/a def zonenames(cls, zonedir=None):
4639n/a if zonedir is None:
4640n/a zonedir = cls.zoneroot
4641n/a zone_tab = os.path.join(zonedir, 'zone.tab')
4642n/a try:
4643n/a f = open(zone_tab)
4644n/a except OSError:
4645n/a return
4646n/a with f:
4647n/a for line in f:
4648n/a line = line.strip()
4649n/a if line and not line.startswith('#'):
4650n/a yield line.split()[2]
4651n/a
4652n/a @classmethod
4653n/a def stats(cls, start_year=1):
4654n/a count = gap_count = fold_count = zeros_count = 0
4655n/a min_gap = min_fold = timedelta.max
4656n/a max_gap = max_fold = ZERO
4657n/a min_gap_datetime = max_gap_datetime = datetime.min
4658n/a min_gap_zone = max_gap_zone = None
4659n/a min_fold_datetime = max_fold_datetime = datetime.min
4660n/a min_fold_zone = max_fold_zone = None
4661n/a stats_since = datetime(start_year, 1, 1) # Starting from 1970 eliminates a lot of noise
4662n/a for zonename in cls.zonenames():
4663n/a count += 1
4664n/a tz = cls.fromname(zonename)
4665n/a for dt, shift in tz.transitions():
4666n/a if dt < stats_since:
4667n/a continue
4668n/a if shift > ZERO:
4669n/a gap_count += 1
4670n/a if (shift, dt) > (max_gap, max_gap_datetime):
4671n/a max_gap = shift
4672n/a max_gap_zone = zonename
4673n/a max_gap_datetime = dt
4674n/a if (shift, datetime.max - dt) < (min_gap, datetime.max - min_gap_datetime):
4675n/a min_gap = shift
4676n/a min_gap_zone = zonename
4677n/a min_gap_datetime = dt
4678n/a elif shift < ZERO:
4679n/a fold_count += 1
4680n/a shift = -shift
4681n/a if (shift, dt) > (max_fold, max_fold_datetime):
4682n/a max_fold = shift
4683n/a max_fold_zone = zonename
4684n/a max_fold_datetime = dt
4685n/a if (shift, datetime.max - dt) < (min_fold, datetime.max - min_fold_datetime):
4686n/a min_fold = shift
4687n/a min_fold_zone = zonename
4688n/a min_fold_datetime = dt
4689n/a else:
4690n/a zeros_count += 1
4691n/a trans_counts = (gap_count, fold_count, zeros_count)
4692n/a print("Number of zones: %5d" % count)
4693n/a print("Number of transitions: %5d = %d (gaps) + %d (folds) + %d (zeros)" %
4694n/a ((sum(trans_counts),) + trans_counts))
4695n/a print("Min gap: %16s at %s in %s" % (min_gap, min_gap_datetime, min_gap_zone))
4696n/a print("Max gap: %16s at %s in %s" % (max_gap, max_gap_datetime, max_gap_zone))
4697n/a print("Min fold: %16s at %s in %s" % (min_fold, min_fold_datetime, min_fold_zone))
4698n/a print("Max fold: %16s at %s in %s" % (max_fold, max_fold_datetime, max_fold_zone))
4699n/a
4700n/a
4701n/a def transitions(self):
4702n/a for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
4703n/a shift = ti[0] - prev_ti[0]
4704n/a yield datetime.utcfromtimestamp(t), shift
4705n/a
4706n/a def nondst_folds(self):
4707n/a """Find all folds with the same value of isdst on both sides of the transition."""
4708n/a for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
4709n/a shift = ti[0] - prev_ti[0]
4710n/a if shift < ZERO and ti[1] == prev_ti[1]:
4711n/a yield datetime.utcfromtimestamp(t), -shift, prev_ti[2], ti[2]
4712n/a
4713n/a @classmethod
4714n/a def print_all_nondst_folds(cls, same_abbr=False, start_year=1):
4715n/a count = 0
4716n/a for zonename in cls.zonenames():
4717n/a tz = cls.fromname(zonename)
4718n/a for dt, shift, prev_abbr, abbr in tz.nondst_folds():
4719n/a if dt.year < start_year or same_abbr and prev_abbr != abbr:
4720n/a continue
4721n/a count += 1
4722n/a print("%3d) %-30s %s %10s %5s -> %s" %
4723n/a (count, zonename, dt, shift, prev_abbr, abbr))
4724n/a
4725n/a def folds(self):
4726n/a for t, shift in self.transitions():
4727n/a if shift < ZERO:
4728n/a yield t, -shift
4729n/a
4730n/a def gaps(self):
4731n/a for t, shift in self.transitions():
4732n/a if shift > ZERO:
4733n/a yield t, shift
4734n/a
4735n/a def zeros(self):
4736n/a for t, shift in self.transitions():
4737n/a if not shift:
4738n/a yield t
4739n/a
4740n/a
4741n/aclass ZoneInfoTest(unittest.TestCase):
4742n/a zonename = 'America/New_York'
4743n/a
4744n/a def setUp(self):
4745n/a if sys.platform == "win32":
4746n/a self.skipTest("Skipping zoneinfo tests on Windows")
4747n/a try:
4748n/a self.tz = ZoneInfo.fromname(self.zonename)
4749n/a except FileNotFoundError as err:
4750n/a self.skipTest("Skipping %s: %s" % (self.zonename, err))
4751n/a
4752n/a def assertEquivDatetimes(self, a, b):
4753n/a self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)),
4754n/a (b.replace(tzinfo=None), b.fold, id(b.tzinfo)))
4755n/a
4756n/a def test_folds(self):
4757n/a tz = self.tz
4758n/a for dt, shift in tz.folds():
4759n/a for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]:
4760n/a udt = dt + x
4761n/a ldt = tz.fromutc(udt.replace(tzinfo=tz))
4762n/a self.assertEqual(ldt.fold, 1)
4763n/a adt = udt.replace(tzinfo=timezone.utc).astimezone(tz)
4764n/a self.assertEquivDatetimes(adt, ldt)
4765n/a utcoffset = ldt.utcoffset()
4766n/a self.assertEqual(ldt.replace(tzinfo=None), udt + utcoffset)
4767n/a # Round trip
4768n/a self.assertEquivDatetimes(ldt.astimezone(timezone.utc),
4769n/a udt.replace(tzinfo=timezone.utc))
4770n/a
4771n/a
4772n/a for x in [-timedelta.resolution, shift]:
4773n/a udt = dt + x
4774n/a udt = udt.replace(tzinfo=tz)
4775n/a ldt = tz.fromutc(udt)
4776n/a self.assertEqual(ldt.fold, 0)
4777n/a
4778n/a def test_gaps(self):
4779n/a tz = self.tz
4780n/a for dt, shift in tz.gaps():
4781n/a for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]:
4782n/a udt = dt + x
4783n/a udt = udt.replace(tzinfo=tz)
4784n/a ldt = tz.fromutc(udt)
4785n/a self.assertEqual(ldt.fold, 0)
4786n/a adt = udt.replace(tzinfo=timezone.utc).astimezone(tz)
4787n/a self.assertEquivDatetimes(adt, ldt)
4788n/a utcoffset = ldt.utcoffset()
4789n/a self.assertEqual(ldt.replace(tzinfo=None), udt.replace(tzinfo=None) + utcoffset)
4790n/a # Create a local time inside the gap
4791n/a ldt = tz.fromutc(dt.replace(tzinfo=tz)) - shift + x
4792n/a self.assertLess(ldt.replace(fold=1).utcoffset(),
4793n/a ldt.replace(fold=0).utcoffset(),
4794n/a "At %s." % ldt)
4795n/a
4796n/a for x in [-timedelta.resolution, shift]:
4797n/a udt = dt + x
4798n/a ldt = tz.fromutc(udt.replace(tzinfo=tz))
4799n/a self.assertEqual(ldt.fold, 0)
4800n/a
4801n/a def test_system_transitions(self):
4802n/a if ('Riyadh8' in self.zonename or
4803n/a # From tzdata NEWS file:
4804n/a # The files solar87, solar88, and solar89 are no longer distributed.
4805n/a # They were a negative experiment - that is, a demonstration that
4806n/a # tz data can represent solar time only with some difficulty and error.
4807n/a # Their presence in the distribution caused confusion, as Riyadh
4808n/a # civil time was generally not solar time in those years.
4809n/a self.zonename.startswith('right/')):
4810n/a self.skipTest("Skipping %s" % self.zonename)
4811n/a tz = self.tz
4812n/a TZ = os.environ.get('TZ')
4813n/a os.environ['TZ'] = self.zonename
4814n/a try:
4815n/a _time.tzset()
4816n/a for udt, shift in tz.transitions():
4817n/a if udt.year >= 2037:
4818n/a # System support for times around the end of 32-bit time_t
4819n/a # and later is flaky on many systems.
4820n/a break
4821n/a s0 = (udt - datetime(1970, 1, 1)) // SEC
4822n/a ss = shift // SEC # shift seconds
4823n/a for x in [-40 * 3600, -20*3600, -1, 0,
4824n/a ss - 1, ss + 20 * 3600, ss + 40 * 3600]:
4825n/a s = s0 + x
4826n/a sdt = datetime.fromtimestamp(s)
4827n/a tzdt = datetime.fromtimestamp(s, tz).replace(tzinfo=None)
4828n/a self.assertEquivDatetimes(sdt, tzdt)
4829n/a s1 = sdt.timestamp()
4830n/a self.assertEqual(s, s1)
4831n/a if ss > 0: # gap
4832n/a # Create local time inside the gap
4833n/a dt = datetime.fromtimestamp(s0) - shift / 2
4834n/a ts0 = dt.timestamp()
4835n/a ts1 = dt.replace(fold=1).timestamp()
4836n/a self.assertEqual(ts0, s0 + ss / 2)
4837n/a self.assertEqual(ts1, s0 - ss / 2)
4838n/a finally:
4839n/a if TZ is None:
4840n/a del os.environ['TZ']
4841n/a else:
4842n/a os.environ['TZ'] = TZ
4843n/a _time.tzset()
4844n/a
4845n/a
4846n/aclass ZoneInfoCompleteTest(unittest.TestSuite):
4847n/a def __init__(self):
4848n/a tests = []
4849n/a if is_resource_enabled('tzdata'):
4850n/a for name in ZoneInfo.zonenames():
4851n/a Test = type('ZoneInfoTest[%s]' % name, (ZoneInfoTest,), {})
4852n/a Test.zonename = name
4853n/a for method in dir(Test):
4854n/a if method.startswith('test_'):
4855n/a tests.append(Test(method))
4856n/a super().__init__(tests)
4857n/a
4858n/a# Iran had a sub-minute UTC offset before 1946.
4859n/aclass IranTest(ZoneInfoTest):
4860n/a zonename = 'Asia/Tehran'
4861n/a
4862n/adef load_tests(loader, standard_tests, pattern):
4863n/a standard_tests.addTest(ZoneInfoCompleteTest())
4864n/a return standard_tests
4865n/a
4866n/a
4867n/aif __name__ == "__main__":
4868n/a unittest.main()