1 """Test date/time type.
3 See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
11 from test
import test_support
13 from datetime
import MINYEAR
, MAXYEAR
14 from datetime
import timedelta
15 from datetime
import tzinfo
16 from datetime
import time
17 from datetime
import date
, datetime
19 pickle_choices
= [(pickler
, unpickler
, proto
)
20 for pickler
in pickle
, cPickle
21 for unpickler
in pickle
, cPickle
22 for proto
in range(3)]
23 assert len(pickle_choices
) == 2*2*3
25 # An arbitrary collection of objects of non-datetime types, for testing
26 # mixed-type comparisons.
27 OTHERSTUFF
= (10, 10L, 34.5, "abc", {}, [], ())
30 #############################################################################
33 class TestModule(unittest
.TestCase
):
35 def test_constants(self
):
37 self
.assertEqual(datetime
.MINYEAR
, 1)
38 self
.assertEqual(datetime
.MAXYEAR
, 9999)
40 #############################################################################
43 class FixedOffset(tzinfo
):
44 def __init__(self
, offset
, name
, dstoffset
=42):
45 if isinstance(offset
, int):
46 offset
= timedelta(minutes
=offset
)
47 if isinstance(dstoffset
, int):
48 dstoffset
= timedelta(minutes
=dstoffset
)
49 self
.__offset
= offset
51 self
.__dstoffset
= dstoffset
53 return self
.__name
.lower()
54 def utcoffset(self
, dt
):
59 return self
.__dstoffset
61 class PicklableFixedOffset(FixedOffset
):
62 def __init__(self
, offset
=None, name
=None, dstoffset
=None):
63 FixedOffset
.__init
__(self
, offset
, name
, dstoffset
)
65 class TestTZInfo(unittest
.TestCase
):
67 def test_non_abstractness(self
):
68 # In order to allow subclasses to get pickled, the C implementation
69 # wasn't able to get away with having __init__ raise
70 # NotImplementedError.
73 self
.assertRaises(NotImplementedError, useless
.tzname
, dt
)
74 self
.assertRaises(NotImplementedError, useless
.utcoffset
, dt
)
75 self
.assertRaises(NotImplementedError, useless
.dst
, dt
)
77 def test_subclass_must_override(self
):
78 class NotEnough(tzinfo
):
79 def __init__(self
, offset
, name
):
80 self
.__offset
= offset
82 self
.failUnless(issubclass(NotEnough
, tzinfo
))
83 ne
= NotEnough(3, "NotByALongShot")
84 self
.failUnless(isinstance(ne
, tzinfo
))
87 self
.assertRaises(NotImplementedError, ne
.tzname
, dt
)
88 self
.assertRaises(NotImplementedError, ne
.utcoffset
, dt
)
89 self
.assertRaises(NotImplementedError, ne
.dst
, dt
)
91 def test_normal(self
):
92 fo
= FixedOffset(3, "Three")
93 self
.failUnless(isinstance(fo
, tzinfo
))
94 for dt
in datetime
.now(), None:
95 self
.assertEqual(fo
.utcoffset(dt
), timedelta(minutes
=3))
96 self
.assertEqual(fo
.tzname(dt
), "Three")
97 self
.assertEqual(fo
.dst(dt
), timedelta(minutes
=42))
99 def test_pickling_base(self
):
100 # There's no point to pickling tzinfo objects on their own (they
101 # carry no data), but they need to be picklable anyway else
102 # concrete subclasses can't be pickled.
103 orig
= tzinfo
.__new
__(tzinfo
)
104 self
.failUnless(type(orig
) is tzinfo
)
105 for pickler
, unpickler
, proto
in pickle_choices
:
106 green
= pickler
.dumps(orig
, proto
)
107 derived
= unpickler
.loads(green
)
108 self
.failUnless(type(derived
) is tzinfo
)
110 def test_pickling_subclass(self
):
111 # Make sure we can pickle/unpickle an instance of a subclass.
112 offset
= timedelta(minutes
=-300)
113 orig
= PicklableFixedOffset(offset
, 'cookie')
114 self
.failUnless(isinstance(orig
, tzinfo
))
115 self
.failUnless(type(orig
) is PicklableFixedOffset
)
116 self
.assertEqual(orig
.utcoffset(None), offset
)
117 self
.assertEqual(orig
.tzname(None), 'cookie')
118 for pickler
, unpickler
, proto
in pickle_choices
:
119 green
= pickler
.dumps(orig
, proto
)
120 derived
= unpickler
.loads(green
)
121 self
.failUnless(isinstance(derived
, tzinfo
))
122 self
.failUnless(type(derived
) is PicklableFixedOffset
)
123 self
.assertEqual(derived
.utcoffset(None), offset
)
124 self
.assertEqual(derived
.tzname(None), 'cookie')
126 #############################################################################
127 # Base clase for testing a particular aspect of timedelta, time, date and
128 # datetime comparisons.
130 class HarmlessMixedComparison(unittest
.TestCase
):
131 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
133 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
136 def test_harmless_mixed_comparison(self
):
137 me
= self
.theclass(1, 1, 1)
139 self
.failIf(me
== ())
140 self
.failUnless(me
!= ())
141 self
.failIf(() == me
)
142 self
.failUnless(() != me
)
144 self
.failUnless(me
in [1, 20L, [], me
])
145 self
.failIf(me
not in [1, 20L, [], me
])
147 self
.failUnless([] in [me
, 1, 20L, []])
148 self
.failIf([] not in [me
, 1, 20L, []])
150 def test_harmful_mixed_comparison(self
):
151 me
= self
.theclass(1, 1, 1)
153 self
.assertRaises(TypeError, lambda: me
< ())
154 self
.assertRaises(TypeError, lambda: me
<= ())
155 self
.assertRaises(TypeError, lambda: me
> ())
156 self
.assertRaises(TypeError, lambda: me
>= ())
158 self
.assertRaises(TypeError, lambda: () < me
)
159 self
.assertRaises(TypeError, lambda: () <= me
)
160 self
.assertRaises(TypeError, lambda: () > me
)
161 self
.assertRaises(TypeError, lambda: () >= me
)
163 self
.assertRaises(TypeError, cmp, (), me
)
164 self
.assertRaises(TypeError, cmp, me
, ())
166 #############################################################################
169 class TestTimeDelta(HarmlessMixedComparison
):
173 def test_constructor(self
):
174 eq
= self
.assertEqual
177 # Check keyword args to constructor
178 eq(td(), td(weeks
=0, days
=0, hours
=0, minutes
=0, seconds
=0,
179 milliseconds
=0, microseconds
=0))
180 eq(td(1), td(days
=1))
181 eq(td(0, 1), td(seconds
=1))
182 eq(td(0, 0, 1), td(microseconds
=1))
183 eq(td(weeks
=1), td(days
=7))
184 eq(td(days
=1), td(hours
=24))
185 eq(td(hours
=1), td(minutes
=60))
186 eq(td(minutes
=1), td(seconds
=60))
187 eq(td(seconds
=1), td(milliseconds
=1000))
188 eq(td(milliseconds
=1), td(microseconds
=1000))
190 # Check float args to constructor
191 eq(td(weeks
=1.0/7), td(days
=1))
192 eq(td(days
=1.0/24), td(hours
=1))
193 eq(td(hours
=1.0/60), td(minutes
=1))
194 eq(td(minutes
=1.0/60), td(seconds
=1))
195 eq(td(seconds
=0.001), td(milliseconds
=1))
196 eq(td(milliseconds
=0.001), td(microseconds
=1))
198 def test_computations(self
):
199 eq
= self
.assertEqual
203 b
= td(0, 60) # One minute
204 c
= td(0, 0, 1000) # One millisecond
205 eq(a
+b
+c
, td(7, 60, 1000))
206 eq(a
-b
, td(6, 24*3600 - 60))
209 eq(-b
, td(-1, 24*3600 - 60))
210 eq(-c
, td(-1, 24*3600 - 1, 999000))
213 eq(td(6, 24*3600), a
)
214 eq(td(0, 0, 60*1000000), b
)
220 eq(b
*10L, td(0, 600))
221 eq(c
*10, td(0, 0, 10000))
222 eq(10*c
, td(0, 0, 10000))
223 eq(c
*10L, td(0, 0, 10000))
227 eq(b
*(60*24), (b
*60)*24)
228 eq(b
*(60*24), (60*b
)*24)
233 eq(c
//1000, td(0, 0, 1))
234 eq(a
//10, td(0, 7*24*360))
235 eq(a
//3600000, td(0, 0, 7*24*1000))
237 def test_disallowed_computations(self
):
240 # Add/sub ints, longs, floats should be illegal
242 self
.assertRaises(TypeError, lambda: a
+i
)
243 self
.assertRaises(TypeError, lambda: a
-i
)
244 self
.assertRaises(TypeError, lambda: i
+a
)
245 self
.assertRaises(TypeError, lambda: i
-a
)
247 # Mul/div by float isn't supported.
249 self
.assertRaises(TypeError, lambda: a
*x
)
250 self
.assertRaises(TypeError, lambda: x
*a
)
251 self
.assertRaises(TypeError, lambda: a
/x
)
252 self
.assertRaises(TypeError, lambda: x
/a
)
253 self
.assertRaises(TypeError, lambda: a
// x
)
254 self
.assertRaises(TypeError, lambda: x
// a
)
256 # Divison of int by timedelta doesn't make sense.
257 # Division by zero doesn't make sense.
259 self
.assertRaises(TypeError, lambda: zero
// a
)
260 self
.assertRaises(ZeroDivisionError, lambda: a
// zero
)
262 def test_basic_attributes(self
):
263 days
, seconds
, us
= 1, 7, 31
264 td
= timedelta(days
, seconds
, us
)
265 self
.assertEqual(td
.days
, days
)
266 self
.assertEqual(td
.seconds
, seconds
)
267 self
.assertEqual(td
.microseconds
, us
)
269 def test_carries(self
):
270 t1
= timedelta(days
=100,
275 microseconds
=(3*60 - 12) * 1e6
+ 1)
276 t2
= timedelta(microseconds
=1)
277 self
.assertEqual(t1
, t2
)
279 def test_hash_equality(self
):
280 t1
= timedelta(days
=100,
285 microseconds
=(3*60 - 12) * 1000000)
287 self
.assertEqual(hash(t1
), hash(t2
))
289 t1
+= timedelta(weeks
=7)
290 t2
+= timedelta(days
=7*7)
291 self
.assertEqual(t1
, t2
)
292 self
.assertEqual(hash(t1
), hash(t2
))
296 self
.assertEqual(len(d
), 1)
297 self
.assertEqual(d
[t1
], 2)
299 def test_pickling(self
):
301 orig
= timedelta(*args
)
302 for pickler
, unpickler
, proto
in pickle_choices
:
303 green
= pickler
.dumps(orig
, proto
)
304 derived
= unpickler
.loads(green
)
305 self
.assertEqual(orig
, derived
)
307 def test_compare(self
):
308 t1
= timedelta(2, 3, 4)
309 t2
= timedelta(2, 3, 4)
310 self
.failUnless(t1
== t2
)
311 self
.failUnless(t1
<= t2
)
312 self
.failUnless(t1
>= t2
)
313 self
.failUnless(not t1
!= t2
)
314 self
.failUnless(not t1
< t2
)
315 self
.failUnless(not t1
> t2
)
316 self
.assertEqual(cmp(t1
, t2
), 0)
317 self
.assertEqual(cmp(t2
, t1
), 0)
319 for args
in (3, 3, 3), (2, 4, 4), (2, 3, 5):
320 t2
= timedelta(*args
) # this is larger than t1
321 self
.failUnless(t1
< t2
)
322 self
.failUnless(t2
> t1
)
323 self
.failUnless(t1
<= t2
)
324 self
.failUnless(t2
>= t1
)
325 self
.failUnless(t1
!= t2
)
326 self
.failUnless(t2
!= t1
)
327 self
.failUnless(not t1
== t2
)
328 self
.failUnless(not t2
== t1
)
329 self
.failUnless(not t1
> t2
)
330 self
.failUnless(not t2
< t1
)
331 self
.failUnless(not t1
>= t2
)
332 self
.failUnless(not t2
<= t1
)
333 self
.assertEqual(cmp(t1
, t2
), -1)
334 self
.assertEqual(cmp(t2
, t1
), 1)
336 for badarg
in OTHERSTUFF
:
337 self
.assertEqual(t1
== badarg
, False)
338 self
.assertEqual(t1
!= badarg
, True)
339 self
.assertEqual(badarg
== t1
, False)
340 self
.assertEqual(badarg
!= t1
, True)
342 self
.assertRaises(TypeError, lambda: t1
<= badarg
)
343 self
.assertRaises(TypeError, lambda: t1
< badarg
)
344 self
.assertRaises(TypeError, lambda: t1
> badarg
)
345 self
.assertRaises(TypeError, lambda: t1
>= badarg
)
346 self
.assertRaises(TypeError, lambda: badarg
<= t1
)
347 self
.assertRaises(TypeError, lambda: badarg
< t1
)
348 self
.assertRaises(TypeError, lambda: badarg
> t1
)
349 self
.assertRaises(TypeError, lambda: badarg
>= t1
)
353 eq
= self
.assertEqual
355 eq(str(td(1)), "1 day, 0:00:00")
356 eq(str(td(-1)), "-1 day, 0:00:00")
357 eq(str(td(2)), "2 days, 0:00:00")
358 eq(str(td(-2)), "-2 days, 0:00:00")
360 eq(str(td(hours
=12, minutes
=58, seconds
=59)), "12:58:59")
361 eq(str(td(hours
=2, minutes
=3, seconds
=4)), "2:03:04")
362 eq(str(td(weeks
=-30, hours
=23, minutes
=12, seconds
=34)),
363 "-210 days, 23:12:34")
365 eq(str(td(milliseconds
=1)), "0:00:00.001000")
366 eq(str(td(microseconds
=3)), "0:00:00.000003")
368 eq(str(td(days
=999999999, hours
=23, minutes
=59, seconds
=59,
369 microseconds
=999999)),
370 "999999999 days, 23:59:59.999999")
372 def test_roundtrip(self
):
373 for td
in (timedelta(days
=999999999, hours
=23, minutes
=59,
374 seconds
=59, microseconds
=999999),
375 timedelta(days
=-999999999),
376 timedelta(days
=1, seconds
=2, microseconds
=3)):
378 # Verify td -> string -> td identity.
380 self
.failUnless(s
.startswith('datetime.'))
383 self
.assertEqual(td
, td2
)
385 # Verify identity via reconstructing from pieces.
386 td2
= timedelta(td
.days
, td
.seconds
, td
.microseconds
)
387 self
.assertEqual(td
, td2
)
389 def test_resolution_info(self
):
390 self
.assert_(isinstance(timedelta
.min, timedelta
))
391 self
.assert_(isinstance(timedelta
.max, timedelta
))
392 self
.assert_(isinstance(timedelta
.resolution
, timedelta
))
393 self
.assert_(timedelta
.max > timedelta
.min)
394 self
.assertEqual(timedelta
.min, timedelta(-999999999))
395 self
.assertEqual(timedelta
.max, timedelta(999999999, 24*3600-1, 1e6
-1))
396 self
.assertEqual(timedelta
.resolution
, timedelta(0, 0, 1))
398 def test_overflow(self
):
399 tiny
= timedelta
.resolution
401 td
= timedelta
.min + tiny
402 td
-= tiny
# no problem
403 self
.assertRaises(OverflowError, td
.__sub
__, tiny
)
404 self
.assertRaises(OverflowError, td
.__add
__, -tiny
)
406 td
= timedelta
.max - tiny
407 td
+= tiny
# no problem
408 self
.assertRaises(OverflowError, td
.__add
__, tiny
)
409 self
.assertRaises(OverflowError, td
.__sub
__, -tiny
)
411 self
.assertRaises(OverflowError, lambda: -timedelta
.max)
413 def test_microsecond_rounding(self
):
415 eq
= self
.assertEqual
417 # Single-field rounding.
418 eq(td(milliseconds
=0.4/1000), td(0)) # rounds to 0
419 eq(td(milliseconds
=-0.4/1000), td(0)) # rounds to 0
420 eq(td(milliseconds
=0.6/1000), td(microseconds
=1))
421 eq(td(milliseconds
=-0.6/1000), td(microseconds
=-1))
423 # Rounding due to contributions from more than one field.
425 us_per_day
= us_per_hour
* 24
426 eq(td(days
=.4/us_per_day
), td(0))
427 eq(td(hours
=.2/us_per_hour
), td(0))
428 eq(td(days
=.4/us_per_day
, hours
=.2/us_per_hour
), td(microseconds
=1))
430 eq(td(days
=-.4/us_per_day
), td(0))
431 eq(td(hours
=-.2/us_per_hour
), td(0))
432 eq(td(days
=-.4/us_per_day
, hours
=-.2/us_per_hour
), td(microseconds
=-1))
434 def test_massive_normalization(self
):
435 td
= timedelta(microseconds
=-1)
436 self
.assertEqual((td
.days
, td
.seconds
, td
.microseconds
),
437 (-1, 24*3600-1, 999999))
440 self
.failUnless(timedelta(1))
441 self
.failUnless(timedelta(0, 1))
442 self
.failUnless(timedelta(0, 0, 1))
443 self
.failUnless(timedelta(microseconds
=1))
444 self
.failUnless(not timedelta(0))
446 def test_subclass_timedelta(self
):
450 return T(td
.days
, td
.seconds
, td
.microseconds
)
451 from_td
= staticmethod(from_td
)
454 sum = (self
.days
* 24 +
455 self
.seconds
/ 3600.0 +
456 self
.microseconds
/ 3600e6
)
460 self
.assert_(type(t1
) is T
)
461 self
.assertEqual(t1
.as_hours(), 24)
463 t2
= T(days
=-1, seconds
=-3600)
464 self
.assert_(type(t2
) is T
)
465 self
.assertEqual(t2
.as_hours(), -25)
468 self
.assert_(type(t3
) is timedelta
)
470 self
.assert_(type(t4
) is T
)
471 self
.assertEqual(t3
.days
, t4
.days
)
472 self
.assertEqual(t3
.seconds
, t4
.seconds
)
473 self
.assertEqual(t3
.microseconds
, t4
.microseconds
)
474 self
.assertEqual(str(t3
), str(t4
))
475 self
.assertEqual(t4
.as_hours(), -1)
477 #############################################################################
480 class TestDateOnly(unittest
.TestCase
):
481 # Tests here won't pass if also run on datetime objects, so don't
482 # subclass this to test datetimes too.
484 def test_delta_non_days_ignored(self
):
485 dt
= date(2000, 1, 2)
486 delta
= timedelta(days
=1, hours
=2, minutes
=3, seconds
=4,
488 days
= timedelta(delta
.days
)
489 self
.assertEqual(days
, timedelta(1))
492 self
.assertEqual(dt2
, dt
+ days
)
495 self
.assertEqual(dt2
, dt
+ days
)
498 self
.assertEqual(dt2
, dt
- days
)
501 days
= timedelta(delta
.days
)
502 self
.assertEqual(days
, timedelta(-2))
505 self
.assertEqual(dt2
, dt
+ days
)
508 self
.assertEqual(dt2
, dt
+ days
)
511 self
.assertEqual(dt2
, dt
- days
)
513 class TestDate(HarmlessMixedComparison
):
514 # Tests here should pass for both dates and datetimes, except for a
515 # few tests that TestDateTime overrides.
519 def test_basic_attributes(self
):
520 dt
= self
.theclass(2002, 3, 1)
521 self
.assertEqual(dt
.year
, 2002)
522 self
.assertEqual(dt
.month
, 3)
523 self
.assertEqual(dt
.day
, 1)
525 def test_roundtrip(self
):
526 for dt
in (self
.theclass(1, 2, 3),
527 self
.theclass
.today()):
528 # Verify dt -> string -> date identity.
530 self
.failUnless(s
.startswith('datetime.'))
533 self
.assertEqual(dt
, dt2
)
535 # Verify identity via reconstructing from pieces.
536 dt2
= self
.theclass(dt
.year
, dt
.month
, dt
.day
)
537 self
.assertEqual(dt
, dt2
)
539 def test_ordinal_conversions(self
):
540 # Check some fixed values.
541 for y
, m
, d
, n
in [(1, 1, 1, 1), # calendar origin
544 # first example from "Calendrical Calculations"
545 (1945, 11, 12, 710347)]:
546 d
= self
.theclass(y
, m
, d
)
547 self
.assertEqual(n
, d
.toordinal())
548 fromord
= self
.theclass
.fromordinal(n
)
549 self
.assertEqual(d
, fromord
)
550 if hasattr(fromord
, "hour"):
551 # if we're checking something fancier than a date, verify
552 # the extra fields have been zeroed out
553 self
.assertEqual(fromord
.hour
, 0)
554 self
.assertEqual(fromord
.minute
, 0)
555 self
.assertEqual(fromord
.second
, 0)
556 self
.assertEqual(fromord
.microsecond
, 0)
558 # Check first and last days of year spottily across the whole
559 # range of years supported.
560 for year
in xrange(MINYEAR
, MAXYEAR
+1, 7):
561 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
562 d
= self
.theclass(year
, 1, 1)
564 d2
= self
.theclass
.fromordinal(n
)
565 self
.assertEqual(d
, d2
)
566 # Verify that moving back a day gets to the end of year-1.
568 d
= self
.theclass
.fromordinal(n
-1)
569 d2
= self
.theclass(year
-1, 12, 31)
570 self
.assertEqual(d
, d2
)
571 self
.assertEqual(d2
.toordinal(), n
-1)
573 # Test every day in a leap-year and a non-leap year.
574 dim
= [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
575 for year
, isleap
in (2000, True), (2002, False):
576 n
= self
.theclass(year
, 1, 1).toordinal()
577 for month
, maxday
in zip(range(1, 13), dim
):
578 if month
== 2 and isleap
:
580 for day
in range(1, maxday
+1):
581 d
= self
.theclass(year
, month
, day
)
582 self
.assertEqual(d
.toordinal(), n
)
583 self
.assertEqual(d
, self
.theclass
.fromordinal(n
))
586 def test_extreme_ordinals(self
):
587 a
= self
.theclass
.min
588 a
= self
.theclass(a
.year
, a
.month
, a
.day
) # get rid of time parts
590 b
= a
.fromordinal(aord
)
591 self
.assertEqual(a
, b
)
593 self
.assertRaises(ValueError, lambda: a
.fromordinal(aord
- 1))
595 b
= a
+ timedelta(days
=1)
596 self
.assertEqual(b
.toordinal(), aord
+ 1)
597 self
.assertEqual(b
, self
.theclass
.fromordinal(aord
+ 1))
599 a
= self
.theclass
.max
600 a
= self
.theclass(a
.year
, a
.month
, a
.day
) # get rid of time parts
602 b
= a
.fromordinal(aord
)
603 self
.assertEqual(a
, b
)
605 self
.assertRaises(ValueError, lambda: a
.fromordinal(aord
+ 1))
607 b
= a
- timedelta(days
=1)
608 self
.assertEqual(b
.toordinal(), aord
- 1)
609 self
.assertEqual(b
, self
.theclass
.fromordinal(aord
- 1))
611 def test_bad_constructor_arguments(self
):
613 self
.theclass(MINYEAR
, 1, 1) # no exception
614 self
.theclass(MAXYEAR
, 1, 1) # no exception
615 self
.assertRaises(ValueError, self
.theclass
, MINYEAR
-1, 1, 1)
616 self
.assertRaises(ValueError, self
.theclass
, MAXYEAR
+1, 1, 1)
618 self
.theclass(2000, 1, 1) # no exception
619 self
.theclass(2000, 12, 1) # no exception
620 self
.assertRaises(ValueError, self
.theclass
, 2000, 0, 1)
621 self
.assertRaises(ValueError, self
.theclass
, 2000, 13, 1)
623 self
.theclass(2000, 2, 29) # no exception
624 self
.theclass(2004, 2, 29) # no exception
625 self
.theclass(2400, 2, 29) # no exception
626 self
.assertRaises(ValueError, self
.theclass
, 2000, 2, 30)
627 self
.assertRaises(ValueError, self
.theclass
, 2001, 2, 29)
628 self
.assertRaises(ValueError, self
.theclass
, 2100, 2, 29)
629 self
.assertRaises(ValueError, self
.theclass
, 1900, 2, 29)
630 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 0)
631 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 32)
633 def test_hash_equality(self
):
634 d
= self
.theclass(2000, 12, 31)
636 e
= self
.theclass(2000, 12, 31)
637 self
.assertEqual(d
, e
)
638 self
.assertEqual(hash(d
), hash(e
))
642 self
.assertEqual(len(dic
), 1)
643 self
.assertEqual(dic
[d
], 2)
644 self
.assertEqual(dic
[e
], 2)
646 d
= self
.theclass(2001, 1, 1)
648 e
= self
.theclass(2001, 1, 1)
649 self
.assertEqual(d
, e
)
650 self
.assertEqual(hash(d
), hash(e
))
654 self
.assertEqual(len(dic
), 1)
655 self
.assertEqual(dic
[d
], 2)
656 self
.assertEqual(dic
[e
], 2)
658 def test_computations(self
):
659 a
= self
.theclass(2002, 1, 31)
660 b
= self
.theclass(1956, 1, 31)
663 self
.assertEqual(diff
.days
, 46*365 + len(range(1956, 2002, 4)))
664 self
.assertEqual(diff
.seconds
, 0)
665 self
.assertEqual(diff
.microseconds
, 0)
669 a
= self
.theclass(2002, 3, 2)
670 self
.assertEqual(a
+ day
, self
.theclass(2002, 3, 3))
671 self
.assertEqual(day
+ a
, self
.theclass(2002, 3, 3))
672 self
.assertEqual(a
- day
, self
.theclass(2002, 3, 1))
673 self
.assertEqual(-day
+ a
, self
.theclass(2002, 3, 1))
674 self
.assertEqual(a
+ week
, self
.theclass(2002, 3, 9))
675 self
.assertEqual(a
- week
, self
.theclass(2002, 2, 23))
676 self
.assertEqual(a
+ 52*week
, self
.theclass(2003, 3, 1))
677 self
.assertEqual(a
- 52*week
, self
.theclass(2001, 3, 3))
678 self
.assertEqual((a
+ week
) - a
, week
)
679 self
.assertEqual((a
+ day
) - a
, day
)
680 self
.assertEqual((a
- week
) - a
, -week
)
681 self
.assertEqual((a
- day
) - a
, -day
)
682 self
.assertEqual(a
- (a
+ week
), -week
)
683 self
.assertEqual(a
- (a
+ day
), -day
)
684 self
.assertEqual(a
- (a
- week
), week
)
685 self
.assertEqual(a
- (a
- day
), day
)
687 # Add/sub ints, longs, floats should be illegal
689 self
.assertRaises(TypeError, lambda: a
+i
)
690 self
.assertRaises(TypeError, lambda: a
-i
)
691 self
.assertRaises(TypeError, lambda: i
+a
)
692 self
.assertRaises(TypeError, lambda: i
-a
)
694 # delta - date is senseless.
695 self
.assertRaises(TypeError, lambda: day
- a
)
696 # mixing date and (delta or date) via * or // is senseless
697 self
.assertRaises(TypeError, lambda: day
* a
)
698 self
.assertRaises(TypeError, lambda: a
* day
)
699 self
.assertRaises(TypeError, lambda: day
// a
)
700 self
.assertRaises(TypeError, lambda: a
// day
)
701 self
.assertRaises(TypeError, lambda: a
* a
)
702 self
.assertRaises(TypeError, lambda: a
// a
)
703 # date + date is senseless
704 self
.assertRaises(TypeError, lambda: a
+ a
)
706 def test_overflow(self
):
707 tiny
= self
.theclass
.resolution
709 dt
= self
.theclass
.min + tiny
710 dt
-= tiny
# no problem
711 self
.assertRaises(OverflowError, dt
.__sub
__, tiny
)
712 self
.assertRaises(OverflowError, dt
.__add
__, -tiny
)
714 dt
= self
.theclass
.max - tiny
715 dt
+= tiny
# no problem
716 self
.assertRaises(OverflowError, dt
.__add
__, tiny
)
717 self
.assertRaises(OverflowError, dt
.__sub
__, -tiny
)
719 def test_fromtimestamp(self
):
722 # Try an arbitrary fixed value.
723 year
, month
, day
= 1999, 9, 19
724 ts
= time
.mktime((year
, month
, day
, 0, 0, 0, 0, 0, -1))
725 d
= self
.theclass
.fromtimestamp(ts
)
726 self
.assertEqual(d
.year
, year
)
727 self
.assertEqual(d
.month
, month
)
728 self
.assertEqual(d
.day
, day
)
730 def test_today(self
):
733 # We claim that today() is like fromtimestamp(time.time()), so
735 for dummy
in range(3):
736 today
= self
.theclass
.today()
738 todayagain
= self
.theclass
.fromtimestamp(ts
)
739 if today
== todayagain
:
741 # There are several legit reasons that could fail:
742 # 1. It recently became midnight, between the today() and the
744 # 2. The platform time() has such fine resolution that we'll
745 # never get the same value twice.
746 # 3. The platform time() has poor resolution, and we just
747 # happened to call today() right before a resolution quantum
749 # 4. The system clock got fiddled between calls.
750 # In any case, wait a little while and try again.
753 # It worked or it didn't. If it didn't, assume it's reason #2, and
754 # let the test pass if they're within half a second of each other.
755 self
.failUnless(today
== todayagain
or
756 abs(todayagain
- today
) < timedelta(seconds
=0.5))
758 def test_weekday(self
):
760 # March 4, 2002 is a Monday
761 self
.assertEqual(self
.theclass(2002, 3, 4+i
).weekday(), i
)
762 self
.assertEqual(self
.theclass(2002, 3, 4+i
).isoweekday(), i
+1)
763 # January 2, 1956 is a Monday
764 self
.assertEqual(self
.theclass(1956, 1, 2+i
).weekday(), i
)
765 self
.assertEqual(self
.theclass(1956, 1, 2+i
).isoweekday(), i
+1)
767 def test_isocalendar(self
):
768 # Check examples from
769 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
771 d
= self
.theclass(2003, 12, 22+i
)
772 self
.assertEqual(d
.isocalendar(), (2003, 52, i
+1))
773 d
= self
.theclass(2003, 12, 29) + timedelta(i
)
774 self
.assertEqual(d
.isocalendar(), (2004, 1, i
+1))
775 d
= self
.theclass(2004, 1, 5+i
)
776 self
.assertEqual(d
.isocalendar(), (2004, 2, i
+1))
777 d
= self
.theclass(2009, 12, 21+i
)
778 self
.assertEqual(d
.isocalendar(), (2009, 52, i
+1))
779 d
= self
.theclass(2009, 12, 28) + timedelta(i
)
780 self
.assertEqual(d
.isocalendar(), (2009, 53, i
+1))
781 d
= self
.theclass(2010, 1, 4+i
)
782 self
.assertEqual(d
.isocalendar(), (2010, 1, i
+1))
784 def test_iso_long_years(self
):
785 # Calculate long ISO years and compare to table from
786 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
787 ISO_LONG_YEARS_TABLE
= """
812 iso_long_years
= map(int, ISO_LONG_YEARS_TABLE
.split())
813 iso_long_years
.sort()
816 d
= self
.theclass(2000+i
, 12, 31)
817 d1
= self
.theclass(1600+i
, 12, 31)
818 self
.assertEqual(d
.isocalendar()[1:], d1
.isocalendar()[1:])
819 if d
.isocalendar()[1] == 53:
821 self
.assertEqual(L
, iso_long_years
)
823 def test_isoformat(self
):
824 t
= self
.theclass(2, 3, 2)
825 self
.assertEqual(t
.isoformat(), "0002-03-02")
827 def test_ctime(self
):
828 t
= self
.theclass(2002, 3, 2)
829 self
.assertEqual(t
.ctime(), "Sat Mar 2 00:00:00 2002")
831 def test_strftime(self
):
832 t
= self
.theclass(2005, 3, 2)
833 self
.assertEqual(t
.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
834 self
.assertEqual(t
.strftime(""), "") # SF bug #761337
836 self
.assertRaises(TypeError, t
.strftime
) # needs an arg
837 self
.assertRaises(TypeError, t
.strftime
, "one", "two") # too many args
838 self
.assertRaises(TypeError, t
.strftime
, 42) # arg wrong type
840 # A naive object replaces %z and %Z w/ empty strings.
841 self
.assertEqual(t
.strftime("'%z' '%Z'"), "'' ''")
843 def test_resolution_info(self
):
844 self
.assert_(isinstance(self
.theclass
.min, self
.theclass
))
845 self
.assert_(isinstance(self
.theclass
.max, self
.theclass
))
846 self
.assert_(isinstance(self
.theclass
.resolution
, timedelta
))
847 self
.assert_(self
.theclass
.max > self
.theclass
.min)
849 def test_extreme_timedelta(self
):
850 big
= self
.theclass
.max - self
.theclass
.min
851 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
852 n
= (big
.days
*24*3600 + big
.seconds
)*1000000 + big
.microseconds
853 # n == 315537897599999999 ~= 2**58.13
854 justasbig
= timedelta(0, 0, n
)
855 self
.assertEqual(big
, justasbig
)
856 self
.assertEqual(self
.theclass
.min + big
, self
.theclass
.max)
857 self
.assertEqual(self
.theclass
.max - big
, self
.theclass
.min)
859 def test_timetuple(self
):
861 # January 2, 1956 is a Monday (0)
862 d
= self
.theclass(1956, 1, 2+i
)
864 self
.assertEqual(t
, (1956, 1, 2+i
, 0, 0, 0, i
, 2+i
, -1))
865 # February 1, 1956 is a Wednesday (2)
866 d
= self
.theclass(1956, 2, 1+i
)
868 self
.assertEqual(t
, (1956, 2, 1+i
, 0, 0, 0, (2+i
)%7, 32+i
, -1))
869 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
871 d
= self
.theclass(1956, 3, 1+i
)
873 self
.assertEqual(t
, (1956, 3, 1+i
, 0, 0, 0, (3+i
)%7, 61+i
, -1))
874 self
.assertEqual(t
.tm_year
, 1956)
875 self
.assertEqual(t
.tm_mon
, 3)
876 self
.assertEqual(t
.tm_mday
, 1+i
)
877 self
.assertEqual(t
.tm_hour
, 0)
878 self
.assertEqual(t
.tm_min
, 0)
879 self
.assertEqual(t
.tm_sec
, 0)
880 self
.assertEqual(t
.tm_wday
, (3+i
)%7)
881 self
.assertEqual(t
.tm_yday
, 61+i
)
882 self
.assertEqual(t
.tm_isdst
, -1)
884 def test_pickling(self
):
886 orig
= self
.theclass(*args
)
887 for pickler
, unpickler
, proto
in pickle_choices
:
888 green
= pickler
.dumps(orig
, proto
)
889 derived
= unpickler
.loads(green
)
890 self
.assertEqual(orig
, derived
)
892 def test_compare(self
):
893 t1
= self
.theclass(2, 3, 4)
894 t2
= self
.theclass(2, 3, 4)
895 self
.failUnless(t1
== t2
)
896 self
.failUnless(t1
<= t2
)
897 self
.failUnless(t1
>= t2
)
898 self
.failUnless(not t1
!= t2
)
899 self
.failUnless(not t1
< t2
)
900 self
.failUnless(not t1
> t2
)
901 self
.assertEqual(cmp(t1
, t2
), 0)
902 self
.assertEqual(cmp(t2
, t1
), 0)
904 for args
in (3, 3, 3), (2, 4, 4), (2, 3, 5):
905 t2
= self
.theclass(*args
) # this is larger than t1
906 self
.failUnless(t1
< t2
)
907 self
.failUnless(t2
> t1
)
908 self
.failUnless(t1
<= t2
)
909 self
.failUnless(t2
>= t1
)
910 self
.failUnless(t1
!= t2
)
911 self
.failUnless(t2
!= t1
)
912 self
.failUnless(not t1
== t2
)
913 self
.failUnless(not t2
== t1
)
914 self
.failUnless(not t1
> t2
)
915 self
.failUnless(not t2
< t1
)
916 self
.failUnless(not t1
>= t2
)
917 self
.failUnless(not t2
<= t1
)
918 self
.assertEqual(cmp(t1
, t2
), -1)
919 self
.assertEqual(cmp(t2
, t1
), 1)
921 for badarg
in OTHERSTUFF
:
922 self
.assertEqual(t1
== badarg
, False)
923 self
.assertEqual(t1
!= badarg
, True)
924 self
.assertEqual(badarg
== t1
, False)
925 self
.assertEqual(badarg
!= t1
, True)
927 self
.assertRaises(TypeError, lambda: t1
< badarg
)
928 self
.assertRaises(TypeError, lambda: t1
> badarg
)
929 self
.assertRaises(TypeError, lambda: t1
>= badarg
)
930 self
.assertRaises(TypeError, lambda: badarg
<= t1
)
931 self
.assertRaises(TypeError, lambda: badarg
< t1
)
932 self
.assertRaises(TypeError, lambda: badarg
> t1
)
933 self
.assertRaises(TypeError, lambda: badarg
>= t1
)
935 def test_mixed_compare(self
):
936 our
= self
.theclass(2000, 4, 5)
937 self
.assertRaises(TypeError, cmp, our
, 1)
938 self
.assertRaises(TypeError, cmp, 1, our
)
940 class AnotherDateTimeClass(object):
941 def __cmp__(self
, other
):
942 # Return "equal" so calling this can't be confused with
943 # compare-by-address (which never says "equal" for distinct
947 # This still errors, because date and datetime comparison raise
948 # TypeError instead of NotImplemented when they don't know what to
949 # do, in order to stop comparison from falling back to the default
950 # compare-by-address.
951 their
= AnotherDateTimeClass()
952 self
.assertRaises(TypeError, cmp, our
, their
)
953 # Oops: The next stab raises TypeError in the C implementation,
954 # but not in the Python implementation of datetime. The difference
955 # is due to that the Python implementation defines __cmp__ but
956 # the C implementation defines tp_richcompare. This is more pain
957 # to fix than it's worth, so commenting out the test.
958 # self.assertEqual(cmp(their, our), 0)
960 # But date and datetime comparison return NotImplemented instead if the
961 # other object has a timetuple attr. This gives the other object a
962 # chance to do the comparison.
963 class Comparable(AnotherDateTimeClass
):
968 self
.assertEqual(cmp(our
, their
), 0)
969 self
.assertEqual(cmp(their
, our
), 0)
970 self
.failUnless(our
== their
)
971 self
.failUnless(their
== our
)
974 # All dates are considered true.
975 self
.failUnless(self
.theclass
.min)
976 self
.failUnless(self
.theclass
.max)
978 def test_srftime_out_of_range(self
):
979 # For nasty technical reasons, we can't handle years before 1900.
981 self
.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
982 for y
in 1, 49, 51, 99, 100, 1000, 1899:
983 self
.assertRaises(ValueError, cls(y
, 1, 1).strftime
, "%Y")
985 def test_replace(self
):
989 self
.assertEqual(base
, base
.replace())
992 for name
, newval
in (("year", 2),
997 expected
= cls(*newargs
)
998 got
= base
.replace(**{name
: newval
})
999 self
.assertEqual(expected
, got
)
1003 base
= cls(2000, 2, 29)
1004 self
.assertRaises(ValueError, base
.replace
, year
=2001)
1006 def test_subclass_date(self
):
1008 class C(self
.theclass
):
1011 def __new__(cls
, *args
, **kws
):
1013 extra
= temp
.pop('extra')
1014 result
= self
.theclass
.__new
__(cls
, *args
, **temp
)
1015 result
.extra
= extra
1018 def newmeth(self
, start
):
1019 return start
+ self
.year
+ self
.month
1023 dt1
= self
.theclass(*args
)
1024 dt2
= C(*args
, **{'extra': 7})
1026 self
.assertEqual(dt2
.__class__
, C
)
1027 self
.assertEqual(dt2
.theAnswer
, 42)
1028 self
.assertEqual(dt2
.extra
, 7)
1029 self
.assertEqual(dt1
.toordinal(), dt2
.toordinal())
1030 self
.assertEqual(dt2
.newmeth(-7), dt1
.year
+ dt1
.month
- 7)
1033 #############################################################################
1036 class TestDateTime(TestDate
):
1040 def test_basic_attributes(self
):
1041 dt
= self
.theclass(2002, 3, 1, 12, 0)
1042 self
.assertEqual(dt
.year
, 2002)
1043 self
.assertEqual(dt
.month
, 3)
1044 self
.assertEqual(dt
.day
, 1)
1045 self
.assertEqual(dt
.hour
, 12)
1046 self
.assertEqual(dt
.minute
, 0)
1047 self
.assertEqual(dt
.second
, 0)
1048 self
.assertEqual(dt
.microsecond
, 0)
1050 def test_basic_attributes_nonzero(self
):
1051 # Make sure all attributes are non-zero so bugs in
1052 # bit-shifting access show up.
1053 dt
= self
.theclass(2002, 3, 1, 12, 59, 59, 8000)
1054 self
.assertEqual(dt
.year
, 2002)
1055 self
.assertEqual(dt
.month
, 3)
1056 self
.assertEqual(dt
.day
, 1)
1057 self
.assertEqual(dt
.hour
, 12)
1058 self
.assertEqual(dt
.minute
, 59)
1059 self
.assertEqual(dt
.second
, 59)
1060 self
.assertEqual(dt
.microsecond
, 8000)
1062 def test_roundtrip(self
):
1063 for dt
in (self
.theclass(1, 2, 3, 4, 5, 6, 7),
1064 self
.theclass
.now()):
1065 # Verify dt -> string -> datetime identity.
1067 self
.failUnless(s
.startswith('datetime.'))
1070 self
.assertEqual(dt
, dt2
)
1072 # Verify identity via reconstructing from pieces.
1073 dt2
= self
.theclass(dt
.year
, dt
.month
, dt
.day
,
1074 dt
.hour
, dt
.minute
, dt
.second
,
1076 self
.assertEqual(dt
, dt2
)
1078 def test_isoformat(self
):
1079 t
= self
.theclass(2, 3, 2, 4, 5, 1, 123)
1080 self
.assertEqual(t
.isoformat(), "0002-03-02T04:05:01.000123")
1081 self
.assertEqual(t
.isoformat('T'), "0002-03-02T04:05:01.000123")
1082 self
.assertEqual(t
.isoformat(' '), "0002-03-02 04:05:01.000123")
1083 # str is ISO format with the separator forced to a blank.
1084 self
.assertEqual(str(t
), "0002-03-02 04:05:01.000123")
1086 t
= self
.theclass(2, 3, 2)
1087 self
.assertEqual(t
.isoformat(), "0002-03-02T00:00:00")
1088 self
.assertEqual(t
.isoformat('T'), "0002-03-02T00:00:00")
1089 self
.assertEqual(t
.isoformat(' '), "0002-03-02 00:00:00")
1090 # str is ISO format with the separator forced to a blank.
1091 self
.assertEqual(str(t
), "0002-03-02 00:00:00")
1093 def test_more_ctime(self
):
1094 # Test fields that TestDate doesn't touch.
1097 t
= self
.theclass(2002, 3, 2, 18, 3, 5, 123)
1098 self
.assertEqual(t
.ctime(), "Sat Mar 2 18:03:05 2002")
1099 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1100 # out. The difference is that t.ctime() produces " 2" for the day,
1101 # but platform ctime() produces "02" for the day. According to
1102 # C99, t.ctime() is correct here.
1103 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1105 # So test a case where that difference doesn't matter.
1106 t
= self
.theclass(2002, 3, 22, 18, 3, 5, 123)
1107 self
.assertEqual(t
.ctime(), time
.ctime(time
.mktime(t
.timetuple())))
1109 def test_tz_independent_comparing(self
):
1110 dt1
= self
.theclass(2002, 3, 1, 9, 0, 0)
1111 dt2
= self
.theclass(2002, 3, 1, 10, 0, 0)
1112 dt3
= self
.theclass(2002, 3, 1, 9, 0, 0)
1113 self
.assertEqual(dt1
, dt3
)
1114 self
.assert_(dt2
> dt3
)
1116 # Make sure comparison doesn't forget microseconds, and isn't done
1117 # via comparing a float timestamp (an IEEE double doesn't have enough
1118 # precision to span microsecond resolution across years 1 thru 9999,
1119 # so comparing via timestamp necessarily calls some distinct values
1121 dt1
= self
.theclass(MAXYEAR
, 12, 31, 23, 59, 59, 999998)
1122 us
= timedelta(microseconds
=1)
1124 self
.assertEqual(dt2
- dt1
, us
)
1125 self
.assert_(dt1
< dt2
)
1127 def test_bad_constructor_arguments(self
):
1129 self
.theclass(MINYEAR
, 1, 1) # no exception
1130 self
.theclass(MAXYEAR
, 1, 1) # no exception
1131 self
.assertRaises(ValueError, self
.theclass
, MINYEAR
-1, 1, 1)
1132 self
.assertRaises(ValueError, self
.theclass
, MAXYEAR
+1, 1, 1)
1134 self
.theclass(2000, 1, 1) # no exception
1135 self
.theclass(2000, 12, 1) # no exception
1136 self
.assertRaises(ValueError, self
.theclass
, 2000, 0, 1)
1137 self
.assertRaises(ValueError, self
.theclass
, 2000, 13, 1)
1139 self
.theclass(2000, 2, 29) # no exception
1140 self
.theclass(2004, 2, 29) # no exception
1141 self
.theclass(2400, 2, 29) # no exception
1142 self
.assertRaises(ValueError, self
.theclass
, 2000, 2, 30)
1143 self
.assertRaises(ValueError, self
.theclass
, 2001, 2, 29)
1144 self
.assertRaises(ValueError, self
.theclass
, 2100, 2, 29)
1145 self
.assertRaises(ValueError, self
.theclass
, 1900, 2, 29)
1146 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 0)
1147 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 32)
1149 self
.theclass(2000, 1, 31, 0) # no exception
1150 self
.theclass(2000, 1, 31, 23) # no exception
1151 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 31, -1)
1152 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 31, 24)
1154 self
.theclass(2000, 1, 31, 23, 0) # no exception
1155 self
.theclass(2000, 1, 31, 23, 59) # no exception
1156 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 31, 23, -1)
1157 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 31, 23, 60)
1159 self
.theclass(2000, 1, 31, 23, 59, 0) # no exception
1160 self
.theclass(2000, 1, 31, 23, 59, 59) # no exception
1161 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 31, 23, 59, -1)
1162 self
.assertRaises(ValueError, self
.theclass
, 2000, 1, 31, 23, 59, 60)
1164 self
.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1165 self
.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1166 self
.assertRaises(ValueError, self
.theclass
,
1167 2000, 1, 31, 23, 59, 59, -1)
1168 self
.assertRaises(ValueError, self
.theclass
,
1169 2000, 1, 31, 23, 59, 59,
1172 def test_hash_equality(self
):
1173 d
= self
.theclass(2000, 12, 31, 23, 30, 17)
1174 e
= self
.theclass(2000, 12, 31, 23, 30, 17)
1175 self
.assertEqual(d
, e
)
1176 self
.assertEqual(hash(d
), hash(e
))
1180 self
.assertEqual(len(dic
), 1)
1181 self
.assertEqual(dic
[d
], 2)
1182 self
.assertEqual(dic
[e
], 2)
1184 d
= self
.theclass(2001, 1, 1, 0, 5, 17)
1185 e
= self
.theclass(2001, 1, 1, 0, 5, 17)
1186 self
.assertEqual(d
, e
)
1187 self
.assertEqual(hash(d
), hash(e
))
1191 self
.assertEqual(len(dic
), 1)
1192 self
.assertEqual(dic
[d
], 2)
1193 self
.assertEqual(dic
[e
], 2)
1195 def test_computations(self
):
1196 a
= self
.theclass(2002, 1, 31)
1197 b
= self
.theclass(1956, 1, 31)
1199 self
.assertEqual(diff
.days
, 46*365 + len(range(1956, 2002, 4)))
1200 self
.assertEqual(diff
.seconds
, 0)
1201 self
.assertEqual(diff
.microseconds
, 0)
1202 a
= self
.theclass(2002, 3, 2, 17, 6)
1203 millisec
= timedelta(0, 0, 1000)
1204 hour
= timedelta(0, 3600)
1207 self
.assertEqual(a
+ hour
, self
.theclass(2002, 3, 2, 18, 6))
1208 self
.assertEqual(hour
+ a
, self
.theclass(2002, 3, 2, 18, 6))
1209 self
.assertEqual(a
+ 10*hour
, self
.theclass(2002, 3, 3, 3, 6))
1210 self
.assertEqual(a
- hour
, self
.theclass(2002, 3, 2, 16, 6))
1211 self
.assertEqual(-hour
+ a
, self
.theclass(2002, 3, 2, 16, 6))
1212 self
.assertEqual(a
- hour
, a
+ -hour
)
1213 self
.assertEqual(a
- 20*hour
, self
.theclass(2002, 3, 1, 21, 6))
1214 self
.assertEqual(a
+ day
, self
.theclass(2002, 3, 3, 17, 6))
1215 self
.assertEqual(a
- day
, self
.theclass(2002, 3, 1, 17, 6))
1216 self
.assertEqual(a
+ week
, self
.theclass(2002, 3, 9, 17, 6))
1217 self
.assertEqual(a
- week
, self
.theclass(2002, 2, 23, 17, 6))
1218 self
.assertEqual(a
+ 52*week
, self
.theclass(2003, 3, 1, 17, 6))
1219 self
.assertEqual(a
- 52*week
, self
.theclass(2001, 3, 3, 17, 6))
1220 self
.assertEqual((a
+ week
) - a
, week
)
1221 self
.assertEqual((a
+ day
) - a
, day
)
1222 self
.assertEqual((a
+ hour
) - a
, hour
)
1223 self
.assertEqual((a
+ millisec
) - a
, millisec
)
1224 self
.assertEqual((a
- week
) - a
, -week
)
1225 self
.assertEqual((a
- day
) - a
, -day
)
1226 self
.assertEqual((a
- hour
) - a
, -hour
)
1227 self
.assertEqual((a
- millisec
) - a
, -millisec
)
1228 self
.assertEqual(a
- (a
+ week
), -week
)
1229 self
.assertEqual(a
- (a
+ day
), -day
)
1230 self
.assertEqual(a
- (a
+ hour
), -hour
)
1231 self
.assertEqual(a
- (a
+ millisec
), -millisec
)
1232 self
.assertEqual(a
- (a
- week
), week
)
1233 self
.assertEqual(a
- (a
- day
), day
)
1234 self
.assertEqual(a
- (a
- hour
), hour
)
1235 self
.assertEqual(a
- (a
- millisec
), millisec
)
1236 self
.assertEqual(a
+ (week
+ day
+ hour
+ millisec
),
1237 self
.theclass(2002, 3, 10, 18, 6, 0, 1000))
1238 self
.assertEqual(a
+ (week
+ day
+ hour
+ millisec
),
1239 (((a
+ week
) + day
) + hour
) + millisec
)
1240 self
.assertEqual(a
- (week
+ day
+ hour
+ millisec
),
1241 self
.theclass(2002, 2, 22, 16, 5, 59, 999000))
1242 self
.assertEqual(a
- (week
+ day
+ hour
+ millisec
),
1243 (((a
- week
) - day
) - hour
) - millisec
)
1244 # Add/sub ints, longs, floats should be illegal
1245 for i
in 1, 1L, 1.0:
1246 self
.assertRaises(TypeError, lambda: a
+i
)
1247 self
.assertRaises(TypeError, lambda: a
-i
)
1248 self
.assertRaises(TypeError, lambda: i
+a
)
1249 self
.assertRaises(TypeError, lambda: i
-a
)
1251 # delta - datetime is senseless.
1252 self
.assertRaises(TypeError, lambda: day
- a
)
1253 # mixing datetime and (delta or datetime) via * or // is senseless
1254 self
.assertRaises(TypeError, lambda: day
* a
)
1255 self
.assertRaises(TypeError, lambda: a
* day
)
1256 self
.assertRaises(TypeError, lambda: day
// a
)
1257 self
.assertRaises(TypeError, lambda: a
// day
)
1258 self
.assertRaises(TypeError, lambda: a
* a
)
1259 self
.assertRaises(TypeError, lambda: a
// a
)
1260 # datetime + datetime is senseless
1261 self
.assertRaises(TypeError, lambda: a
+ a
)
1263 def test_pickling(self
):
1264 args
= 6, 7, 23, 20, 59, 1, 64**2
1265 orig
= self
.theclass(*args
)
1266 for pickler
, unpickler
, proto
in pickle_choices
:
1267 green
= pickler
.dumps(orig
, proto
)
1268 derived
= unpickler
.loads(green
)
1269 self
.assertEqual(orig
, derived
)
1271 def test_more_pickling(self
):
1272 a
= self
.theclass(2003, 2, 7, 16, 48, 37, 444116)
1275 self
.assertEqual(b
.year
, 2003)
1276 self
.assertEqual(b
.month
, 2)
1277 self
.assertEqual(b
.day
, 7)
1279 def test_more_compare(self
):
1280 # The test_compare() inherited from TestDate covers the error cases.
1281 # We just want to test lexicographic ordering on the members datetime
1282 # has that date lacks.
1283 args
= [2000, 11, 29, 20, 58, 16, 999998]
1284 t1
= self
.theclass(*args
)
1285 t2
= self
.theclass(*args
)
1286 self
.failUnless(t1
== t2
)
1287 self
.failUnless(t1
<= t2
)
1288 self
.failUnless(t1
>= t2
)
1289 self
.failUnless(not t1
!= t2
)
1290 self
.failUnless(not t1
< t2
)
1291 self
.failUnless(not t1
> t2
)
1292 self
.assertEqual(cmp(t1
, t2
), 0)
1293 self
.assertEqual(cmp(t2
, t1
), 0)
1295 for i
in range(len(args
)):
1297 newargs
[i
] = args
[i
] + 1
1298 t2
= self
.theclass(*newargs
) # this is larger than t1
1299 self
.failUnless(t1
< t2
)
1300 self
.failUnless(t2
> t1
)
1301 self
.failUnless(t1
<= t2
)
1302 self
.failUnless(t2
>= t1
)
1303 self
.failUnless(t1
!= t2
)
1304 self
.failUnless(t2
!= t1
)
1305 self
.failUnless(not t1
== t2
)
1306 self
.failUnless(not t2
== t1
)
1307 self
.failUnless(not t1
> t2
)
1308 self
.failUnless(not t2
< t1
)
1309 self
.failUnless(not t1
>= t2
)
1310 self
.failUnless(not t2
<= t1
)
1311 self
.assertEqual(cmp(t1
, t2
), -1)
1312 self
.assertEqual(cmp(t2
, t1
), 1)
1315 # A helper for timestamp constructor tests.
1316 def verify_field_equality(self
, expected
, got
):
1317 self
.assertEqual(expected
.tm_year
, got
.year
)
1318 self
.assertEqual(expected
.tm_mon
, got
.month
)
1319 self
.assertEqual(expected
.tm_mday
, got
.day
)
1320 self
.assertEqual(expected
.tm_hour
, got
.hour
)
1321 self
.assertEqual(expected
.tm_min
, got
.minute
)
1322 self
.assertEqual(expected
.tm_sec
, got
.second
)
1324 def test_fromtimestamp(self
):
1328 expected
= time
.localtime(ts
)
1329 got
= self
.theclass
.fromtimestamp(ts
)
1330 self
.verify_field_equality(expected
, got
)
1332 def test_utcfromtimestamp(self
):
1336 expected
= time
.gmtime(ts
)
1337 got
= self
.theclass
.utcfromtimestamp(ts
)
1338 self
.verify_field_equality(expected
, got
)
1340 def test_utcnow(self
):
1343 # Call it a success if utcnow() and utcfromtimestamp() are within
1344 # a second of each other.
1345 tolerance
= timedelta(seconds
=1)
1346 for dummy
in range(3):
1347 from_now
= self
.theclass
.utcnow()
1348 from_timestamp
= self
.theclass
.utcfromtimestamp(time
.time())
1349 if abs(from_timestamp
- from_now
) <= tolerance
:
1351 # Else try again a few times.
1352 self
.failUnless(abs(from_timestamp
- from_now
) <= tolerance
)
1354 def test_more_timetuple(self
):
1355 # This tests fields beyond those tested by the TestDate.test_timetuple.
1356 t
= self
.theclass(2004, 12, 31, 6, 22, 33)
1357 self
.assertEqual(t
.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1358 self
.assertEqual(t
.timetuple(),
1359 (t
.year
, t
.month
, t
.day
,
1360 t
.hour
, t
.minute
, t
.second
,
1362 t
.toordinal() - date(t
.year
, 1, 1).toordinal() + 1,
1365 self
.assertEqual(tt
.tm_year
, t
.year
)
1366 self
.assertEqual(tt
.tm_mon
, t
.month
)
1367 self
.assertEqual(tt
.tm_mday
, t
.day
)
1368 self
.assertEqual(tt
.tm_hour
, t
.hour
)
1369 self
.assertEqual(tt
.tm_min
, t
.minute
)
1370 self
.assertEqual(tt
.tm_sec
, t
.second
)
1371 self
.assertEqual(tt
.tm_wday
, t
.weekday())
1372 self
.assertEqual(tt
.tm_yday
, t
.toordinal() -
1373 date(t
.year
, 1, 1).toordinal() + 1)
1374 self
.assertEqual(tt
.tm_isdst
, -1)
1376 def test_more_strftime(self
):
1377 # This tests fields beyond those tested by the TestDate.test_strftime.
1378 t
= self
.theclass(2004, 12, 31, 6, 22, 33)
1379 self
.assertEqual(t
.strftime("%m %d %y %S %M %H %j"),
1380 "12 31 04 33 22 06 366")
1382 def test_extract(self
):
1383 dt
= self
.theclass(2002, 3, 4, 18, 45, 3, 1234)
1384 self
.assertEqual(dt
.date(), date(2002, 3, 4))
1385 self
.assertEqual(dt
.time(), time(18, 45, 3, 1234))
1387 def test_combine(self
):
1388 d
= date(2002, 3, 4)
1389 t
= time(18, 45, 3, 1234)
1390 expected
= self
.theclass(2002, 3, 4, 18, 45, 3, 1234)
1391 combine
= self
.theclass
.combine
1393 self
.assertEqual(dt
, expected
)
1395 dt
= combine(time
=t
, date
=d
)
1396 self
.assertEqual(dt
, expected
)
1398 self
.assertEqual(d
, dt
.date())
1399 self
.assertEqual(t
, dt
.time())
1400 self
.assertEqual(dt
, combine(dt
.date(), dt
.time()))
1402 self
.assertRaises(TypeError, combine
) # need an arg
1403 self
.assertRaises(TypeError, combine
, d
) # need two args
1404 self
.assertRaises(TypeError, combine
, t
, d
) # args reversed
1405 self
.assertRaises(TypeError, combine
, d
, t
, 1) # too many args
1406 self
.assertRaises(TypeError, combine
, "date", "time") # wrong types
1408 def test_replace(self
):
1410 args
= [1, 2, 3, 4, 5, 6, 7]
1412 self
.assertEqual(base
, base
.replace())
1415 for name
, newval
in (("year", 2),
1421 ("microsecond", 8)):
1424 expected
= cls(*newargs
)
1425 got
= base
.replace(**{name
: newval
})
1426 self
.assertEqual(expected
, got
)
1430 base
= cls(2000, 2, 29)
1431 self
.assertRaises(ValueError, base
.replace
, year
=2001)
1433 def test_astimezone(self
):
1434 # Pretty boring! The TZ test is more interesting here. astimezone()
1435 # simply can't be applied to a naive object.
1436 dt
= self
.theclass
.now()
1437 f
= FixedOffset(44, "")
1438 self
.assertRaises(TypeError, dt
.astimezone
) # not enough args
1439 self
.assertRaises(TypeError, dt
.astimezone
, f
, f
) # too many args
1440 self
.assertRaises(TypeError, dt
.astimezone
, dt
) # arg wrong type
1441 self
.assertRaises(ValueError, dt
.astimezone
, f
) # naive
1442 self
.assertRaises(ValueError, dt
.astimezone
, tz
=f
) # naive
1444 class Bogus(tzinfo
):
1445 def utcoffset(self
, dt
): return None
1446 def dst(self
, dt
): return timedelta(0)
1448 self
.assertRaises(ValueError, dt
.astimezone
, bog
) # naive
1450 class AlsoBogus(tzinfo
):
1451 def utcoffset(self
, dt
): return timedelta(0)
1452 def dst(self
, dt
): return None
1453 alsobog
= AlsoBogus()
1454 self
.assertRaises(ValueError, dt
.astimezone
, alsobog
) # also naive
1456 def test_subclass_datetime(self
):
1458 class C(self
.theclass
):
1461 def __new__(cls
, *args
, **kws
):
1463 extra
= temp
.pop('extra')
1464 result
= self
.theclass
.__new
__(cls
, *args
, **temp
)
1465 result
.extra
= extra
1468 def newmeth(self
, start
):
1469 return start
+ self
.year
+ self
.month
+ self
.second
1471 args
= 2003, 4, 14, 12, 13, 41
1473 dt1
= self
.theclass(*args
)
1474 dt2
= C(*args
, **{'extra': 7})
1476 self
.assertEqual(dt2
.__class__
, C
)
1477 self
.assertEqual(dt2
.theAnswer
, 42)
1478 self
.assertEqual(dt2
.extra
, 7)
1479 self
.assertEqual(dt1
.toordinal(), dt2
.toordinal())
1480 self
.assertEqual(dt2
.newmeth(-7), dt1
.year
+ dt1
.month
+
1483 class TestTime(HarmlessMixedComparison
):
1487 def test_basic_attributes(self
):
1488 t
= self
.theclass(12, 0)
1489 self
.assertEqual(t
.hour
, 12)
1490 self
.assertEqual(t
.minute
, 0)
1491 self
.assertEqual(t
.second
, 0)
1492 self
.assertEqual(t
.microsecond
, 0)
1494 def test_basic_attributes_nonzero(self
):
1495 # Make sure all attributes are non-zero so bugs in
1496 # bit-shifting access show up.
1497 t
= self
.theclass(12, 59, 59, 8000)
1498 self
.assertEqual(t
.hour
, 12)
1499 self
.assertEqual(t
.minute
, 59)
1500 self
.assertEqual(t
.second
, 59)
1501 self
.assertEqual(t
.microsecond
, 8000)
1503 def test_roundtrip(self
):
1504 t
= self
.theclass(1, 2, 3, 4)
1506 # Verify t -> string -> time identity.
1508 self
.failUnless(s
.startswith('datetime.'))
1511 self
.assertEqual(t
, t2
)
1513 # Verify identity via reconstructing from pieces.
1514 t2
= self
.theclass(t
.hour
, t
.minute
, t
.second
,
1516 self
.assertEqual(t
, t2
)
1518 def test_comparing(self
):
1520 t1
= self
.theclass(*args
)
1521 t2
= self
.theclass(*args
)
1522 self
.failUnless(t1
== t2
)
1523 self
.failUnless(t1
<= t2
)
1524 self
.failUnless(t1
>= t2
)
1525 self
.failUnless(not t1
!= t2
)
1526 self
.failUnless(not t1
< t2
)
1527 self
.failUnless(not t1
> t2
)
1528 self
.assertEqual(cmp(t1
, t2
), 0)
1529 self
.assertEqual(cmp(t2
, t1
), 0)
1531 for i
in range(len(args
)):
1533 newargs
[i
] = args
[i
] + 1
1534 t2
= self
.theclass(*newargs
) # this is larger than t1
1535 self
.failUnless(t1
< t2
)
1536 self
.failUnless(t2
> t1
)
1537 self
.failUnless(t1
<= t2
)
1538 self
.failUnless(t2
>= t1
)
1539 self
.failUnless(t1
!= t2
)
1540 self
.failUnless(t2
!= t1
)
1541 self
.failUnless(not t1
== t2
)
1542 self
.failUnless(not t2
== t1
)
1543 self
.failUnless(not t1
> t2
)
1544 self
.failUnless(not t2
< t1
)
1545 self
.failUnless(not t1
>= t2
)
1546 self
.failUnless(not t2
<= t1
)
1547 self
.assertEqual(cmp(t1
, t2
), -1)
1548 self
.assertEqual(cmp(t2
, t1
), 1)
1550 for badarg
in OTHERSTUFF
:
1551 self
.assertEqual(t1
== badarg
, False)
1552 self
.assertEqual(t1
!= badarg
, True)
1553 self
.assertEqual(badarg
== t1
, False)
1554 self
.assertEqual(badarg
!= t1
, True)
1556 self
.assertRaises(TypeError, lambda: t1
<= badarg
)
1557 self
.assertRaises(TypeError, lambda: t1
< badarg
)
1558 self
.assertRaises(TypeError, lambda: t1
> badarg
)
1559 self
.assertRaises(TypeError, lambda: t1
>= badarg
)
1560 self
.assertRaises(TypeError, lambda: badarg
<= t1
)
1561 self
.assertRaises(TypeError, lambda: badarg
< t1
)
1562 self
.assertRaises(TypeError, lambda: badarg
> t1
)
1563 self
.assertRaises(TypeError, lambda: badarg
>= t1
)
1565 def test_bad_constructor_arguments(self
):
1567 self
.theclass(0, 0) # no exception
1568 self
.theclass(23, 0) # no exception
1569 self
.assertRaises(ValueError, self
.theclass
, -1, 0)
1570 self
.assertRaises(ValueError, self
.theclass
, 24, 0)
1572 self
.theclass(23, 0) # no exception
1573 self
.theclass(23, 59) # no exception
1574 self
.assertRaises(ValueError, self
.theclass
, 23, -1)
1575 self
.assertRaises(ValueError, self
.theclass
, 23, 60)
1577 self
.theclass(23, 59, 0) # no exception
1578 self
.theclass(23, 59, 59) # no exception
1579 self
.assertRaises(ValueError, self
.theclass
, 23, 59, -1)
1580 self
.assertRaises(ValueError, self
.theclass
, 23, 59, 60)
1582 self
.theclass(23, 59, 59, 0) # no exception
1583 self
.theclass(23, 59, 59, 999999) # no exception
1584 self
.assertRaises(ValueError, self
.theclass
, 23, 59, 59, -1)
1585 self
.assertRaises(ValueError, self
.theclass
, 23, 59, 59, 1000000)
1587 def test_hash_equality(self
):
1588 d
= self
.theclass(23, 30, 17)
1589 e
= self
.theclass(23, 30, 17)
1590 self
.assertEqual(d
, e
)
1591 self
.assertEqual(hash(d
), hash(e
))
1595 self
.assertEqual(len(dic
), 1)
1596 self
.assertEqual(dic
[d
], 2)
1597 self
.assertEqual(dic
[e
], 2)
1599 d
= self
.theclass(0, 5, 17)
1600 e
= self
.theclass(0, 5, 17)
1601 self
.assertEqual(d
, e
)
1602 self
.assertEqual(hash(d
), hash(e
))
1606 self
.assertEqual(len(dic
), 1)
1607 self
.assertEqual(dic
[d
], 2)
1608 self
.assertEqual(dic
[e
], 2)
1610 def test_isoformat(self
):
1611 t
= self
.theclass(4, 5, 1, 123)
1612 self
.assertEqual(t
.isoformat(), "04:05:01.000123")
1613 self
.assertEqual(t
.isoformat(), str(t
))
1616 self
.assertEqual(t
.isoformat(), "00:00:00")
1617 self
.assertEqual(t
.isoformat(), str(t
))
1619 t
= self
.theclass(microsecond
=1)
1620 self
.assertEqual(t
.isoformat(), "00:00:00.000001")
1621 self
.assertEqual(t
.isoformat(), str(t
))
1623 t
= self
.theclass(microsecond
=10)
1624 self
.assertEqual(t
.isoformat(), "00:00:00.000010")
1625 self
.assertEqual(t
.isoformat(), str(t
))
1627 t
= self
.theclass(microsecond
=100)
1628 self
.assertEqual(t
.isoformat(), "00:00:00.000100")
1629 self
.assertEqual(t
.isoformat(), str(t
))
1631 t
= self
.theclass(microsecond
=1000)
1632 self
.assertEqual(t
.isoformat(), "00:00:00.001000")
1633 self
.assertEqual(t
.isoformat(), str(t
))
1635 t
= self
.theclass(microsecond
=10000)
1636 self
.assertEqual(t
.isoformat(), "00:00:00.010000")
1637 self
.assertEqual(t
.isoformat(), str(t
))
1639 t
= self
.theclass(microsecond
=100000)
1640 self
.assertEqual(t
.isoformat(), "00:00:00.100000")
1641 self
.assertEqual(t
.isoformat(), str(t
))
1643 def test_strftime(self
):
1644 t
= self
.theclass(1, 2, 3, 4)
1645 self
.assertEqual(t
.strftime('%H %M %S'), "01 02 03")
1646 # A naive object replaces %z and %Z with empty strings.
1647 self
.assertEqual(t
.strftime("'%z' '%Z'"), "'' ''")
1650 self
.assertEqual(str(self
.theclass(1, 2, 3, 4)), "01:02:03.000004")
1651 self
.assertEqual(str(self
.theclass(10, 2, 3, 4000)), "10:02:03.004000")
1652 self
.assertEqual(str(self
.theclass(0, 2, 3, 400000)), "00:02:03.400000")
1653 self
.assertEqual(str(self
.theclass(12, 2, 3, 0)), "12:02:03")
1654 self
.assertEqual(str(self
.theclass(23, 15, 0, 0)), "23:15:00")
1656 def test_repr(self
):
1657 name
= 'datetime.' + self
.theclass
.__name
__
1658 self
.assertEqual(repr(self
.theclass(1, 2, 3, 4)),
1659 "%s(1, 2, 3, 4)" % name
)
1660 self
.assertEqual(repr(self
.theclass(10, 2, 3, 4000)),
1661 "%s(10, 2, 3, 4000)" % name
)
1662 self
.assertEqual(repr(self
.theclass(0, 2, 3, 400000)),
1663 "%s(0, 2, 3, 400000)" % name
)
1664 self
.assertEqual(repr(self
.theclass(12, 2, 3, 0)),
1665 "%s(12, 2, 3)" % name
)
1666 self
.assertEqual(repr(self
.theclass(23, 15, 0, 0)),
1667 "%s(23, 15)" % name
)
1669 def test_resolution_info(self
):
1670 self
.assert_(isinstance(self
.theclass
.min, self
.theclass
))
1671 self
.assert_(isinstance(self
.theclass
.max, self
.theclass
))
1672 self
.assert_(isinstance(self
.theclass
.resolution
, timedelta
))
1673 self
.assert_(self
.theclass
.max > self
.theclass
.min)
1675 def test_pickling(self
):
1676 args
= 20, 59, 16, 64**2
1677 orig
= self
.theclass(*args
)
1678 for pickler
, unpickler
, proto
in pickle_choices
:
1679 green
= pickler
.dumps(orig
, proto
)
1680 derived
= unpickler
.loads(green
)
1681 self
.assertEqual(orig
, derived
)
1683 def test_bool(self
):
1685 self
.failUnless(cls(1))
1686 self
.failUnless(cls(0, 1))
1687 self
.failUnless(cls(0, 0, 1))
1688 self
.failUnless(cls(0, 0, 0, 1))
1689 self
.failUnless(not cls(0))
1690 self
.failUnless(not cls())
1692 def test_replace(self
):
1696 self
.assertEqual(base
, base
.replace())
1699 for name
, newval
in (("hour", 5),
1702 ("microsecond", 8)):
1705 expected
= cls(*newargs
)
1706 got
= base
.replace(**{name
: newval
})
1707 self
.assertEqual(expected
, got
)
1712 self
.assertRaises(ValueError, base
.replace
, hour
=24)
1713 self
.assertRaises(ValueError, base
.replace
, minute
=-1)
1714 self
.assertRaises(ValueError, base
.replace
, second
=100)
1715 self
.assertRaises(ValueError, base
.replace
, microsecond
=1000000)
1717 def test_subclass_time(self
):
1719 class C(self
.theclass
):
1722 def __new__(cls
, *args
, **kws
):
1724 extra
= temp
.pop('extra')
1725 result
= self
.theclass
.__new
__(cls
, *args
, **temp
)
1726 result
.extra
= extra
1729 def newmeth(self
, start
):
1730 return start
+ self
.hour
+ self
.second
1734 dt1
= self
.theclass(*args
)
1735 dt2
= C(*args
, **{'extra': 7})
1737 self
.assertEqual(dt2
.__class__
, C
)
1738 self
.assertEqual(dt2
.theAnswer
, 42)
1739 self
.assertEqual(dt2
.extra
, 7)
1740 self
.assertEqual(dt1
.isoformat(), dt2
.isoformat())
1741 self
.assertEqual(dt2
.newmeth(-7), dt1
.hour
+ dt1
.second
- 7)
1743 # A mixin for classes with a tzinfo= argument. Subclasses must define
1744 # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
1745 # must be legit (which is true for time and datetime).
1746 class TZInfoBase(unittest
.TestCase
):
1748 def test_argument_passing(self
):
1750 # A datetime passes itself on, a time passes None.
1751 class introspective(tzinfo
):
1752 def tzname(self
, dt
): return dt
and "real" or "none"
1753 def utcoffset(self
, dt
):
1754 return timedelta(minutes
= dt
and 42 or -42)
1757 obj
= cls(1, 2, 3, tzinfo
=introspective())
1759 expected
= cls
is time
and "none" or "real"
1760 self
.assertEqual(obj
.tzname(), expected
)
1762 expected
= timedelta(minutes
=(cls
is time
and -42 or 42))
1763 self
.assertEqual(obj
.utcoffset(), expected
)
1764 self
.assertEqual(obj
.dst(), expected
)
1766 def test_bad_tzinfo_classes(self
):
1768 self
.assertRaises(TypeError, cls
, 1, 1, 1, tzinfo
=12)
1770 class NiceTry(object):
1771 def __init__(self
): pass
1772 def utcoffset(self
, dt
): pass
1773 self
.assertRaises(TypeError, cls
, 1, 1, 1, tzinfo
=NiceTry
)
1775 class BetterTry(tzinfo
):
1776 def __init__(self
): pass
1777 def utcoffset(self
, dt
): pass
1779 t
= cls(1, 1, 1, tzinfo
=b
)
1780 self
.failUnless(t
.tzinfo
is b
)
1782 def test_utc_offset_out_of_bounds(self
):
1784 def __init__(self
, offset
):
1785 self
.offset
= timedelta(minutes
=offset
)
1786 def utcoffset(self
, dt
):
1790 for offset
, legit
in ((-1440, False),
1795 t
= cls(1, 2, 3, tzinfo
=Edgy(offset
))
1796 elif cls
is datetime
:
1797 t
= cls(6, 6, 6, 1, 2, 3, tzinfo
=Edgy(offset
))
1799 assert 0, "impossible"
1802 h
, m
= divmod(aofs
, 60)
1803 tag
= "%c%02d:%02d" % (offset
< 0 and '-' or '+', h
, m
)
1804 if isinstance(t
, datetime
):
1806 self
.assertEqual(str(t
), "01:02:03" + tag
)
1808 self
.assertRaises(ValueError, str, t
)
1810 def test_tzinfo_classes(self
):
1813 def utcoffset(self
, dt
): return None
1814 def dst(self
, dt
): return None
1815 def tzname(self
, dt
): return None
1816 for t
in (cls(1, 1, 1),
1817 cls(1, 1, 1, tzinfo
=None),
1818 cls(1, 1, 1, tzinfo
=C1())):
1819 self
.failUnless(t
.utcoffset() is None)
1820 self
.failUnless(t
.dst() is None)
1821 self
.failUnless(t
.tzname() is None)
1824 def utcoffset(self
, dt
): return timedelta(minutes
=-1439)
1825 def dst(self
, dt
): return timedelta(minutes
=1439)
1826 def tzname(self
, dt
): return "aname"
1827 t
= cls(1, 1, 1, tzinfo
=C3())
1828 self
.assertEqual(t
.utcoffset(), timedelta(minutes
=-1439))
1829 self
.assertEqual(t
.dst(), timedelta(minutes
=1439))
1830 self
.assertEqual(t
.tzname(), "aname")
1834 def utcoffset(self
, dt
): return "aname"
1835 def dst(self
, dt
): return 7
1836 def tzname(self
, dt
): return 0
1837 t
= cls(1, 1, 1, tzinfo
=C4())
1838 self
.assertRaises(TypeError, t
.utcoffset
)
1839 self
.assertRaises(TypeError, t
.dst
)
1840 self
.assertRaises(TypeError, t
.tzname
)
1842 # Offset out of range.
1844 def utcoffset(self
, dt
): return timedelta(hours
=-24)
1845 def dst(self
, dt
): return timedelta(hours
=24)
1846 t
= cls(1, 1, 1, tzinfo
=C6())
1847 self
.assertRaises(ValueError, t
.utcoffset
)
1848 self
.assertRaises(ValueError, t
.dst
)
1850 # Not a whole number of minutes.
1852 def utcoffset(self
, dt
): return timedelta(seconds
=61)
1853 def dst(self
, dt
): return timedelta(microseconds
=-81)
1854 t
= cls(1, 1, 1, tzinfo
=C7())
1855 self
.assertRaises(ValueError, t
.utcoffset
)
1856 self
.assertRaises(ValueError, t
.dst
)
1858 def test_aware_compare(self
):
1861 # Ensure that utcoffset() gets ignored if the comparands have
1862 # the same tzinfo member.
1863 class OperandDependentOffset(tzinfo
):
1864 def utcoffset(self
, t
):
1866 # d0 and d1 equal after adjustment
1867 return timedelta(minutes
=t
.minute
)
1869 # d2 off in the weeds
1870 return timedelta(minutes
=59)
1872 base
= cls(8, 9, 10, tzinfo
=OperandDependentOffset())
1873 d0
= base
.replace(minute
=3)
1874 d1
= base
.replace(minute
=9)
1875 d2
= base
.replace(minute
=11)
1876 for x
in d0
, d1
, d2
:
1877 for y
in d0
, d1
, d2
:
1879 expected
= cmp(x
.minute
, y
.minute
)
1880 self
.assertEqual(got
, expected
)
1882 # However, if they're different members, uctoffset is not ignored.
1883 # Note that a time can't actually have an operand-depedent offset,
1884 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
1885 # so skip this test for time.
1887 d0
= base
.replace(minute
=3, tzinfo
=OperandDependentOffset())
1888 d1
= base
.replace(minute
=9, tzinfo
=OperandDependentOffset())
1889 d2
= base
.replace(minute
=11, tzinfo
=OperandDependentOffset())
1890 for x
in d0
, d1
, d2
:
1891 for y
in d0
, d1
, d2
:
1893 if (x
is d0
or x
is d1
) and (y
is d0
or y
is d1
):
1902 self
.assertEqual(got
, expected
)
1905 # Testing time objects with a non-None tzinfo.
1906 class TestTimeTZ(TestTime
, TZInfoBase
):
1909 def test_empty(self
):
1911 self
.assertEqual(t
.hour
, 0)
1912 self
.assertEqual(t
.minute
, 0)
1913 self
.assertEqual(t
.second
, 0)
1914 self
.assertEqual(t
.microsecond
, 0)
1915 self
.failUnless(t
.tzinfo
is None)
1917 def test_zones(self
):
1918 est
= FixedOffset(-300, "EST", 1)
1919 utc
= FixedOffset(0, "UTC", -2)
1920 met
= FixedOffset(60, "MET", 3)
1921 t1
= time( 7, 47, tzinfo
=est
)
1922 t2
= time(12, 47, tzinfo
=utc
)
1923 t3
= time(13, 47, tzinfo
=met
)
1924 t4
= time(microsecond
=40)
1925 t5
= time(microsecond
=40, tzinfo
=utc
)
1927 self
.assertEqual(t1
.tzinfo
, est
)
1928 self
.assertEqual(t2
.tzinfo
, utc
)
1929 self
.assertEqual(t3
.tzinfo
, met
)
1930 self
.failUnless(t4
.tzinfo
is None)
1931 self
.assertEqual(t5
.tzinfo
, utc
)
1933 self
.assertEqual(t1
.utcoffset(), timedelta(minutes
=-300))
1934 self
.assertEqual(t2
.utcoffset(), timedelta(minutes
=0))
1935 self
.assertEqual(t3
.utcoffset(), timedelta(minutes
=60))
1936 self
.failUnless(t4
.utcoffset() is None)
1937 self
.assertRaises(TypeError, t1
.utcoffset
, "no args")
1939 self
.assertEqual(t1
.tzname(), "EST")
1940 self
.assertEqual(t2
.tzname(), "UTC")
1941 self
.assertEqual(t3
.tzname(), "MET")
1942 self
.failUnless(t4
.tzname() is None)
1943 self
.assertRaises(TypeError, t1
.tzname
, "no args")
1945 self
.assertEqual(t1
.dst(), timedelta(minutes
=1))
1946 self
.assertEqual(t2
.dst(), timedelta(minutes
=-2))
1947 self
.assertEqual(t3
.dst(), timedelta(minutes
=3))
1948 self
.failUnless(t4
.dst() is None)
1949 self
.assertRaises(TypeError, t1
.dst
, "no args")
1951 self
.assertEqual(hash(t1
), hash(t2
))
1952 self
.assertEqual(hash(t1
), hash(t3
))
1953 self
.assertEqual(hash(t2
), hash(t3
))
1955 self
.assertEqual(t1
, t2
)
1956 self
.assertEqual(t1
, t3
)
1957 self
.assertEqual(t2
, t3
)
1958 self
.assertRaises(TypeError, lambda: t4
== t5
) # mixed tz-aware & naive
1959 self
.assertRaises(TypeError, lambda: t4
< t5
) # mixed tz-aware & naive
1960 self
.assertRaises(TypeError, lambda: t5
< t4
) # mixed tz-aware & naive
1962 self
.assertEqual(str(t1
), "07:47:00-05:00")
1963 self
.assertEqual(str(t2
), "12:47:00+00:00")
1964 self
.assertEqual(str(t3
), "13:47:00+01:00")
1965 self
.assertEqual(str(t4
), "00:00:00.000040")
1966 self
.assertEqual(str(t5
), "00:00:00.000040+00:00")
1968 self
.assertEqual(t1
.isoformat(), "07:47:00-05:00")
1969 self
.assertEqual(t2
.isoformat(), "12:47:00+00:00")
1970 self
.assertEqual(t3
.isoformat(), "13:47:00+01:00")
1971 self
.assertEqual(t4
.isoformat(), "00:00:00.000040")
1972 self
.assertEqual(t5
.isoformat(), "00:00:00.000040+00:00")
1975 self
.assertEqual(repr(t1
), d
+ "(7, 47, tzinfo=est)")
1976 self
.assertEqual(repr(t2
), d
+ "(12, 47, tzinfo=utc)")
1977 self
.assertEqual(repr(t3
), d
+ "(13, 47, tzinfo=met)")
1978 self
.assertEqual(repr(t4
), d
+ "(0, 0, 0, 40)")
1979 self
.assertEqual(repr(t5
), d
+ "(0, 0, 0, 40, tzinfo=utc)")
1981 self
.assertEqual(t1
.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
1982 "07:47:00 %Z=EST %z=-0500")
1983 self
.assertEqual(t2
.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
1984 self
.assertEqual(t3
.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
1986 yuck
= FixedOffset(-1439, "%z %Z %%z%%Z")
1987 t1
= time(23, 59, tzinfo
=yuck
)
1988 self
.assertEqual(t1
.strftime("%H:%M %%Z='%Z' %%z='%z'"),
1989 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
1991 # Check that an invalid tzname result raises an exception.
1992 class Badtzname(tzinfo
):
1993 def tzname(self
, dt
): return 42
1994 t
= time(2, 3, 4, tzinfo
=Badtzname())
1995 self
.assertEqual(t
.strftime("%H:%M:%S"), "02:03:04")
1996 self
.assertRaises(TypeError, t
.strftime
, "%Z")
1998 def test_hash_edge_cases(self
):
1999 # Offsets that overflow a basic time.
2000 t1
= self
.theclass(0, 1, 2, 3, tzinfo
=FixedOffset(1439, ""))
2001 t2
= self
.theclass(0, 0, 2, 3, tzinfo
=FixedOffset(1438, ""))
2002 self
.assertEqual(hash(t1
), hash(t2
))
2004 t1
= self
.theclass(23, 58, 6, 100, tzinfo
=FixedOffset(-1000, ""))
2005 t2
= self
.theclass(23, 48, 6, 100, tzinfo
=FixedOffset(-1010, ""))
2006 self
.assertEqual(hash(t1
), hash(t2
))
2008 def test_pickling(self
):
2009 # Try one without a tzinfo.
2010 args
= 20, 59, 16, 64**2
2011 orig
= self
.theclass(*args
)
2012 for pickler
, unpickler
, proto
in pickle_choices
:
2013 green
= pickler
.dumps(orig
, proto
)
2014 derived
= unpickler
.loads(green
)
2015 self
.assertEqual(orig
, derived
)
2017 # Try one with a tzinfo.
2018 tinfo
= PicklableFixedOffset(-300, 'cookie')
2019 orig
= self
.theclass(5, 6, 7, tzinfo
=tinfo
)
2020 for pickler
, unpickler
, proto
in pickle_choices
:
2021 green
= pickler
.dumps(orig
, proto
)
2022 derived
= unpickler
.loads(green
)
2023 self
.assertEqual(orig
, derived
)
2024 self
.failUnless(isinstance(derived
.tzinfo
, PicklableFixedOffset
))
2025 self
.assertEqual(derived
.utcoffset(), timedelta(minutes
=-300))
2026 self
.assertEqual(derived
.tzname(), 'cookie')
2028 def test_more_bool(self
):
2029 # Test cases with non-None tzinfo.
2032 t
= cls(0, tzinfo
=FixedOffset(-300, ""))
2035 t
= cls(5, tzinfo
=FixedOffset(-300, ""))
2038 t
= cls(5, tzinfo
=FixedOffset(300, ""))
2039 self
.failUnless(not t
)
2041 t
= cls(23, 59, tzinfo
=FixedOffset(23*60 + 59, ""))
2042 self
.failUnless(not t
)
2044 # Mostly ensuring this doesn't overflow internally.
2045 t
= cls(0, tzinfo
=FixedOffset(23*60 + 59, ""))
2048 # But this should yield a value error -- the utcoffset is bogus.
2049 t
= cls(0, tzinfo
=FixedOffset(24*60, ""))
2050 self
.assertRaises(ValueError, lambda: bool(t
))
2053 t
= cls(0, tzinfo
=FixedOffset(-24*60, ""))
2054 self
.assertRaises(ValueError, lambda: bool(t
))
2056 def test_replace(self
):
2058 z100
= FixedOffset(100, "+100")
2059 zm200
= FixedOffset(timedelta(minutes
=-200), "-200")
2060 args
= [1, 2, 3, 4, z100
]
2062 self
.assertEqual(base
, base
.replace())
2065 for name
, newval
in (("hour", 5),
2072 expected
= cls(*newargs
)
2073 got
= base
.replace(**{name
: newval
})
2074 self
.assertEqual(expected
, got
)
2077 # Ensure we can get rid of a tzinfo.
2078 self
.assertEqual(base
.tzname(), "+100")
2079 base2
= base
.replace(tzinfo
=None)
2080 self
.failUnless(base2
.tzinfo
is None)
2081 self
.failUnless(base2
.tzname() is None)
2083 # Ensure we can add one.
2084 base3
= base2
.replace(tzinfo
=z100
)
2085 self
.assertEqual(base
, base3
)
2086 self
.failUnless(base
.tzinfo
is base3
.tzinfo
)
2090 self
.assertRaises(ValueError, base
.replace
, hour
=24)
2091 self
.assertRaises(ValueError, base
.replace
, minute
=-1)
2092 self
.assertRaises(ValueError, base
.replace
, second
=100)
2093 self
.assertRaises(ValueError, base
.replace
, microsecond
=1000000)
2095 def test_mixed_compare(self
):
2098 self
.assertEqual(t1
, t2
)
2099 t2
= t2
.replace(tzinfo
=None)
2100 self
.assertEqual(t1
, t2
)
2101 t2
= t2
.replace(tzinfo
=FixedOffset(None, ""))
2102 self
.assertEqual(t1
, t2
)
2103 t2
= t2
.replace(tzinfo
=FixedOffset(0, ""))
2104 self
.assertRaises(TypeError, lambda: t1
== t2
)
2106 # In time w/ identical tzinfo objects, utcoffset is ignored.
2107 class Varies(tzinfo
):
2109 self
.offset
= timedelta(minutes
=22)
2110 def utcoffset(self
, t
):
2111 self
.offset
+= timedelta(minutes
=1)
2115 t1
= t2
.replace(tzinfo
=v
)
2116 t2
= t2
.replace(tzinfo
=v
)
2117 self
.assertEqual(t1
.utcoffset(), timedelta(minutes
=23))
2118 self
.assertEqual(t2
.utcoffset(), timedelta(minutes
=24))
2119 self
.assertEqual(t1
, t2
)
2121 # But if they're not identical, it isn't ignored.
2122 t2
= t2
.replace(tzinfo
=Varies())
2123 self
.failUnless(t1
< t2
) # t1's offset counter still going up
2125 def test_subclass_timetz(self
):
2127 class C(self
.theclass
):
2130 def __new__(cls
, *args
, **kws
):
2132 extra
= temp
.pop('extra')
2133 result
= self
.theclass
.__new
__(cls
, *args
, **temp
)
2134 result
.extra
= extra
2137 def newmeth(self
, start
):
2138 return start
+ self
.hour
+ self
.second
2140 args
= 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2142 dt1
= self
.theclass(*args
)
2143 dt2
= C(*args
, **{'extra': 7})
2145 self
.assertEqual(dt2
.__class__
, C
)
2146 self
.assertEqual(dt2
.theAnswer
, 42)
2147 self
.assertEqual(dt2
.extra
, 7)
2148 self
.assertEqual(dt1
.utcoffset(), dt2
.utcoffset())
2149 self
.assertEqual(dt2
.newmeth(-7), dt1
.hour
+ dt1
.second
- 7)
2152 # Testing datetime objects with a non-None tzinfo.
2154 class TestDateTimeTZ(TestDateTime
, TZInfoBase
):
2157 def test_trivial(self
):
2158 dt
= self
.theclass(1, 2, 3, 4, 5, 6, 7)
2159 self
.assertEqual(dt
.year
, 1)
2160 self
.assertEqual(dt
.month
, 2)
2161 self
.assertEqual(dt
.day
, 3)
2162 self
.assertEqual(dt
.hour
, 4)
2163 self
.assertEqual(dt
.minute
, 5)
2164 self
.assertEqual(dt
.second
, 6)
2165 self
.assertEqual(dt
.microsecond
, 7)
2166 self
.assertEqual(dt
.tzinfo
, None)
2168 def test_even_more_compare(self
):
2169 # The test_compare() and test_more_compare() inherited from TestDate
2170 # and TestDateTime covered non-tzinfo cases.
2172 # Smallest possible after UTC adjustment.
2173 t1
= self
.theclass(1, 1, 1, tzinfo
=FixedOffset(1439, ""))
2174 # Largest possible after UTC adjustment.
2175 t2
= self
.theclass(MAXYEAR
, 12, 31, 23, 59, 59, 999999,
2176 tzinfo
=FixedOffset(-1439, ""))
2178 # Make sure those compare correctly, and w/o overflow.
2179 self
.failUnless(t1
< t2
)
2180 self
.failUnless(t1
!= t2
)
2181 self
.failUnless(t2
> t1
)
2183 self
.failUnless(t1
== t1
)
2184 self
.failUnless(t2
== t2
)
2186 # Equal afer adjustment.
2187 t1
= self
.theclass(1, 12, 31, 23, 59, tzinfo
=FixedOffset(1, ""))
2188 t2
= self
.theclass(2, 1, 1, 3, 13, tzinfo
=FixedOffset(3*60+13+2, ""))
2189 self
.assertEqual(t1
, t2
)
2191 # Change t1 not to subtract a minute, and t1 should be larger.
2192 t1
= self
.theclass(1, 12, 31, 23, 59, tzinfo
=FixedOffset(0, ""))
2193 self
.failUnless(t1
> t2
)
2195 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2196 t1
= self
.theclass(1, 12, 31, 23, 59, tzinfo
=FixedOffset(2, ""))
2197 self
.failUnless(t1
< t2
)
2199 # Back to the original t1, but make seconds resolve it.
2200 t1
= self
.theclass(1, 12, 31, 23, 59, tzinfo
=FixedOffset(1, ""),
2202 self
.failUnless(t1
> t2
)
2204 # Likewise, but make microseconds resolve it.
2205 t1
= self
.theclass(1, 12, 31, 23, 59, tzinfo
=FixedOffset(1, ""),
2207 self
.failUnless(t1
> t2
)
2209 # Make t2 naive and it should fail.
2210 t2
= self
.theclass
.min
2211 self
.assertRaises(TypeError, lambda: t1
== t2
)
2212 self
.assertEqual(t2
, t2
)
2214 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2215 class Naive(tzinfo
):
2216 def utcoffset(self
, dt
): return None
2217 t2
= self
.theclass(5, 6, 7, tzinfo
=Naive())
2218 self
.assertRaises(TypeError, lambda: t1
== t2
)
2219 self
.assertEqual(t2
, t2
)
2221 # OTOH, it's OK to compare two of these mixing the two ways of being
2223 t1
= self
.theclass(5, 6, 7)
2224 self
.assertEqual(t1
, t2
)
2226 # Try a bogus uctoffset.
2227 class Bogus(tzinfo
):
2228 def utcoffset(self
, dt
):
2229 return timedelta(minutes
=1440) # out of bounds
2230 t1
= self
.theclass(2, 2, 2, tzinfo
=Bogus())
2231 t2
= self
.theclass(2, 2, 2, tzinfo
=FixedOffset(0, ""))
2232 self
.assertRaises(ValueError, lambda: t1
== t2
)
2234 def test_pickling(self
):
2235 # Try one without a tzinfo.
2236 args
= 6, 7, 23, 20, 59, 1, 64**2
2237 orig
= self
.theclass(*args
)
2238 for pickler
, unpickler
, proto
in pickle_choices
:
2239 green
= pickler
.dumps(orig
, proto
)
2240 derived
= unpickler
.loads(green
)
2241 self
.assertEqual(orig
, derived
)
2243 # Try one with a tzinfo.
2244 tinfo
= PicklableFixedOffset(-300, 'cookie')
2245 orig
= self
.theclass(*args
, **{'tzinfo': tinfo
})
2246 derived
= self
.theclass(1, 1, 1, tzinfo
=FixedOffset(0, "", 0))
2247 for pickler
, unpickler
, proto
in pickle_choices
:
2248 green
= pickler
.dumps(orig
, proto
)
2249 derived
= unpickler
.loads(green
)
2250 self
.assertEqual(orig
, derived
)
2251 self
.failUnless(isinstance(derived
.tzinfo
,
2252 PicklableFixedOffset
))
2253 self
.assertEqual(derived
.utcoffset(), timedelta(minutes
=-300))
2254 self
.assertEqual(derived
.tzname(), 'cookie')
2256 def test_extreme_hashes(self
):
2257 # If an attempt is made to hash these via subtracting the offset
2258 # then hashing a datetime object, OverflowError results. The
2259 # Python implementation used to blow up here.
2260 t
= self
.theclass(1, 1, 1, tzinfo
=FixedOffset(1439, ""))
2262 t
= self
.theclass(MAXYEAR
, 12, 31, 23, 59, 59, 999999,
2263 tzinfo
=FixedOffset(-1439, ""))
2266 # OTOH, an OOB offset should blow up.
2267 t
= self
.theclass(5, 5, 5, tzinfo
=FixedOffset(-1440, ""))
2268 self
.assertRaises(ValueError, hash, t
)
2270 def test_zones(self
):
2271 est
= FixedOffset(-300, "EST")
2272 utc
= FixedOffset(0, "UTC")
2273 met
= FixedOffset(60, "MET")
2274 t1
= datetime(2002, 3, 19, 7, 47, tzinfo
=est
)
2275 t2
= datetime(2002, 3, 19, 12, 47, tzinfo
=utc
)
2276 t3
= datetime(2002, 3, 19, 13, 47, tzinfo
=met
)
2277 self
.assertEqual(t1
.tzinfo
, est
)
2278 self
.assertEqual(t2
.tzinfo
, utc
)
2279 self
.assertEqual(t3
.tzinfo
, met
)
2280 self
.assertEqual(t1
.utcoffset(), timedelta(minutes
=-300))
2281 self
.assertEqual(t2
.utcoffset(), timedelta(minutes
=0))
2282 self
.assertEqual(t3
.utcoffset(), timedelta(minutes
=60))
2283 self
.assertEqual(t1
.tzname(), "EST")
2284 self
.assertEqual(t2
.tzname(), "UTC")
2285 self
.assertEqual(t3
.tzname(), "MET")
2286 self
.assertEqual(hash(t1
), hash(t2
))
2287 self
.assertEqual(hash(t1
), hash(t3
))
2288 self
.assertEqual(hash(t2
), hash(t3
))
2289 self
.assertEqual(t1
, t2
)
2290 self
.assertEqual(t1
, t3
)
2291 self
.assertEqual(t2
, t3
)
2292 self
.assertEqual(str(t1
), "2002-03-19 07:47:00-05:00")
2293 self
.assertEqual(str(t2
), "2002-03-19 12:47:00+00:00")
2294 self
.assertEqual(str(t3
), "2002-03-19 13:47:00+01:00")
2295 d
= 'datetime.datetime(2002, 3, 19, '
2296 self
.assertEqual(repr(t1
), d
+ "7, 47, tzinfo=est)")
2297 self
.assertEqual(repr(t2
), d
+ "12, 47, tzinfo=utc)")
2298 self
.assertEqual(repr(t3
), d
+ "13, 47, tzinfo=met)")
2300 def test_combine(self
):
2301 met
= FixedOffset(60, "MET")
2302 d
= date(2002, 3, 4)
2303 tz
= time(18, 45, 3, 1234, tzinfo
=met
)
2304 dt
= datetime
.combine(d
, tz
)
2305 self
.assertEqual(dt
, datetime(2002, 3, 4, 18, 45, 3, 1234,
2308 def test_extract(self
):
2309 met
= FixedOffset(60, "MET")
2310 dt
= self
.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo
=met
)
2311 self
.assertEqual(dt
.date(), date(2002, 3, 4))
2312 self
.assertEqual(dt
.time(), time(18, 45, 3, 1234))
2313 self
.assertEqual(dt
.timetz(), time(18, 45, 3, 1234, tzinfo
=met
))
2315 def test_tz_aware_arithmetic(self
):
2318 now
= self
.theclass
.now()
2319 tz55
= FixedOffset(-330, "west 5:30")
2320 timeaware
= now
.time().replace(tzinfo
=tz55
)
2321 nowaware
= self
.theclass
.combine(now
.date(), timeaware
)
2322 self
.failUnless(nowaware
.tzinfo
is tz55
)
2323 self
.assertEqual(nowaware
.timetz(), timeaware
)
2325 # Can't mix aware and non-aware.
2326 self
.assertRaises(TypeError, lambda: now
- nowaware
)
2327 self
.assertRaises(TypeError, lambda: nowaware
- now
)
2329 # And adding datetime's doesn't make sense, aware or not.
2330 self
.assertRaises(TypeError, lambda: now
+ nowaware
)
2331 self
.assertRaises(TypeError, lambda: nowaware
+ now
)
2332 self
.assertRaises(TypeError, lambda: nowaware
+ nowaware
)
2334 # Subtracting should yield 0.
2335 self
.assertEqual(now
- now
, timedelta(0))
2336 self
.assertEqual(nowaware
- nowaware
, timedelta(0))
2338 # Adding a delta should preserve tzinfo.
2339 delta
= timedelta(weeks
=1, minutes
=12, microseconds
=5678)
2340 nowawareplus
= nowaware
+ delta
2341 self
.failUnless(nowaware
.tzinfo
is tz55
)
2342 nowawareplus2
= delta
+ nowaware
2343 self
.failUnless(nowawareplus2
.tzinfo
is tz55
)
2344 self
.assertEqual(nowawareplus
, nowawareplus2
)
2346 # that - delta should be what we started with, and that - what we
2347 # started with should be delta.
2348 diff
= nowawareplus
- delta
2349 self
.failUnless(diff
.tzinfo
is tz55
)
2350 self
.assertEqual(nowaware
, diff
)
2351 self
.assertRaises(TypeError, lambda: delta
- nowawareplus
)
2352 self
.assertEqual(nowawareplus
- nowaware
, delta
)
2354 # Make up a random timezone.
2355 tzr
= FixedOffset(random
.randrange(-1439, 1440), "randomtimezone")
2356 # Attach it to nowawareplus.
2357 nowawareplus
= nowawareplus
.replace(tzinfo
=tzr
)
2358 self
.failUnless(nowawareplus
.tzinfo
is tzr
)
2359 # Make sure the difference takes the timezone adjustments into account.
2360 got
= nowaware
- nowawareplus
2361 # Expected: (nowaware base - nowaware offset) -
2362 # (nowawareplus base - nowawareplus offset) =
2363 # (nowaware base - nowawareplus base) +
2364 # (nowawareplus offset - nowaware offset) =
2365 # -delta + nowawareplus offset - nowaware offset
2366 expected
= nowawareplus
.utcoffset() - nowaware
.utcoffset() - delta
2367 self
.assertEqual(got
, expected
)
2369 # Try max possible difference.
2370 min = self
.theclass(1, 1, 1, tzinfo
=FixedOffset(1439, "min"))
2371 max = self
.theclass(MAXYEAR
, 12, 31, 23, 59, 59, 999999,
2372 tzinfo
=FixedOffset(-1439, "max"))
2374 self
.assertEqual(maxdiff
, self
.theclass
.max - self
.theclass
.min +
2375 timedelta(minutes
=2*1439))
2377 def test_tzinfo_now(self
):
2378 meth
= self
.theclass
.now
2379 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2381 # Try with and without naming the keyword.
2382 off42
= FixedOffset(42, "42")
2383 another
= meth(off42
)
2384 again
= meth(tz
=off42
)
2385 self
.failUnless(another
.tzinfo
is again
.tzinfo
)
2386 self
.assertEqual(another
.utcoffset(), timedelta(minutes
=42))
2387 # Bad argument with and w/o naming the keyword.
2388 self
.assertRaises(TypeError, meth
, 16)
2389 self
.assertRaises(TypeError, meth
, tzinfo
=16)
2391 self
.assertRaises(TypeError, meth
, tinfo
=off42
)
2393 self
.assertRaises(TypeError, meth
, off42
, off42
)
2395 # We don't know which time zone we're in, and don't have a tzinfo
2396 # class to represent it, so seeing whether a tz argument actually
2397 # does a conversion is tricky.
2398 weirdtz
= FixedOffset(timedelta(hours
=15, minutes
=58), "weirdtz", 0)
2399 utc
= FixedOffset(0, "utc", 0)
2400 for dummy
in range(3):
2401 now
= datetime
.now(weirdtz
)
2402 self
.failUnless(now
.tzinfo
is weirdtz
)
2403 utcnow
= datetime
.utcnow().replace(tzinfo
=utc
)
2404 now2
= utcnow
.astimezone(weirdtz
)
2405 if abs(now
- now2
) < timedelta(seconds
=30):
2407 # Else the code is broken, or more than 30 seconds passed between
2408 # calls; assuming the latter, just try again.
2410 # Three strikes and we're out.
2411 self
.fail("utcnow(), now(tz), or astimezone() may be broken")
2413 def test_tzinfo_fromtimestamp(self
):
2415 meth
= self
.theclass
.fromtimestamp
2417 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2419 # Try with and without naming the keyword.
2420 off42
= FixedOffset(42, "42")
2421 another
= meth(ts
, off42
)
2422 again
= meth(ts
, tz
=off42
)
2423 self
.failUnless(another
.tzinfo
is again
.tzinfo
)
2424 self
.assertEqual(another
.utcoffset(), timedelta(minutes
=42))
2425 # Bad argument with and w/o naming the keyword.
2426 self
.assertRaises(TypeError, meth
, ts
, 16)
2427 self
.assertRaises(TypeError, meth
, ts
, tzinfo
=16)
2429 self
.assertRaises(TypeError, meth
, ts
, tinfo
=off42
)
2431 self
.assertRaises(TypeError, meth
, ts
, off42
, off42
)
2433 self
.assertRaises(TypeError, meth
)
2435 # Try to make sure tz= actually does some conversion.
2436 timestamp
= 1000000000
2437 utcdatetime
= datetime
.utcfromtimestamp(timestamp
)
2438 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2439 # But on some flavor of Mac, it's nowhere near that. So we can't have
2440 # any idea here what time that actually is, we can only test that
2441 # relative changes match.
2442 utcoffset
= timedelta(hours
=-15, minutes
=39) # arbitrary, but not zero
2443 tz
= FixedOffset(utcoffset
, "tz", 0)
2444 expected
= utcdatetime
+ utcoffset
2445 got
= datetime
.fromtimestamp(timestamp
, tz
)
2446 self
.assertEqual(expected
, got
.replace(tzinfo
=None))
2448 def test_tzinfo_utcnow(self
):
2449 meth
= self
.theclass
.utcnow
2450 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2452 # Try with and without naming the keyword; for whatever reason,
2453 # utcnow() doesn't accept a tzinfo argument.
2454 off42
= FixedOffset(42, "42")
2455 self
.assertRaises(TypeError, meth
, off42
)
2456 self
.assertRaises(TypeError, meth
, tzinfo
=off42
)
2458 def test_tzinfo_utcfromtimestamp(self
):
2460 meth
= self
.theclass
.utcfromtimestamp
2462 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2464 # Try with and without naming the keyword; for whatever reason,
2465 # utcfromtimestamp() doesn't accept a tzinfo argument.
2466 off42
= FixedOffset(42, "42")
2467 self
.assertRaises(TypeError, meth
, ts
, off42
)
2468 self
.assertRaises(TypeError, meth
, ts
, tzinfo
=off42
)
2470 def test_tzinfo_timetuple(self
):
2471 # TestDateTime tested most of this. datetime adds a twist to the
2474 def __init__(self
, dstvalue
):
2475 if isinstance(dstvalue
, int):
2476 dstvalue
= timedelta(minutes
=dstvalue
)
2477 self
.dstvalue
= dstvalue
2479 return self
.dstvalue
2482 for dstvalue
, flag
in (-33, 1), (33, 1), (0, 0), (None, -1):
2483 d
= cls(1, 1, 1, 10, 20, 30, 40, tzinfo
=DST(dstvalue
))
2485 self
.assertEqual(1, t
.tm_year
)
2486 self
.assertEqual(1, t
.tm_mon
)
2487 self
.assertEqual(1, t
.tm_mday
)
2488 self
.assertEqual(10, t
.tm_hour
)
2489 self
.assertEqual(20, t
.tm_min
)
2490 self
.assertEqual(30, t
.tm_sec
)
2491 self
.assertEqual(0, t
.tm_wday
)
2492 self
.assertEqual(1, t
.tm_yday
)
2493 self
.assertEqual(flag
, t
.tm_isdst
)
2495 # dst() returns wrong type.
2496 self
.assertRaises(TypeError, cls(1, 1, 1, tzinfo
=DST("x")).timetuple
)
2498 # dst() at the edge.
2499 self
.assertEqual(cls(1,1,1, tzinfo
=DST(1439)).timetuple().tm_isdst
, 1)
2500 self
.assertEqual(cls(1,1,1, tzinfo
=DST(-1439)).timetuple().tm_isdst
, 1)
2502 # dst() out of range.
2503 self
.assertRaises(ValueError, cls(1,1,1, tzinfo
=DST(1440)).timetuple
)
2504 self
.assertRaises(ValueError, cls(1,1,1, tzinfo
=DST(-1440)).timetuple
)
2506 def test_utctimetuple(self
):
2508 def __init__(self
, dstvalue
):
2509 if isinstance(dstvalue
, int):
2510 dstvalue
= timedelta(minutes
=dstvalue
)
2511 self
.dstvalue
= dstvalue
2513 return self
.dstvalue
2516 # This can't work: DST didn't implement utcoffset.
2517 self
.assertRaises(NotImplementedError,
2518 cls(1, 1, 1, tzinfo
=DST(0)).utcoffset
)
2521 def __init__(self
, uofs
, dofs
=None):
2522 DST
.__init
__(self
, dofs
)
2523 self
.uofs
= timedelta(minutes
=uofs
)
2524 def utcoffset(self
, dt
):
2527 # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
2528 # in effect for a UTC time.
2529 for dstvalue
in -33, 33, 0, None:
2530 d
= cls(1, 2, 3, 10, 20, 30, 40, tzinfo
=UOFS(-53, dstvalue
))
2531 t
= d
.utctimetuple()
2532 self
.assertEqual(d
.year
, t
.tm_year
)
2533 self
.assertEqual(d
.month
, t
.tm_mon
)
2534 self
.assertEqual(d
.day
, t
.tm_mday
)
2535 self
.assertEqual(11, t
.tm_hour
) # 20mm + 53mm = 1hn + 13mm
2536 self
.assertEqual(13, t
.tm_min
)
2537 self
.assertEqual(d
.second
, t
.tm_sec
)
2538 self
.assertEqual(d
.weekday(), t
.tm_wday
)
2539 self
.assertEqual(d
.toordinal() - date(1, 1, 1).toordinal() + 1,
2541 self
.assertEqual(0, t
.tm_isdst
)
2543 # At the edges, UTC adjustment can normalize into years out-of-range
2544 # for a datetime object. Ensure that a correct timetuple is
2546 tiny
= cls(MINYEAR
, 1, 1, 0, 0, 37, tzinfo
=UOFS(1439))
2547 # That goes back 1 minute less than a full day.
2548 t
= tiny
.utctimetuple()
2549 self
.assertEqual(t
.tm_year
, MINYEAR
-1)
2550 self
.assertEqual(t
.tm_mon
, 12)
2551 self
.assertEqual(t
.tm_mday
, 31)
2552 self
.assertEqual(t
.tm_hour
, 0)
2553 self
.assertEqual(t
.tm_min
, 1)
2554 self
.assertEqual(t
.tm_sec
, 37)
2555 self
.assertEqual(t
.tm_yday
, 366) # "year 0" is a leap year
2556 self
.assertEqual(t
.tm_isdst
, 0)
2558 huge
= cls(MAXYEAR
, 12, 31, 23, 59, 37, 999999, tzinfo
=UOFS(-1439))
2559 # That goes forward 1 minute less than a full day.
2560 t
= huge
.utctimetuple()
2561 self
.assertEqual(t
.tm_year
, MAXYEAR
+1)
2562 self
.assertEqual(t
.tm_mon
, 1)
2563 self
.assertEqual(t
.tm_mday
, 1)
2564 self
.assertEqual(t
.tm_hour
, 23)
2565 self
.assertEqual(t
.tm_min
, 58)
2566 self
.assertEqual(t
.tm_sec
, 37)
2567 self
.assertEqual(t
.tm_yday
, 1)
2568 self
.assertEqual(t
.tm_isdst
, 0)
2570 def test_tzinfo_isoformat(self
):
2571 zero
= FixedOffset(0, "+00:00")
2572 plus
= FixedOffset(220, "+03:40")
2573 minus
= FixedOffset(-231, "-03:51")
2574 unknown
= FixedOffset(None, "")
2577 datestr
= '0001-02-03'
2578 for ofs
in None, zero
, plus
, minus
, unknown
:
2579 for us
in 0, 987001:
2580 d
= cls(1, 2, 3, 4, 5, 59, us
, tzinfo
=ofs
)
2581 timestr
= '04:05:59' + (us
and '.987001' or '')
2582 ofsstr
= ofs
is not None and d
.tzname() or ''
2583 tailstr
= timestr
+ ofsstr
2585 self
.assertEqual(iso
, datestr
+ 'T' + tailstr
)
2586 self
.assertEqual(iso
, d
.isoformat('T'))
2587 self
.assertEqual(d
.isoformat('k'), datestr
+ 'k' + tailstr
)
2588 self
.assertEqual(str(d
), datestr
+ ' ' + tailstr
)
2590 def test_replace(self
):
2592 z100
= FixedOffset(100, "+100")
2593 zm200
= FixedOffset(timedelta(minutes
=-200), "-200")
2594 args
= [1, 2, 3, 4, 5, 6, 7, z100
]
2596 self
.assertEqual(base
, base
.replace())
2599 for name
, newval
in (("year", 2),
2609 expected
= cls(*newargs
)
2610 got
= base
.replace(**{name
: newval
})
2611 self
.assertEqual(expected
, got
)
2614 # Ensure we can get rid of a tzinfo.
2615 self
.assertEqual(base
.tzname(), "+100")
2616 base2
= base
.replace(tzinfo
=None)
2617 self
.failUnless(base2
.tzinfo
is None)
2618 self
.failUnless(base2
.tzname() is None)
2620 # Ensure we can add one.
2621 base3
= base2
.replace(tzinfo
=z100
)
2622 self
.assertEqual(base
, base3
)
2623 self
.failUnless(base
.tzinfo
is base3
.tzinfo
)
2626 base
= cls(2000, 2, 29)
2627 self
.assertRaises(ValueError, base
.replace
, year
=2001)
2629 def test_more_astimezone(self
):
2630 # The inherited test_astimezone covered some trivial and error cases.
2631 fnone
= FixedOffset(None, "None")
2632 f44m
= FixedOffset(44, "44")
2633 fm5h
= FixedOffset(-timedelta(hours
=5), "m300")
2635 dt
= self
.theclass
.now(tz
=f44m
)
2636 self
.failUnless(dt
.tzinfo
is f44m
)
2637 # Replacing with degenerate tzinfo raises an exception.
2638 self
.assertRaises(ValueError, dt
.astimezone
, fnone
)
2639 # Ditto with None tz.
2640 self
.assertRaises(TypeError, dt
.astimezone
, None)
2641 # Replacing with same tzinfo makes no change.
2642 x
= dt
.astimezone(dt
.tzinfo
)
2643 self
.failUnless(x
.tzinfo
is f44m
)
2644 self
.assertEqual(x
.date(), dt
.date())
2645 self
.assertEqual(x
.time(), dt
.time())
2647 # Replacing with different tzinfo does adjust.
2648 got
= dt
.astimezone(fm5h
)
2649 self
.failUnless(got
.tzinfo
is fm5h
)
2650 self
.assertEqual(got
.utcoffset(), timedelta(hours
=-5))
2651 expected
= dt
- dt
.utcoffset() # in effect, convert to UTC
2652 expected
+= fm5h
.utcoffset(dt
) # and from there to local time
2653 expected
= expected
.replace(tzinfo
=fm5h
) # and attach new tzinfo
2654 self
.assertEqual(got
.date(), expected
.date())
2655 self
.assertEqual(got
.time(), expected
.time())
2656 self
.assertEqual(got
.timetz(), expected
.timetz())
2657 self
.failUnless(got
.tzinfo
is expected
.tzinfo
)
2658 self
.assertEqual(got
, expected
)
2660 def test_aware_subtract(self
):
2663 # Ensure that utcoffset() is ignored when the operands have the
2664 # same tzinfo member.
2665 class OperandDependentOffset(tzinfo
):
2666 def utcoffset(self
, t
):
2668 # d0 and d1 equal after adjustment
2669 return timedelta(minutes
=t
.minute
)
2671 # d2 off in the weeds
2672 return timedelta(minutes
=59)
2674 base
= cls(8, 9, 10, 11, 12, 13, 14, tzinfo
=OperandDependentOffset())
2675 d0
= base
.replace(minute
=3)
2676 d1
= base
.replace(minute
=9)
2677 d2
= base
.replace(minute
=11)
2678 for x
in d0
, d1
, d2
:
2679 for y
in d0
, d1
, d2
:
2681 expected
= timedelta(minutes
=x
.minute
- y
.minute
)
2682 self
.assertEqual(got
, expected
)
2684 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
2686 base
= cls(8, 9, 10, 11, 12, 13, 14)
2687 d0
= base
.replace(minute
=3, tzinfo
=OperandDependentOffset())
2688 d1
= base
.replace(minute
=9, tzinfo
=OperandDependentOffset())
2689 d2
= base
.replace(minute
=11, tzinfo
=OperandDependentOffset())
2690 for x
in d0
, d1
, d2
:
2691 for y
in d0
, d1
, d2
:
2693 if (x
is d0
or x
is d1
) and (y
is d0
or y
is d1
):
2694 expected
= timedelta(0)
2696 expected
= timedelta(0)
2698 expected
= timedelta(minutes
=(11-59)-0)
2701 expected
= timedelta(minutes
=0-(11-59))
2702 self
.assertEqual(got
, expected
)
2704 def test_mixed_compare(self
):
2705 t1
= datetime(1, 2, 3, 4, 5, 6, 7)
2706 t2
= datetime(1, 2, 3, 4, 5, 6, 7)
2707 self
.assertEqual(t1
, t2
)
2708 t2
= t2
.replace(tzinfo
=None)
2709 self
.assertEqual(t1
, t2
)
2710 t2
= t2
.replace(tzinfo
=FixedOffset(None, ""))
2711 self
.assertEqual(t1
, t2
)
2712 t2
= t2
.replace(tzinfo
=FixedOffset(0, ""))
2713 self
.assertRaises(TypeError, lambda: t1
== t2
)
2715 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
2716 class Varies(tzinfo
):
2718 self
.offset
= timedelta(minutes
=22)
2719 def utcoffset(self
, t
):
2720 self
.offset
+= timedelta(minutes
=1)
2724 t1
= t2
.replace(tzinfo
=v
)
2725 t2
= t2
.replace(tzinfo
=v
)
2726 self
.assertEqual(t1
.utcoffset(), timedelta(minutes
=23))
2727 self
.assertEqual(t2
.utcoffset(), timedelta(minutes
=24))
2728 self
.assertEqual(t1
, t2
)
2730 # But if they're not identical, it isn't ignored.
2731 t2
= t2
.replace(tzinfo
=Varies())
2732 self
.failUnless(t1
< t2
) # t1's offset counter still going up
2734 def test_subclass_datetimetz(self
):
2736 class C(self
.theclass
):
2739 def __new__(cls
, *args
, **kws
):
2741 extra
= temp
.pop('extra')
2742 result
= self
.theclass
.__new
__(cls
, *args
, **temp
)
2743 result
.extra
= extra
2746 def newmeth(self
, start
):
2747 return start
+ self
.hour
+ self
.year
2749 args
= 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2751 dt1
= self
.theclass(*args
)
2752 dt2
= C(*args
, **{'extra': 7})
2754 self
.assertEqual(dt2
.__class__
, C
)
2755 self
.assertEqual(dt2
.theAnswer
, 42)
2756 self
.assertEqual(dt2
.extra
, 7)
2757 self
.assertEqual(dt1
.utcoffset(), dt2
.utcoffset())
2758 self
.assertEqual(dt2
.newmeth(-7), dt1
.hour
+ dt1
.year
- 7)
2760 # Pain to set up DST-aware tzinfo classes.
2762 def first_sunday_on_or_after(dt
):
2763 days_to_go
= 6 - dt
.weekday()
2765 dt
+= timedelta(days_to_go
)
2769 HOUR
= timedelta(hours
=1)
2770 DAY
= timedelta(days
=1)
2771 # In the US, DST starts at 2am (standard time) on the first Sunday in April.
2772 DSTSTART
= datetime(1, 4, 1, 2)
2773 # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
2774 # which is the first Sunday on or after Oct 25. Because we view 1:MM as
2775 # being standard time on that day, there is no spelling in local time of
2776 # the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
2777 DSTEND
= datetime(1, 10, 25, 1)
2779 class USTimeZone(tzinfo
):
2781 def __init__(self
, hours
, reprname
, stdname
, dstname
):
2782 self
.stdoffset
= timedelta(hours
=hours
)
2783 self
.reprname
= reprname
2784 self
.stdname
= stdname
2785 self
.dstname
= dstname
2788 return self
.reprname
2790 def tzname(self
, dt
):
2796 def utcoffset(self
, dt
):
2797 return self
.stdoffset
+ self
.dst(dt
)
2800 if dt
is None or dt
.tzinfo
is None:
2801 # An exception instead may be sensible here, in one or more of
2804 assert dt
.tzinfo
is self
2806 # Find first Sunday in April.
2807 start
= first_sunday_on_or_after(DSTSTART
.replace(year
=dt
.year
))
2808 assert start
.weekday() == 6 and start
.month
== 4 and start
.day
<= 7
2810 # Find last Sunday in October.
2811 end
= first_sunday_on_or_after(DSTEND
.replace(year
=dt
.year
))
2812 assert end
.weekday() == 6 and end
.month
== 10 and end
.day
>= 25
2814 # Can't compare naive to aware objects, so strip the timezone from
2816 if start
<= dt
.replace(tzinfo
=None) < end
:
2821 Eastern
= USTimeZone(-5, "Eastern", "EST", "EDT")
2822 Central
= USTimeZone(-6, "Central", "CST", "CDT")
2823 Mountain
= USTimeZone(-7, "Mountain", "MST", "MDT")
2824 Pacific
= USTimeZone(-8, "Pacific", "PST", "PDT")
2825 utc_real
= FixedOffset(0, "UTC", 0)
2826 # For better test coverage, we want another flavor of UTC that's west of
2827 # the Eastern and Pacific timezones.
2828 utc_fake
= FixedOffset(-12*60, "UTCfake", 0)
2830 class TestTimezoneConversions(unittest
.TestCase
):
2831 # The DST switch times for 2002, in std time.
2832 dston
= datetime(2002, 4, 7, 2)
2833 dstoff
= datetime(2002, 10, 27, 1)
2837 # Check a time that's inside DST.
2838 def checkinside(self
, dt
, tz
, utc
, dston
, dstoff
):
2839 self
.assertEqual(dt
.dst(), HOUR
)
2841 # Conversion to our own timezone is always an identity.
2842 self
.assertEqual(dt
.astimezone(tz
), dt
)
2844 asutc
= dt
.astimezone(utc
)
2845 there_and_back
= asutc
.astimezone(tz
)
2847 # Conversion to UTC and back isn't always an identity here,
2848 # because there are redundant spellings (in local time) of
2849 # UTC time when DST begins: the clock jumps from 1:59:59
2850 # to 3:00:00, and a local time of 2:MM:SS doesn't really
2851 # make sense then. The classes above treat 2:MM:SS as
2852 # daylight time then (it's "after 2am"), really an alias
2853 # for 1:MM:SS standard time. The latter form is what
2854 # conversion back from UTC produces.
2855 if dt
.date() == dston
.date() and dt
.hour
== 2:
2856 # We're in the redundant hour, and coming back from
2857 # UTC gives the 1:MM:SS standard-time spelling.
2858 self
.assertEqual(there_and_back
+ HOUR
, dt
)
2859 # Although during was considered to be in daylight
2860 # time, there_and_back is not.
2861 self
.assertEqual(there_and_back
.dst(), ZERO
)
2862 # They're the same times in UTC.
2863 self
.assertEqual(there_and_back
.astimezone(utc
),
2866 # We're not in the redundant hour.
2867 self
.assertEqual(dt
, there_and_back
)
2869 # Because we have a redundant spelling when DST begins, there is
2870 # (unforunately) an hour when DST ends that can't be spelled at all in
2871 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
2872 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
2873 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
2874 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
2875 # expressed in local time. Nevertheless, we want conversion back
2876 # from UTC to mimic the local clock's "repeat an hour" behavior.
2877 nexthour_utc
= asutc
+ HOUR
2878 nexthour_tz
= nexthour_utc
.astimezone(tz
)
2879 if dt
.date() == dstoff
.date() and dt
.hour
== 0:
2880 # We're in the hour before the last DST hour. The last DST hour
2881 # is ineffable. We want the conversion back to repeat 1:MM.
2882 self
.assertEqual(nexthour_tz
, dt
.replace(hour
=1))
2883 nexthour_utc
+= HOUR
2884 nexthour_tz
= nexthour_utc
.astimezone(tz
)
2885 self
.assertEqual(nexthour_tz
, dt
.replace(hour
=1))
2887 self
.assertEqual(nexthour_tz
- dt
, HOUR
)
2889 # Check a time that's outside DST.
2890 def checkoutside(self
, dt
, tz
, utc
):
2891 self
.assertEqual(dt
.dst(), ZERO
)
2893 # Conversion to our own timezone is always an identity.
2894 self
.assertEqual(dt
.astimezone(tz
), dt
)
2896 # Converting to UTC and back is an identity too.
2897 asutc
= dt
.astimezone(utc
)
2898 there_and_back
= asutc
.astimezone(tz
)
2899 self
.assertEqual(dt
, there_and_back
)
2901 def convert_between_tz_and_utc(self
, tz
, utc
):
2902 dston
= self
.dston
.replace(tzinfo
=tz
)
2903 # Because 1:MM on the day DST ends is taken as being standard time,
2904 # there is no spelling in tz for the last hour of daylight time.
2905 # For purposes of the test, the last hour of DST is 0:MM, which is
2906 # taken as being daylight time (and 1:MM is taken as being standard
2908 dstoff
= self
.dstoff
.replace(tzinfo
=tz
)
2909 for delta
in (timedelta(weeks
=13),
2912 timedelta(minutes
=1),
2913 timedelta(microseconds
=1)):
2915 self
.checkinside(dston
, tz
, utc
, dston
, dstoff
)
2916 for during
in dston
+ delta
, dstoff
- delta
:
2917 self
.checkinside(during
, tz
, utc
, dston
, dstoff
)
2919 self
.checkoutside(dstoff
, tz
, utc
)
2920 for outside
in dston
- delta
, dstoff
+ delta
:
2921 self
.checkoutside(outside
, tz
, utc
)
2923 def test_easy(self
):
2924 # Despite the name of this test, the endcases are excruciating.
2925 self
.convert_between_tz_and_utc(Eastern
, utc_real
)
2926 self
.convert_between_tz_and_utc(Pacific
, utc_real
)
2927 self
.convert_between_tz_and_utc(Eastern
, utc_fake
)
2928 self
.convert_between_tz_and_utc(Pacific
, utc_fake
)
2929 # The next is really dancing near the edge. It works because
2930 # Pacific and Eastern are far enough apart that their "problem
2931 # hours" don't overlap.
2932 self
.convert_between_tz_and_utc(Eastern
, Pacific
)
2933 self
.convert_between_tz_and_utc(Pacific
, Eastern
)
2934 # OTOH, these fail! Don't enable them. The difficulty is that
2935 # the edge case tests assume that every hour is representable in
2936 # the "utc" class. This is always true for a fixed-offset tzinfo
2937 # class (lke utc_real and utc_fake), but not for Eastern or Central.
2938 # For these adjacent DST-aware time zones, the range of time offsets
2939 # tested ends up creating hours in the one that aren't representable
2940 # in the other. For the same reason, we would see failures in the
2941 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
2942 # offset deltas in convert_between_tz_and_utc().
2944 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
2945 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
2947 def test_tricky(self
):
2948 # 22:00 on day before daylight starts.
2949 fourback
= self
.dston
- timedelta(hours
=4)
2950 ninewest
= FixedOffset(-9*60, "-0900", 0)
2951 fourback
= fourback
.replace(tzinfo
=ninewest
)
2952 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
2953 # 2", we should get the 3 spelling.
2954 # If we plug 22:00 the day before into Eastern, it "looks like std
2955 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
2956 # to 22:00 lands on 2:00, which makes no sense in local time (the
2957 # local clock jumps from 1 to 3). The point here is to make sure we
2958 # get the 3 spelling.
2959 expected
= self
.dston
.replace(hour
=3)
2960 got
= fourback
.astimezone(Eastern
).replace(tzinfo
=None)
2961 self
.assertEqual(expected
, got
)
2963 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
2964 # case we want the 1:00 spelling.
2965 sixutc
= self
.dston
.replace(hour
=6, tzinfo
=utc_real
)
2966 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
2967 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
2969 expected
= self
.dston
.replace(hour
=1)
2970 got
= sixutc
.astimezone(Eastern
).replace(tzinfo
=None)
2971 self
.assertEqual(expected
, got
)
2973 # Now on the day DST ends, we want "repeat an hour" behavior.
2974 # UTC 4:MM 5:MM 6:MM 7:MM checking these
2975 # EST 23:MM 0:MM 1:MM 2:MM
2976 # EDT 0:MM 1:MM 2:MM 3:MM
2977 # wall 0:MM 1:MM 1:MM 2:MM against these
2978 for utc
in utc_real
, utc_fake
:
2979 for tz
in Eastern
, Pacific
:
2980 first_std_hour
= self
.dstoff
- timedelta(hours
=2) # 23:MM
2981 # Convert that to UTC.
2982 first_std_hour
-= tz
.utcoffset(None)
2983 # Adjust for possibly fake UTC.
2984 asutc
= first_std_hour
+ utc
.utcoffset(None)
2985 # First UTC hour to convert; this is 4:00 when utc=utc_real &
2987 asutcbase
= asutc
.replace(tzinfo
=utc
)
2988 for tzhour
in (0, 1, 1, 2):
2989 expectedbase
= self
.dstoff
.replace(hour
=tzhour
)
2990 for minute
in 0, 30, 59:
2991 expected
= expectedbase
.replace(minute
=minute
)
2992 asutc
= asutcbase
.replace(minute
=minute
)
2993 astz
= asutc
.astimezone(tz
)
2994 self
.assertEqual(astz
.replace(tzinfo
=None), expected
)
2998 def test_bogus_dst(self
):
3000 def utcoffset(self
, dt
): return HOUR
3001 def dst(self
, dt
): return HOUR
3003 now
= self
.theclass
.now().replace(tzinfo
=utc_real
)
3005 now
.astimezone(ok())
3009 def dst(self
, dt
): return None
3010 self
.assertRaises(ValueError, now
.astimezone
, notok())
3012 def test_fromutc(self
):
3013 self
.assertRaises(TypeError, Eastern
.fromutc
) # not enough args
3014 now
= datetime
.utcnow().replace(tzinfo
=utc_real
)
3015 self
.assertRaises(ValueError, Eastern
.fromutc
, now
) # wrong tzinfo
3016 now
= now
.replace(tzinfo
=Eastern
) # insert correct tzinfo
3017 enow
= Eastern
.fromutc(now
) # doesn't blow up
3018 self
.assertEqual(enow
.tzinfo
, Eastern
) # has right tzinfo member
3019 self
.assertRaises(TypeError, Eastern
.fromutc
, now
, now
) # too many args
3020 self
.assertRaises(TypeError, Eastern
.fromutc
, date
.today()) # wrong type
3022 # Always converts UTC to standard time.
3023 class FauxUSTimeZone(USTimeZone
):
3024 def fromutc(self
, dt
):
3025 return dt
+ self
.stdoffset
3026 FEastern
= FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3028 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3029 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3030 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3032 # Check around DST start.
3033 start
= self
.dston
.replace(hour
=4, tzinfo
=Eastern
)
3034 fstart
= start
.replace(tzinfo
=FEastern
)
3035 for wall
in 23, 0, 1, 3, 4, 5:
3036 expected
= start
.replace(hour
=wall
)
3038 expected
-= timedelta(days
=1)
3039 got
= Eastern
.fromutc(start
)
3040 self
.assertEqual(expected
, got
)
3042 expected
= fstart
+ FEastern
.stdoffset
3043 got
= FEastern
.fromutc(fstart
)
3044 self
.assertEqual(expected
, got
)
3046 # Ensure astimezone() calls fromutc() too.
3047 got
= fstart
.replace(tzinfo
=utc_real
).astimezone(FEastern
)
3048 self
.assertEqual(expected
, got
)
3053 # Check around DST end.
3054 start
= self
.dstoff
.replace(hour
=4, tzinfo
=Eastern
)
3055 fstart
= start
.replace(tzinfo
=FEastern
)
3056 for wall
in 0, 1, 1, 2, 3, 4:
3057 expected
= start
.replace(hour
=wall
)
3058 got
= Eastern
.fromutc(start
)
3059 self
.assertEqual(expected
, got
)
3061 expected
= fstart
+ FEastern
.stdoffset
3062 got
= FEastern
.fromutc(fstart
)
3063 self
.assertEqual(expected
, got
)
3065 # Ensure astimezone() calls fromutc() too.
3066 got
= fstart
.replace(tzinfo
=utc_real
).astimezone(FEastern
)
3067 self
.assertEqual(expected
, got
)
3074 allsuites
= [unittest
.makeSuite(klass
, 'test')
3075 for klass
in (TestModule
,
3084 TestTimezoneConversions
,
3087 return unittest
.TestSuite(allsuites
)
3093 thesuite
= test_suite()
3096 test_support
.run_suite(thesuite
)
3097 if 1: # change to 0, under a debug build, for some leak detection
3101 raise SystemError("gc.garbage not empty after test run: %r" %
3103 if hasattr(sys
, 'gettotalrefcount'):
3104 thisrc
= sys
.gettotalrefcount()
3105 print >> sys
.stderr
, '*' * 10, 'total refs:', thisrc
,
3107 print >> sys
.stderr
, 'delta:', thisrc
- lastrc
3112 if __name__
== "__main__":