blob: 3c534d09f260533b463ae0d79314342bab843f23 [file] [log] [blame]
Ezio Melottif756f942013-04-13 20:12:38 +03001"""Concrete date/time and related types.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002
Ezio Melottif756f942013-04-13 20:12:38 +03003See http://www.iana.org/time-zones/repository/tz-link.html for
4time zone and DST data sources.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00005"""
6
7import time as _time
8import math as _math
9
10def _cmp(x, y):
11 return 0 if x == y else 1 if x > y else -1
12
13MINYEAR = 1
14MAXYEAR = 9999
15_MAXORDINAL = 3652059 # date.max.toordinal()
16
17# Utility functions, adapted from Python's Demo/classes/Dates.py, which
18# also assumes the current Gregorian calendar indefinitely extended in
19# both directions. Difference: Dates.py calls January 1 of year 0 day
20# number 1. The code here calls January 1 of year 1 day number 1. This is
21# to match the definition of the "proleptic Gregorian" calendar in Dershowitz
22# and Reingold's "Calendrical Calculations", where it's the base calendar
23# for all computations. See the book for algorithms for converting between
24# proleptic Gregorian ordinals and many other calendar systems.
25
Benjamin Petersonda0bea22013-08-29 17:29:30 -040026# -1 is a placeholder for indexing purposes.
Benjamin Petersonf908efb2013-08-29 17:27:57 -040027_DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
Alexander Belopolskycf86e362010-07-23 19:25:47 +000028
Benjamin Petersonda0bea22013-08-29 17:29:30 -040029_DAYS_BEFORE_MONTH = [-1] # -1 is a placeholder for indexing purposes.
Alexander Belopolskycf86e362010-07-23 19:25:47 +000030dbm = 0
31for dim in _DAYS_IN_MONTH[1:]:
32 _DAYS_BEFORE_MONTH.append(dbm)
33 dbm += dim
34del dbm, dim
35
36def _is_leap(year):
37 "year -> 1 if leap year, else 0."
38 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
39
40def _days_before_year(year):
41 "year -> number of days before January 1st of year."
42 y = year - 1
43 return y*365 + y//4 - y//100 + y//400
44
45def _days_in_month(year, month):
46 "year, month -> number of days in that month in that year."
47 assert 1 <= month <= 12, month
48 if month == 2 and _is_leap(year):
49 return 29
50 return _DAYS_IN_MONTH[month]
51
52def _days_before_month(year, month):
Ezio Melotti30b9d5d2013-08-17 15:50:46 +030053 "year, month -> number of days in year preceding first day of month."
Alexander Belopolskycf86e362010-07-23 19:25:47 +000054 assert 1 <= month <= 12, 'month must be in 1..12'
55 return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
56
57def _ymd2ord(year, month, day):
58 "year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
59 assert 1 <= month <= 12, 'month must be in 1..12'
60 dim = _days_in_month(year, month)
61 assert 1 <= day <= dim, ('day must be in 1..%d' % dim)
62 return (_days_before_year(year) +
63 _days_before_month(year, month) +
64 day)
65
66_DI400Y = _days_before_year(401) # number of days in 400 years
67_DI100Y = _days_before_year(101) # " " " " 100 "
68_DI4Y = _days_before_year(5) # " " " " 4 "
69
70# A 4-year cycle has an extra leap day over what we'd get from pasting
71# together 4 single years.
72assert _DI4Y == 4 * 365 + 1
73
74# Similarly, a 400-year cycle has an extra leap day over what we'd get from
75# pasting together 4 100-year cycles.
76assert _DI400Y == 4 * _DI100Y + 1
77
78# OTOH, a 100-year cycle has one fewer leap day than we'd get from
79# pasting together 25 4-year cycles.
80assert _DI100Y == 25 * _DI4Y - 1
81
82def _ord2ymd(n):
83 "ordinal -> (year, month, day), considering 01-Jan-0001 as day 1."
84
85 # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years
86 # repeats exactly every 400 years. The basic strategy is to find the
87 # closest 400-year boundary at or before n, then work with the offset
88 # from that boundary to n. Life is much clearer if we subtract 1 from
89 # n first -- then the values of n at 400-year boundaries are exactly
90 # those divisible by _DI400Y:
91 #
92 # D M Y n n-1
93 # -- --- ---- ---------- ----------------
94 # 31 Dec -400 -_DI400Y -_DI400Y -1
95 # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary
96 # ...
97 # 30 Dec 000 -1 -2
98 # 31 Dec 000 0 -1
99 # 1 Jan 001 1 0 400-year boundary
100 # 2 Jan 001 2 1
101 # 3 Jan 001 3 2
102 # ...
103 # 31 Dec 400 _DI400Y _DI400Y -1
104 # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary
105 n -= 1
106 n400, n = divmod(n, _DI400Y)
107 year = n400 * 400 + 1 # ..., -399, 1, 401, ...
108
109 # Now n is the (non-negative) offset, in days, from January 1 of year, to
110 # the desired date. Now compute how many 100-year cycles precede n.
111 # Note that it's possible for n100 to equal 4! In that case 4 full
112 # 100-year cycles precede the desired day, which implies the desired
113 # day is December 31 at the end of a 400-year cycle.
114 n100, n = divmod(n, _DI100Y)
115
116 # Now compute how many 4-year cycles precede it.
117 n4, n = divmod(n, _DI4Y)
118
119 # And now how many single years. Again n1 can be 4, and again meaning
120 # that the desired day is December 31 at the end of the 4-year cycle.
121 n1, n = divmod(n, 365)
122
123 year += n100 * 100 + n4 * 4 + n1
124 if n1 == 4 or n100 == 4:
125 assert n == 0
126 return year-1, 12, 31
127
128 # Now the year is correct, and n is the offset from January 1. We find
129 # the month via an estimate that's either exact or one too large.
130 leapyear = n1 == 3 and (n4 != 24 or n100 == 3)
131 assert leapyear == _is_leap(year)
132 month = (n + 50) >> 5
133 preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)
134 if preceding > n: # estimate is too large
135 month -= 1
136 preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)
137 n -= preceding
138 assert 0 <= n < _days_in_month(year, month)
139
140 # Now the year and month are correct, and n is the offset from the
141 # start of that month: we're done!
142 return year, month, n+1
143
144# Month and day names. For localized versions, see the calendar module.
145_MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
146 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
147_DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
148
149
150def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
151 wday = (_ymd2ord(y, m, d) + 6) % 7
152 dnum = _days_before_month(y, m) + d
153 return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
154
155def _format_time(hh, mm, ss, us):
156 # Skip trailing microseconds when us==0.
157 result = "%02d:%02d:%02d" % (hh, mm, ss)
158 if us:
159 result += ".%06d" % us
160 return result
161
162# Correctly substitute for %z and %Z escapes in strftime formats.
163def _wrap_strftime(object, format, timetuple):
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000164 # Don't call utcoffset() or tzname() unless actually needed.
165 freplace = None # the string to use for %f
166 zreplace = None # the string to use for %z
167 Zreplace = None # the string to use for %Z
168
169 # Scan format for %z and %Z escapes, replacing as needed.
170 newformat = []
171 push = newformat.append
172 i, n = 0, len(format)
173 while i < n:
174 ch = format[i]
175 i += 1
176 if ch == '%':
177 if i < n:
178 ch = format[i]
179 i += 1
180 if ch == 'f':
181 if freplace is None:
182 freplace = '%06d' % getattr(object,
183 'microsecond', 0)
184 newformat.append(freplace)
185 elif ch == 'z':
186 if zreplace is None:
187 zreplace = ""
188 if hasattr(object, "utcoffset"):
189 offset = object.utcoffset()
190 if offset is not None:
191 sign = '+'
192 if offset.days < 0:
193 offset = -offset
194 sign = '-'
195 h, m = divmod(offset, timedelta(hours=1))
196 assert not m % timedelta(minutes=1), "whole minute"
197 m //= timedelta(minutes=1)
198 zreplace = '%c%02d%02d' % (sign, h, m)
199 assert '%' not in zreplace
200 newformat.append(zreplace)
201 elif ch == 'Z':
202 if Zreplace is None:
203 Zreplace = ""
204 if hasattr(object, "tzname"):
205 s = object.tzname()
206 if s is not None:
207 # strftime is going to have at this: escape %
208 Zreplace = s.replace('%', '%%')
209 newformat.append(Zreplace)
210 else:
211 push('%')
212 push(ch)
213 else:
214 push('%')
215 else:
216 push(ch)
217 newformat = "".join(newformat)
218 return _time.strftime(newformat, timetuple)
219
220def _call_tzinfo_method(tzinfo, methname, tzinfoarg):
221 if tzinfo is None:
222 return None
223 return getattr(tzinfo, methname)(tzinfoarg)
224
225# Just raise TypeError if the arg isn't None or a string.
226def _check_tzname(name):
227 if name is not None and not isinstance(name, str):
228 raise TypeError("tzinfo.tzname() must return None or string, "
229 "not '%s'" % type(name))
230
231# name is the offset-producing method, "utcoffset" or "dst".
232# offset is what it returned.
233# If offset isn't None or timedelta, raises TypeError.
234# If offset is None, returns None.
235# Else offset is checked for being in range, and a whole # of minutes.
236# If it is, its integer value is returned. Else ValueError is raised.
237def _check_utc_offset(name, offset):
238 assert name in ("utcoffset", "dst")
239 if offset is None:
240 return
241 if not isinstance(offset, timedelta):
242 raise TypeError("tzinfo.%s() must return None "
243 "or timedelta, not '%s'" % (name, type(offset)))
244 if offset % timedelta(minutes=1) or offset.microseconds:
245 raise ValueError("tzinfo.%s() must return a whole number "
246 "of minutes, got %s" % (name, offset))
247 if not -timedelta(1) < offset < timedelta(1):
248 raise ValueError("%s()=%s, must be must be strictly between"
249 " -timedelta(hours=24) and timedelta(hours=24)"
250 % (name, offset))
251
252def _check_date_fields(year, month, day):
253 if not isinstance(year, int):
254 raise TypeError('int expected')
255 if not MINYEAR <= year <= MAXYEAR:
256 raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
257 if not 1 <= month <= 12:
258 raise ValueError('month must be in 1..12', month)
259 dim = _days_in_month(year, month)
260 if not 1 <= day <= dim:
261 raise ValueError('day must be in 1..%d' % dim, day)
262
263def _check_time_fields(hour, minute, second, microsecond):
264 if not isinstance(hour, int):
265 raise TypeError('int expected')
266 if not 0 <= hour <= 23:
267 raise ValueError('hour must be in 0..23', hour)
268 if not 0 <= minute <= 59:
269 raise ValueError('minute must be in 0..59', minute)
270 if not 0 <= second <= 59:
271 raise ValueError('second must be in 0..59', second)
272 if not 0 <= microsecond <= 999999:
273 raise ValueError('microsecond must be in 0..999999', microsecond)
274
275def _check_tzinfo_arg(tz):
276 if tz is not None and not isinstance(tz, tzinfo):
277 raise TypeError("tzinfo argument must be None or of a tzinfo subclass")
278
279def _cmperror(x, y):
280 raise TypeError("can't compare '%s' to '%s'" % (
281 type(x).__name__, type(y).__name__))
282
283class timedelta:
284 """Represent the difference between two datetime objects.
285
286 Supported operators:
287
288 - add, subtract timedelta
289 - unary plus, minus, abs
290 - compare to timedelta
Serhiy Storchaka95949422013-08-27 19:40:23 +0300291 - multiply, divide by int
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000292
293 In addition, datetime supports subtraction of two datetime objects
294 returning a timedelta, and addition or subtraction of a datetime
295 and a timedelta giving a datetime.
296
297 Representation: (days, seconds, microseconds). Why? Because I
298 felt like it.
299 """
300 __slots__ = '_days', '_seconds', '_microseconds'
301
302 def __new__(cls, days=0, seconds=0, microseconds=0,
303 milliseconds=0, minutes=0, hours=0, weeks=0):
304 # Doing this efficiently and accurately in C is going to be difficult
305 # and error-prone, due to ubiquitous overflow possibilities, and that
306 # C double doesn't have enough bits of precision to represent
307 # microseconds over 10K years faithfully. The code here tries to make
308 # explicit where go-fast assumptions can be relied on, in order to
309 # guide the C implementation; it's way more convoluted than speed-
310 # ignoring auto-overflow-to-long idiomatic Python could be.
311
312 # XXX Check that all inputs are ints or floats.
313
314 # Final values, all integer.
315 # s and us fit in 32-bit signed ints; d isn't bounded.
316 d = s = us = 0
317
318 # Normalize everything to days, seconds, microseconds.
319 days += weeks*7
320 seconds += minutes*60 + hours*3600
321 microseconds += milliseconds*1000
322
323 # Get rid of all fractions, and normalize s and us.
324 # Take a deep breath <wink>.
325 if isinstance(days, float):
326 dayfrac, days = _math.modf(days)
327 daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
328 assert daysecondswhole == int(daysecondswhole) # can't overflow
329 s = int(daysecondswhole)
330 assert days == int(days)
331 d = int(days)
332 else:
333 daysecondsfrac = 0.0
334 d = days
335 assert isinstance(daysecondsfrac, float)
336 assert abs(daysecondsfrac) <= 1.0
337 assert isinstance(d, int)
338 assert abs(s) <= 24 * 3600
339 # days isn't referenced again before redefinition
340
341 if isinstance(seconds, float):
342 secondsfrac, seconds = _math.modf(seconds)
343 assert seconds == int(seconds)
344 seconds = int(seconds)
345 secondsfrac += daysecondsfrac
346 assert abs(secondsfrac) <= 2.0
347 else:
348 secondsfrac = daysecondsfrac
349 # daysecondsfrac isn't referenced again
350 assert isinstance(secondsfrac, float)
351 assert abs(secondsfrac) <= 2.0
352
353 assert isinstance(seconds, int)
354 days, seconds = divmod(seconds, 24*3600)
355 d += days
356 s += int(seconds) # can't overflow
357 assert isinstance(s, int)
358 assert abs(s) <= 2 * 24 * 3600
359 # seconds isn't referenced again before redefinition
360
361 usdouble = secondsfrac * 1e6
362 assert abs(usdouble) < 2.1e6 # exact value not critical
363 # secondsfrac isn't referenced again
364
365 if isinstance(microseconds, float):
366 microseconds += usdouble
367 microseconds = round(microseconds, 0)
368 seconds, microseconds = divmod(microseconds, 1e6)
369 assert microseconds == int(microseconds)
370 assert seconds == int(seconds)
371 days, seconds = divmod(seconds, 24.*3600.)
372 assert days == int(days)
373 assert seconds == int(seconds)
374 d += int(days)
375 s += int(seconds) # can't overflow
376 assert isinstance(s, int)
377 assert abs(s) <= 3 * 24 * 3600
378 else:
379 seconds, microseconds = divmod(microseconds, 1000000)
380 days, seconds = divmod(seconds, 24*3600)
381 d += days
382 s += int(seconds) # can't overflow
383 assert isinstance(s, int)
384 assert abs(s) <= 3 * 24 * 3600
385 microseconds = float(microseconds)
386 microseconds += usdouble
387 microseconds = round(microseconds, 0)
388 assert abs(s) <= 3 * 24 * 3600
389 assert abs(microseconds) < 3.1e6
390
391 # Just a little bit of carrying possible for microseconds and seconds.
392 assert isinstance(microseconds, float)
393 assert int(microseconds) == microseconds
394 us = int(microseconds)
395 seconds, us = divmod(us, 1000000)
396 s += seconds # cant't overflow
397 assert isinstance(s, int)
398 days, s = divmod(s, 24*3600)
399 d += days
400
401 assert isinstance(d, int)
402 assert isinstance(s, int) and 0 <= s < 24*3600
403 assert isinstance(us, int) and 0 <= us < 1000000
404
405 self = object.__new__(cls)
406
407 self._days = d
408 self._seconds = s
409 self._microseconds = us
410 if abs(d) > 999999999:
411 raise OverflowError("timedelta # of days is too large: %d" % d)
412
413 return self
414
415 def __repr__(self):
416 if self._microseconds:
417 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
418 self._days,
419 self._seconds,
420 self._microseconds)
421 if self._seconds:
422 return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__,
423 self._days,
424 self._seconds)
425 return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days)
426
427 def __str__(self):
428 mm, ss = divmod(self._seconds, 60)
429 hh, mm = divmod(mm, 60)
430 s = "%d:%02d:%02d" % (hh, mm, ss)
431 if self._days:
432 def plural(n):
433 return n, abs(n) != 1 and "s" or ""
434 s = ("%d day%s, " % plural(self._days)) + s
435 if self._microseconds:
436 s = s + ".%06d" % self._microseconds
437 return s
438
439 def total_seconds(self):
440 """Total seconds in the duration."""
441 return ((self.days * 86400 + self.seconds)*10**6 +
442 self.microseconds) / 10**6
443
444 # Read-only field accessors
445 @property
446 def days(self):
447 """days"""
448 return self._days
449
450 @property
451 def seconds(self):
452 """seconds"""
453 return self._seconds
454
455 @property
456 def microseconds(self):
457 """microseconds"""
458 return self._microseconds
459
460 def __add__(self, other):
461 if isinstance(other, timedelta):
462 # for CPython compatibility, we cannot use
463 # our __class__ here, but need a real timedelta
464 return timedelta(self._days + other._days,
465 self._seconds + other._seconds,
466 self._microseconds + other._microseconds)
467 return NotImplemented
468
469 __radd__ = __add__
470
471 def __sub__(self, other):
472 if isinstance(other, timedelta):
Alexander Belopolskyb6f5ec72011-04-05 20:07:38 -0400473 # for CPython compatibility, we cannot use
474 # our __class__ here, but need a real timedelta
475 return timedelta(self._days - other._days,
476 self._seconds - other._seconds,
477 self._microseconds - other._microseconds)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000478 return NotImplemented
479
480 def __rsub__(self, other):
481 if isinstance(other, timedelta):
482 return -self + other
483 return NotImplemented
484
485 def __neg__(self):
486 # for CPython compatibility, we cannot use
487 # our __class__ here, but need a real timedelta
488 return timedelta(-self._days,
489 -self._seconds,
490 -self._microseconds)
491
492 def __pos__(self):
493 return self
494
495 def __abs__(self):
496 if self._days < 0:
497 return -self
498 else:
499 return self
500
501 def __mul__(self, other):
502 if isinstance(other, int):
503 # for CPython compatibility, we cannot use
504 # our __class__ here, but need a real timedelta
505 return timedelta(self._days * other,
506 self._seconds * other,
507 self._microseconds * other)
508 if isinstance(other, float):
509 a, b = other.as_integer_ratio()
510 return self * a / b
511 return NotImplemented
512
513 __rmul__ = __mul__
514
515 def _to_microseconds(self):
516 return ((self._days * (24*3600) + self._seconds) * 1000000 +
517 self._microseconds)
518
519 def __floordiv__(self, other):
520 if not isinstance(other, (int, timedelta)):
521 return NotImplemented
522 usec = self._to_microseconds()
523 if isinstance(other, timedelta):
524 return usec // other._to_microseconds()
525 if isinstance(other, int):
526 return timedelta(0, 0, usec // other)
527
528 def __truediv__(self, other):
529 if not isinstance(other, (int, float, timedelta)):
530 return NotImplemented
531 usec = self._to_microseconds()
532 if isinstance(other, timedelta):
533 return usec / other._to_microseconds()
534 if isinstance(other, int):
535 return timedelta(0, 0, usec / other)
536 if isinstance(other, float):
537 a, b = other.as_integer_ratio()
538 return timedelta(0, 0, b * usec / a)
539
540 def __mod__(self, other):
541 if isinstance(other, timedelta):
542 r = self._to_microseconds() % other._to_microseconds()
543 return timedelta(0, 0, r)
544 return NotImplemented
545
546 def __divmod__(self, other):
547 if isinstance(other, timedelta):
548 q, r = divmod(self._to_microseconds(),
549 other._to_microseconds())
550 return q, timedelta(0, 0, r)
551 return NotImplemented
552
553 # Comparisons of timedelta objects with other.
554
555 def __eq__(self, other):
556 if isinstance(other, timedelta):
557 return self._cmp(other) == 0
558 else:
559 return False
560
561 def __ne__(self, other):
562 if isinstance(other, timedelta):
563 return self._cmp(other) != 0
564 else:
565 return True
566
567 def __le__(self, other):
568 if isinstance(other, timedelta):
569 return self._cmp(other) <= 0
570 else:
571 _cmperror(self, other)
572
573 def __lt__(self, other):
574 if isinstance(other, timedelta):
575 return self._cmp(other) < 0
576 else:
577 _cmperror(self, other)
578
579 def __ge__(self, other):
580 if isinstance(other, timedelta):
581 return self._cmp(other) >= 0
582 else:
583 _cmperror(self, other)
584
585 def __gt__(self, other):
586 if isinstance(other, timedelta):
587 return self._cmp(other) > 0
588 else:
589 _cmperror(self, other)
590
591 def _cmp(self, other):
592 assert isinstance(other, timedelta)
593 return _cmp(self._getstate(), other._getstate())
594
595 def __hash__(self):
596 return hash(self._getstate())
597
598 def __bool__(self):
599 return (self._days != 0 or
600 self._seconds != 0 or
601 self._microseconds != 0)
602
603 # Pickle support.
604
605 def _getstate(self):
606 return (self._days, self._seconds, self._microseconds)
607
608 def __reduce__(self):
609 return (self.__class__, self._getstate())
610
611timedelta.min = timedelta(-999999999)
612timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
613 microseconds=999999)
614timedelta.resolution = timedelta(microseconds=1)
615
616class date:
617 """Concrete date type.
618
619 Constructors:
620
621 __new__()
622 fromtimestamp()
623 today()
624 fromordinal()
625
626 Operators:
627
628 __repr__, __str__
629 __cmp__, __hash__
630 __add__, __radd__, __sub__ (add/radd only with timedelta arg)
631
632 Methods:
633
634 timetuple()
635 toordinal()
636 weekday()
637 isoweekday(), isocalendar(), isoformat()
638 ctime()
639 strftime()
640
641 Properties (readonly):
642 year, month, day
643 """
644 __slots__ = '_year', '_month', '_day'
645
646 def __new__(cls, year, month=None, day=None):
647 """Constructor.
648
649 Arguments:
650
651 year, month, day (required, base 1)
652 """
653 if (isinstance(year, bytes) and len(year) == 4 and
654 1 <= year[2] <= 12 and month is None): # Month is sane
655 # Pickle support
656 self = object.__new__(cls)
657 self.__setstate(year)
658 return self
659 _check_date_fields(year, month, day)
660 self = object.__new__(cls)
661 self._year = year
662 self._month = month
663 self._day = day
664 return self
665
666 # Additional constructors
667
668 @classmethod
669 def fromtimestamp(cls, t):
670 "Construct a date from a POSIX timestamp (like time.time())."
671 y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
672 return cls(y, m, d)
673
674 @classmethod
675 def today(cls):
676 "Construct a date from time.time()."
677 t = _time.time()
678 return cls.fromtimestamp(t)
679
680 @classmethod
681 def fromordinal(cls, n):
682 """Contruct a date from a proleptic Gregorian ordinal.
683
684 January 1 of year 1 is day 1. Only the year, month and day are
685 non-zero in the result.
686 """
687 y, m, d = _ord2ymd(n)
688 return cls(y, m, d)
689
690 # Conversions to string
691
692 def __repr__(self):
693 """Convert to formal string, for repr().
694
695 >>> dt = datetime(2010, 1, 1)
696 >>> repr(dt)
697 'datetime.datetime(2010, 1, 1, 0, 0)'
698
699 >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
700 >>> repr(dt)
701 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'
702 """
703 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
704 self._year,
705 self._month,
706 self._day)
707 # XXX These shouldn't depend on time.localtime(), because that
708 # clips the usable dates to [1970 .. 2038). At least ctime() is
709 # easily done without using strftime() -- that's better too because
710 # strftime("%c", ...) is locale specific.
711
712
713 def ctime(self):
714 "Return ctime() style string."
715 weekday = self.toordinal() % 7 or 7
716 return "%s %s %2d 00:00:00 %04d" % (
717 _DAYNAMES[weekday],
718 _MONTHNAMES[self._month],
719 self._day, self._year)
720
721 def strftime(self, fmt):
722 "Format using strftime()."
723 return _wrap_strftime(self, fmt, self.timetuple())
724
725 def __format__(self, fmt):
726 if len(fmt) != 0:
727 return self.strftime(fmt)
728 return str(self)
729
730 def isoformat(self):
731 """Return the date formatted according to ISO.
732
733 This is 'YYYY-MM-DD'.
734
735 References:
736 - http://www.w3.org/TR/NOTE-datetime
737 - http://www.cl.cam.ac.uk/~mgk25/iso-time.html
738 """
739 return "%04d-%02d-%02d" % (self._year, self._month, self._day)
740
741 __str__ = isoformat
742
743 # Read-only field accessors
744 @property
745 def year(self):
746 """year (1-9999)"""
747 return self._year
748
749 @property
750 def month(self):
751 """month (1-12)"""
752 return self._month
753
754 @property
755 def day(self):
756 """day (1-31)"""
757 return self._day
758
759 # Standard conversions, __cmp__, __hash__ (and helpers)
760
761 def timetuple(self):
762 "Return local time tuple compatible with time.localtime()."
763 return _build_struct_time(self._year, self._month, self._day,
764 0, 0, 0, -1)
765
766 def toordinal(self):
767 """Return proleptic Gregorian ordinal for the year, month and day.
768
769 January 1 of year 1 is day 1. Only the year, month and day values
770 contribute to the result.
771 """
772 return _ymd2ord(self._year, self._month, self._day)
773
774 def replace(self, year=None, month=None, day=None):
775 """Return a new date with new values for the specified fields."""
776 if year is None:
777 year = self._year
778 if month is None:
779 month = self._month
780 if day is None:
781 day = self._day
782 _check_date_fields(year, month, day)
783 return date(year, month, day)
784
785 # Comparisons of date objects with other.
786
787 def __eq__(self, other):
788 if isinstance(other, date):
789 return self._cmp(other) == 0
790 return NotImplemented
791
792 def __ne__(self, other):
793 if isinstance(other, date):
794 return self._cmp(other) != 0
795 return NotImplemented
796
797 def __le__(self, other):
798 if isinstance(other, date):
799 return self._cmp(other) <= 0
800 return NotImplemented
801
802 def __lt__(self, other):
803 if isinstance(other, date):
804 return self._cmp(other) < 0
805 return NotImplemented
806
807 def __ge__(self, other):
808 if isinstance(other, date):
809 return self._cmp(other) >= 0
810 return NotImplemented
811
812 def __gt__(self, other):
813 if isinstance(other, date):
814 return self._cmp(other) > 0
815 return NotImplemented
816
817 def _cmp(self, other):
818 assert isinstance(other, date)
819 y, m, d = self._year, self._month, self._day
820 y2, m2, d2 = other._year, other._month, other._day
821 return _cmp((y, m, d), (y2, m2, d2))
822
823 def __hash__(self):
824 "Hash."
825 return hash(self._getstate())
826
827 # Computations
828
829 def __add__(self, other):
830 "Add a date to a timedelta."
831 if isinstance(other, timedelta):
832 o = self.toordinal() + other.days
833 if 0 < o <= _MAXORDINAL:
834 return date.fromordinal(o)
835 raise OverflowError("result out of range")
836 return NotImplemented
837
838 __radd__ = __add__
839
840 def __sub__(self, other):
841 """Subtract two dates, or a date and a timedelta."""
842 if isinstance(other, timedelta):
843 return self + timedelta(-other.days)
844 if isinstance(other, date):
845 days1 = self.toordinal()
846 days2 = other.toordinal()
847 return timedelta(days1 - days2)
848 return NotImplemented
849
850 def weekday(self):
851 "Return day of the week, where Monday == 0 ... Sunday == 6."
852 return (self.toordinal() + 6) % 7
853
854 # Day-of-the-week and week-of-the-year, according to ISO
855
856 def isoweekday(self):
857 "Return day of the week, where Monday == 1 ... Sunday == 7."
858 # 1-Jan-0001 is a Monday
859 return self.toordinal() % 7 or 7
860
861 def isocalendar(self):
862 """Return a 3-tuple containing ISO year, week number, and weekday.
863
864 The first ISO week of the year is the (Mon-Sun) week
865 containing the year's first Thursday; everything else derives
866 from that.
867
868 The first week is 1; Monday is 1 ... Sunday is 7.
869
870 ISO calendar algorithm taken from
871 http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
872 """
873 year = self._year
874 week1monday = _isoweek1monday(year)
875 today = _ymd2ord(self._year, self._month, self._day)
876 # Internally, week and day have origin 0
877 week, day = divmod(today - week1monday, 7)
878 if week < 0:
879 year -= 1
880 week1monday = _isoweek1monday(year)
881 week, day = divmod(today - week1monday, 7)
882 elif week >= 52:
883 if today >= _isoweek1monday(year+1):
884 year += 1
885 week = 0
886 return year, week+1, day+1
887
888 # Pickle support.
889
890 def _getstate(self):
891 yhi, ylo = divmod(self._year, 256)
892 return bytes([yhi, ylo, self._month, self._day]),
893
894 def __setstate(self, string):
895 if len(string) != 4 or not (1 <= string[2] <= 12):
896 raise TypeError("not enough arguments")
897 yhi, ylo, self._month, self._day = string
898 self._year = yhi * 256 + ylo
899
900 def __reduce__(self):
901 return (self.__class__, self._getstate())
902
903_date_class = date # so functions w/ args named "date" can get at the class
904
905date.min = date(1, 1, 1)
906date.max = date(9999, 12, 31)
907date.resolution = timedelta(days=1)
908
909class tzinfo:
910 """Abstract base class for time zone info classes.
911
912 Subclasses must override the name(), utcoffset() and dst() methods.
913 """
914 __slots__ = ()
915 def tzname(self, dt):
916 "datetime -> string name of time zone."
917 raise NotImplementedError("tzinfo subclass must override tzname()")
918
919 def utcoffset(self, dt):
920 "datetime -> minutes east of UTC (negative for west of UTC)"
921 raise NotImplementedError("tzinfo subclass must override utcoffset()")
922
923 def dst(self, dt):
924 """datetime -> DST offset in minutes east of UTC.
925
926 Return 0 if DST not in effect. utcoffset() must include the DST
927 offset.
928 """
929 raise NotImplementedError("tzinfo subclass must override dst()")
930
931 def fromutc(self, dt):
932 "datetime in UTC -> datetime in local time."
933
934 if not isinstance(dt, datetime):
935 raise TypeError("fromutc() requires a datetime argument")
936 if dt.tzinfo is not self:
937 raise ValueError("dt.tzinfo is not self")
938
939 dtoff = dt.utcoffset()
940 if dtoff is None:
941 raise ValueError("fromutc() requires a non-None utcoffset() "
942 "result")
943
944 # See the long comment block at the end of this file for an
945 # explanation of this algorithm.
946 dtdst = dt.dst()
947 if dtdst is None:
948 raise ValueError("fromutc() requires a non-None dst() result")
949 delta = dtoff - dtdst
950 if delta:
951 dt += delta
952 dtdst = dt.dst()
953 if dtdst is None:
954 raise ValueError("fromutc(): dt.dst gave inconsistent "
955 "results; cannot convert")
956 return dt + dtdst
957
958 # Pickle support.
959
960 def __reduce__(self):
961 getinitargs = getattr(self, "__getinitargs__", None)
962 if getinitargs:
963 args = getinitargs()
964 else:
965 args = ()
966 getstate = getattr(self, "__getstate__", None)
967 if getstate:
968 state = getstate()
969 else:
970 state = getattr(self, "__dict__", None) or None
971 if state is None:
972 return (self.__class__, args)
973 else:
974 return (self.__class__, args, state)
975
976_tzinfo_class = tzinfo
977
978class time:
979 """Time with time zone.
980
981 Constructors:
982
983 __new__()
984
985 Operators:
986
987 __repr__, __str__
988 __cmp__, __hash__
989
990 Methods:
991
992 strftime()
993 isoformat()
994 utcoffset()
995 tzname()
996 dst()
997
998 Properties (readonly):
999 hour, minute, second, microsecond, tzinfo
1000 """
1001
1002 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
1003 """Constructor.
1004
1005 Arguments:
1006
1007 hour, minute (required)
1008 second, microsecond (default to zero)
1009 tzinfo (default to None)
1010 """
1011 self = object.__new__(cls)
1012 if isinstance(hour, bytes) and len(hour) == 6:
1013 # Pickle support
1014 self.__setstate(hour, minute or None)
1015 return self
1016 _check_tzinfo_arg(tzinfo)
1017 _check_time_fields(hour, minute, second, microsecond)
1018 self._hour = hour
1019 self._minute = minute
1020 self._second = second
1021 self._microsecond = microsecond
1022 self._tzinfo = tzinfo
1023 return self
1024
1025 # Read-only field accessors
1026 @property
1027 def hour(self):
1028 """hour (0-23)"""
1029 return self._hour
1030
1031 @property
1032 def minute(self):
1033 """minute (0-59)"""
1034 return self._minute
1035
1036 @property
1037 def second(self):
1038 """second (0-59)"""
1039 return self._second
1040
1041 @property
1042 def microsecond(self):
1043 """microsecond (0-999999)"""
1044 return self._microsecond
1045
1046 @property
1047 def tzinfo(self):
1048 """timezone info object"""
1049 return self._tzinfo
1050
1051 # Standard conversions, __hash__ (and helpers)
1052
1053 # Comparisons of time objects with other.
1054
1055 def __eq__(self, other):
1056 if isinstance(other, time):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001057 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001058 else:
1059 return False
1060
1061 def __ne__(self, other):
1062 if isinstance(other, time):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001063 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001064 else:
1065 return True
1066
1067 def __le__(self, other):
1068 if isinstance(other, time):
1069 return self._cmp(other) <= 0
1070 else:
1071 _cmperror(self, other)
1072
1073 def __lt__(self, other):
1074 if isinstance(other, time):
1075 return self._cmp(other) < 0
1076 else:
1077 _cmperror(self, other)
1078
1079 def __ge__(self, other):
1080 if isinstance(other, time):
1081 return self._cmp(other) >= 0
1082 else:
1083 _cmperror(self, other)
1084
1085 def __gt__(self, other):
1086 if isinstance(other, time):
1087 return self._cmp(other) > 0
1088 else:
1089 _cmperror(self, other)
1090
Alexander Belopolsky08313822012-06-15 20:19:47 -04001091 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001092 assert isinstance(other, time)
1093 mytz = self._tzinfo
1094 ottz = other._tzinfo
1095 myoff = otoff = None
1096
1097 if mytz is ottz:
1098 base_compare = True
1099 else:
1100 myoff = self.utcoffset()
1101 otoff = other.utcoffset()
1102 base_compare = myoff == otoff
1103
1104 if base_compare:
1105 return _cmp((self._hour, self._minute, self._second,
1106 self._microsecond),
1107 (other._hour, other._minute, other._second,
1108 other._microsecond))
1109 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001110 if allow_mixed:
1111 return 2 # arbitrary non-zero value
1112 else:
1113 raise TypeError("cannot compare naive and aware times")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001114 myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
1115 othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
1116 return _cmp((myhhmm, self._second, self._microsecond),
1117 (othhmm, other._second, other._microsecond))
1118
1119 def __hash__(self):
1120 """Hash."""
1121 tzoff = self.utcoffset()
1122 if not tzoff: # zero or None
1123 return hash(self._getstate()[0])
1124 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
1125 timedelta(hours=1))
1126 assert not m % timedelta(minutes=1), "whole minute"
1127 m //= timedelta(minutes=1)
1128 if 0 <= h < 24:
1129 return hash(time(h, m, self.second, self.microsecond))
1130 return hash((h, m, self.second, self.microsecond))
1131
1132 # Conversion to string
1133
1134 def _tzstr(self, sep=":"):
1135 """Return formatted timezone offset (+xx:xx) or None."""
1136 off = self.utcoffset()
1137 if off is not None:
1138 if off.days < 0:
1139 sign = "-"
1140 off = -off
1141 else:
1142 sign = "+"
1143 hh, mm = divmod(off, timedelta(hours=1))
1144 assert not mm % timedelta(minutes=1), "whole minute"
1145 mm //= timedelta(minutes=1)
1146 assert 0 <= hh < 24
1147 off = "%s%02d%s%02d" % (sign, hh, sep, mm)
1148 return off
1149
1150 def __repr__(self):
1151 """Convert to formal string, for repr()."""
1152 if self._microsecond != 0:
1153 s = ", %d, %d" % (self._second, self._microsecond)
1154 elif self._second != 0:
1155 s = ", %d" % self._second
1156 else:
1157 s = ""
1158 s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__,
1159 self._hour, self._minute, s)
1160 if self._tzinfo is not None:
1161 assert s[-1:] == ")"
1162 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1163 return s
1164
1165 def isoformat(self):
1166 """Return the time formatted according to ISO.
1167
1168 This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if
1169 self.microsecond == 0.
1170 """
1171 s = _format_time(self._hour, self._minute, self._second,
1172 self._microsecond)
1173 tz = self._tzstr()
1174 if tz:
1175 s += tz
1176 return s
1177
1178 __str__ = isoformat
1179
1180 def strftime(self, fmt):
1181 """Format using strftime(). The date part of the timestamp passed
1182 to underlying strftime should not be used.
1183 """
Alexander Belopolskyb8bb4662011-01-08 00:13:34 +00001184 # The year must be >= 1000 else Python's strftime implementation
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001185 # can raise a bogus exception.
1186 timetuple = (1900, 1, 1,
1187 self._hour, self._minute, self._second,
1188 0, 1, -1)
1189 return _wrap_strftime(self, fmt, timetuple)
1190
1191 def __format__(self, fmt):
1192 if len(fmt) != 0:
1193 return self.strftime(fmt)
1194 return str(self)
1195
1196 # Timezone functions
1197
1198 def utcoffset(self):
1199 """Return the timezone offset in minutes east of UTC (negative west of
1200 UTC)."""
1201 if self._tzinfo is None:
1202 return None
1203 offset = self._tzinfo.utcoffset(None)
1204 _check_utc_offset("utcoffset", offset)
1205 return offset
1206
1207 def tzname(self):
1208 """Return the timezone name.
1209
1210 Note that the name is 100% informational -- there's no requirement that
1211 it mean anything in particular. For example, "GMT", "UTC", "-500",
1212 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1213 """
1214 if self._tzinfo is None:
1215 return None
1216 name = self._tzinfo.tzname(None)
1217 _check_tzname(name)
1218 return name
1219
1220 def dst(self):
1221 """Return 0 if DST is not in effect, or the DST offset (in minutes
1222 eastward) if DST is in effect.
1223
1224 This is purely informational; the DST offset has already been added to
1225 the UTC offset returned by utcoffset() if applicable, so there's no
1226 need to consult dst() unless you're interested in displaying the DST
1227 info.
1228 """
1229 if self._tzinfo is None:
1230 return None
1231 offset = self._tzinfo.dst(None)
1232 _check_utc_offset("dst", offset)
1233 return offset
1234
1235 def replace(self, hour=None, minute=None, second=None, microsecond=None,
1236 tzinfo=True):
1237 """Return a new time with new values for the specified fields."""
1238 if hour is None:
1239 hour = self.hour
1240 if minute is None:
1241 minute = self.minute
1242 if second is None:
1243 second = self.second
1244 if microsecond is None:
1245 microsecond = self.microsecond
1246 if tzinfo is True:
1247 tzinfo = self.tzinfo
1248 _check_time_fields(hour, minute, second, microsecond)
1249 _check_tzinfo_arg(tzinfo)
1250 return time(hour, minute, second, microsecond, tzinfo)
1251
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001252 # Pickle support.
1253
1254 def _getstate(self):
1255 us2, us3 = divmod(self._microsecond, 256)
1256 us1, us2 = divmod(us2, 256)
1257 basestate = bytes([self._hour, self._minute, self._second,
1258 us1, us2, us3])
1259 if self._tzinfo is None:
1260 return (basestate,)
1261 else:
1262 return (basestate, self._tzinfo)
1263
1264 def __setstate(self, string, tzinfo):
1265 if len(string) != 6 or string[0] >= 24:
1266 raise TypeError("an integer is required")
1267 (self._hour, self._minute, self._second,
1268 us1, us2, us3) = string
1269 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1270 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1271 self._tzinfo = tzinfo
1272 else:
1273 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1274
1275 def __reduce__(self):
1276 return (time, self._getstate())
1277
1278_time_class = time # so functions w/ args named "time" can get at the class
1279
1280time.min = time(0, 0, 0)
1281time.max = time(23, 59, 59, 999999)
1282time.resolution = timedelta(microseconds=1)
1283
1284class datetime(date):
1285 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
1286
1287 The year, month and day arguments are required. tzinfo may be None, or an
Serhiy Storchaka95949422013-08-27 19:40:23 +03001288 instance of a tzinfo subclass. The remaining arguments may be ints.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001289 """
1290
1291 __slots__ = date.__slots__ + (
1292 '_hour', '_minute', '_second',
1293 '_microsecond', '_tzinfo')
1294 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1295 microsecond=0, tzinfo=None):
1296 if isinstance(year, bytes) and len(year) == 10:
1297 # Pickle support
1298 self = date.__new__(cls, year[:4])
1299 self.__setstate(year, month)
1300 return self
1301 _check_tzinfo_arg(tzinfo)
1302 _check_time_fields(hour, minute, second, microsecond)
1303 self = date.__new__(cls, year, month, day)
1304 self._hour = hour
1305 self._minute = minute
1306 self._second = second
1307 self._microsecond = microsecond
1308 self._tzinfo = tzinfo
1309 return self
1310
1311 # Read-only field accessors
1312 @property
1313 def hour(self):
1314 """hour (0-23)"""
1315 return self._hour
1316
1317 @property
1318 def minute(self):
1319 """minute (0-59)"""
1320 return self._minute
1321
1322 @property
1323 def second(self):
1324 """second (0-59)"""
1325 return self._second
1326
1327 @property
1328 def microsecond(self):
1329 """microsecond (0-999999)"""
1330 return self._microsecond
1331
1332 @property
1333 def tzinfo(self):
1334 """timezone info object"""
1335 return self._tzinfo
1336
1337 @classmethod
1338 def fromtimestamp(cls, t, tz=None):
1339 """Construct a datetime from a POSIX timestamp (like time.time()).
1340
1341 A timezone info object may be passed in as well.
1342 """
1343
1344 _check_tzinfo_arg(tz)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001345
1346 converter = _time.localtime if tz is None else _time.gmtime
1347
1348 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001349 us = int(frac * 1e6)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001350
1351 # If timestamp is less than one microsecond smaller than a
1352 # full second, us can be rounded up to 1000000. In this case,
1353 # roll over to seconds, otherwise, ValueError is raised
1354 # by the constructor.
1355 if us == 1000000:
1356 t += 1
1357 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001358 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001359 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1360 result = cls(y, m, d, hh, mm, ss, us, tz)
1361 if tz is not None:
1362 result = tz.fromutc(result)
1363 return result
1364
1365 @classmethod
1366 def utcfromtimestamp(cls, t):
1367 "Construct a UTC datetime from a POSIX timestamp (like time.time())."
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001368 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001369 us = int(frac * 1e6)
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001370
1371 # If timestamp is less than one microsecond smaller than a
1372 # full second, us can be rounded up to 1000000. In this case,
1373 # roll over to seconds, otherwise, ValueError is raised
1374 # by the constructor.
1375 if us == 1000000:
1376 t += 1
1377 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001378 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001379 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1380 return cls(y, m, d, hh, mm, ss, us)
1381
1382 # XXX This is supposed to do better than we *can* do by using time.time(),
1383 # XXX if the platform supports a more accurate way. The C implementation
1384 # XXX uses gettimeofday on platforms that have it, but that isn't
1385 # XXX available from Python. So now() may return different results
1386 # XXX across the implementations.
1387 @classmethod
1388 def now(cls, tz=None):
1389 "Construct a datetime from time.time() and optional time zone info."
1390 t = _time.time()
1391 return cls.fromtimestamp(t, tz)
1392
1393 @classmethod
1394 def utcnow(cls):
1395 "Construct a UTC datetime from time.time()."
1396 t = _time.time()
1397 return cls.utcfromtimestamp(t)
1398
1399 @classmethod
1400 def combine(cls, date, time):
1401 "Construct a datetime from a given date and a given time."
1402 if not isinstance(date, _date_class):
1403 raise TypeError("date argument must be a date instance")
1404 if not isinstance(time, _time_class):
1405 raise TypeError("time argument must be a time instance")
1406 return cls(date.year, date.month, date.day,
1407 time.hour, time.minute, time.second, time.microsecond,
1408 time.tzinfo)
1409
1410 def timetuple(self):
1411 "Return local time tuple compatible with time.localtime()."
1412 dst = self.dst()
1413 if dst is None:
1414 dst = -1
1415 elif dst:
1416 dst = 1
1417 else:
1418 dst = 0
1419 return _build_struct_time(self.year, self.month, self.day,
1420 self.hour, self.minute, self.second,
1421 dst)
1422
Alexander Belopolskya4415142012-06-08 12:33:09 -04001423 def timestamp(self):
1424 "Return POSIX timestamp as float"
1425 if self._tzinfo is None:
1426 return _time.mktime((self.year, self.month, self.day,
1427 self.hour, self.minute, self.second,
1428 -1, -1, -1)) + self.microsecond / 1e6
1429 else:
1430 return (self - _EPOCH).total_seconds()
1431
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001432 def utctimetuple(self):
1433 "Return UTC time tuple compatible with time.gmtime()."
1434 offset = self.utcoffset()
1435 if offset:
1436 self -= offset
1437 y, m, d = self.year, self.month, self.day
1438 hh, mm, ss = self.hour, self.minute, self.second
1439 return _build_struct_time(y, m, d, hh, mm, ss, 0)
1440
1441 def date(self):
1442 "Return the date part."
1443 return date(self._year, self._month, self._day)
1444
1445 def time(self):
1446 "Return the time part, with tzinfo None."
1447 return time(self.hour, self.minute, self.second, self.microsecond)
1448
1449 def timetz(self):
1450 "Return the time part, with same tzinfo."
1451 return time(self.hour, self.minute, self.second, self.microsecond,
1452 self._tzinfo)
1453
1454 def replace(self, year=None, month=None, day=None, hour=None,
1455 minute=None, second=None, microsecond=None, tzinfo=True):
1456 """Return a new datetime with new values for the specified fields."""
1457 if year is None:
1458 year = self.year
1459 if month is None:
1460 month = self.month
1461 if day is None:
1462 day = self.day
1463 if hour is None:
1464 hour = self.hour
1465 if minute is None:
1466 minute = self.minute
1467 if second is None:
1468 second = self.second
1469 if microsecond is None:
1470 microsecond = self.microsecond
1471 if tzinfo is True:
1472 tzinfo = self.tzinfo
1473 _check_date_fields(year, month, day)
1474 _check_time_fields(hour, minute, second, microsecond)
1475 _check_tzinfo_arg(tzinfo)
1476 return datetime(year, month, day, hour, minute, second,
1477 microsecond, tzinfo)
1478
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001479 def astimezone(self, tz=None):
1480 if tz is None:
1481 if self.tzinfo is None:
1482 raise ValueError("astimezone() requires an aware datetime")
1483 ts = (self - _EPOCH) // timedelta(seconds=1)
1484 localtm = _time.localtime(ts)
1485 local = datetime(*localtm[:6])
1486 try:
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001487 # Extract TZ data if available
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001488 gmtoff = localtm.tm_gmtoff
1489 zone = localtm.tm_zone
1490 except AttributeError:
1491 # Compute UTC offset and compare with the value implied
1492 # by tm_isdst. If the values match, use the zone name
1493 # implied by tm_isdst.
1494 delta = local - datetime(*_time.gmtime(ts)[:6])
1495 dst = _time.daylight and localtm.tm_isdst > 0
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001496 gmtoff = -(_time.altzone if dst else _time.timezone)
1497 if delta == timedelta(seconds=gmtoff):
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001498 tz = timezone(delta, _time.tzname[dst])
1499 else:
1500 tz = timezone(delta)
1501 else:
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001502 tz = timezone(timedelta(seconds=gmtoff), zone)
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001503
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001504 elif not isinstance(tz, tzinfo):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001505 raise TypeError("tz argument must be an instance of tzinfo")
1506
1507 mytz = self.tzinfo
1508 if mytz is None:
1509 raise ValueError("astimezone() requires an aware datetime")
1510
1511 if tz is mytz:
1512 return self
1513
1514 # Convert self to UTC, and attach the new time zone object.
1515 myoffset = self.utcoffset()
1516 if myoffset is None:
1517 raise ValueError("astimezone() requires an aware datetime")
1518 utc = (self - myoffset).replace(tzinfo=tz)
1519
1520 # Convert from UTC to tz's local time.
1521 return tz.fromutc(utc)
1522
1523 # Ways to produce a string.
1524
1525 def ctime(self):
1526 "Return ctime() style string."
1527 weekday = self.toordinal() % 7 or 7
1528 return "%s %s %2d %02d:%02d:%02d %04d" % (
1529 _DAYNAMES[weekday],
1530 _MONTHNAMES[self._month],
1531 self._day,
1532 self._hour, self._minute, self._second,
1533 self._year)
1534
1535 def isoformat(self, sep='T'):
1536 """Return the time formatted according to ISO.
1537
1538 This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if
1539 self.microsecond == 0.
1540
1541 If self.tzinfo is not None, the UTC offset is also attached, giving
1542 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.
1543
1544 Optional argument sep specifies the separator between date and
1545 time, default 'T'.
1546 """
1547 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day,
1548 sep) +
1549 _format_time(self._hour, self._minute, self._second,
1550 self._microsecond))
1551 off = self.utcoffset()
1552 if off is not None:
1553 if off.days < 0:
1554 sign = "-"
1555 off = -off
1556 else:
1557 sign = "+"
1558 hh, mm = divmod(off, timedelta(hours=1))
1559 assert not mm % timedelta(minutes=1), "whole minute"
1560 mm //= timedelta(minutes=1)
1561 s += "%s%02d:%02d" % (sign, hh, mm)
1562 return s
1563
1564 def __repr__(self):
1565 """Convert to formal string, for repr()."""
1566 L = [self._year, self._month, self._day, # These are never zero
1567 self._hour, self._minute, self._second, self._microsecond]
1568 if L[-1] == 0:
1569 del L[-1]
1570 if L[-1] == 0:
1571 del L[-1]
1572 s = ", ".join(map(str, L))
1573 s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
1574 if self._tzinfo is not None:
1575 assert s[-1:] == ")"
1576 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1577 return s
1578
1579 def __str__(self):
1580 "Convert to string, for str()."
1581 return self.isoformat(sep=' ')
1582
1583 @classmethod
1584 def strptime(cls, date_string, format):
1585 'string, format -> new datetime parsed from a string (like time.strptime()).'
1586 import _strptime
1587 return _strptime._strptime_datetime(cls, date_string, format)
1588
1589 def utcoffset(self):
1590 """Return the timezone offset in minutes east of UTC (negative west of
1591 UTC)."""
1592 if self._tzinfo is None:
1593 return None
1594 offset = self._tzinfo.utcoffset(self)
1595 _check_utc_offset("utcoffset", offset)
1596 return offset
1597
1598 def tzname(self):
1599 """Return the timezone name.
1600
1601 Note that the name is 100% informational -- there's no requirement that
1602 it mean anything in particular. For example, "GMT", "UTC", "-500",
1603 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1604 """
1605 name = _call_tzinfo_method(self._tzinfo, "tzname", self)
1606 _check_tzname(name)
1607 return name
1608
1609 def dst(self):
1610 """Return 0 if DST is not in effect, or the DST offset (in minutes
1611 eastward) if DST is in effect.
1612
1613 This is purely informational; the DST offset has already been added to
1614 the UTC offset returned by utcoffset() if applicable, so there's no
1615 need to consult dst() unless you're interested in displaying the DST
1616 info.
1617 """
1618 if self._tzinfo is None:
1619 return None
1620 offset = self._tzinfo.dst(self)
1621 _check_utc_offset("dst", offset)
1622 return offset
1623
1624 # Comparisons of datetime objects with other.
1625
1626 def __eq__(self, other):
1627 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001628 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001629 elif not isinstance(other, date):
1630 return NotImplemented
1631 else:
1632 return False
1633
1634 def __ne__(self, other):
1635 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001636 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001637 elif not isinstance(other, date):
1638 return NotImplemented
1639 else:
1640 return True
1641
1642 def __le__(self, other):
1643 if isinstance(other, datetime):
1644 return self._cmp(other) <= 0
1645 elif not isinstance(other, date):
1646 return NotImplemented
1647 else:
1648 _cmperror(self, other)
1649
1650 def __lt__(self, other):
1651 if isinstance(other, datetime):
1652 return self._cmp(other) < 0
1653 elif not isinstance(other, date):
1654 return NotImplemented
1655 else:
1656 _cmperror(self, other)
1657
1658 def __ge__(self, other):
1659 if isinstance(other, datetime):
1660 return self._cmp(other) >= 0
1661 elif not isinstance(other, date):
1662 return NotImplemented
1663 else:
1664 _cmperror(self, other)
1665
1666 def __gt__(self, other):
1667 if isinstance(other, datetime):
1668 return self._cmp(other) > 0
1669 elif not isinstance(other, date):
1670 return NotImplemented
1671 else:
1672 _cmperror(self, other)
1673
Alexander Belopolsky08313822012-06-15 20:19:47 -04001674 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001675 assert isinstance(other, datetime)
1676 mytz = self._tzinfo
1677 ottz = other._tzinfo
1678 myoff = otoff = None
1679
1680 if mytz is ottz:
1681 base_compare = True
1682 else:
Alexander Belopolsky016ef552012-06-15 18:15:25 -04001683 myoff = self.utcoffset()
1684 otoff = other.utcoffset()
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001685 base_compare = myoff == otoff
1686
1687 if base_compare:
1688 return _cmp((self._year, self._month, self._day,
1689 self._hour, self._minute, self._second,
1690 self._microsecond),
1691 (other._year, other._month, other._day,
1692 other._hour, other._minute, other._second,
1693 other._microsecond))
1694 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001695 if allow_mixed:
1696 return 2 # arbitrary non-zero value
1697 else:
1698 raise TypeError("cannot compare naive and aware datetimes")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001699 # XXX What follows could be done more efficiently...
1700 diff = self - other # this will take offsets into account
1701 if diff.days < 0:
1702 return -1
1703 return diff and 1 or 0
1704
1705 def __add__(self, other):
1706 "Add a datetime and a timedelta."
1707 if not isinstance(other, timedelta):
1708 return NotImplemented
1709 delta = timedelta(self.toordinal(),
1710 hours=self._hour,
1711 minutes=self._minute,
1712 seconds=self._second,
1713 microseconds=self._microsecond)
1714 delta += other
1715 hour, rem = divmod(delta.seconds, 3600)
1716 minute, second = divmod(rem, 60)
1717 if 0 < delta.days <= _MAXORDINAL:
1718 return datetime.combine(date.fromordinal(delta.days),
1719 time(hour, minute, second,
1720 delta.microseconds,
1721 tzinfo=self._tzinfo))
1722 raise OverflowError("result out of range")
1723
1724 __radd__ = __add__
1725
1726 def __sub__(self, other):
1727 "Subtract two datetimes, or a datetime and a timedelta."
1728 if not isinstance(other, datetime):
1729 if isinstance(other, timedelta):
1730 return self + -other
1731 return NotImplemented
1732
1733 days1 = self.toordinal()
1734 days2 = other.toordinal()
1735 secs1 = self._second + self._minute * 60 + self._hour * 3600
1736 secs2 = other._second + other._minute * 60 + other._hour * 3600
1737 base = timedelta(days1 - days2,
1738 secs1 - secs2,
1739 self._microsecond - other._microsecond)
1740 if self._tzinfo is other._tzinfo:
1741 return base
1742 myoff = self.utcoffset()
1743 otoff = other.utcoffset()
1744 if myoff == otoff:
1745 return base
1746 if myoff is None or otoff is None:
1747 raise TypeError("cannot mix naive and timezone-aware time")
1748 return base + otoff - myoff
1749
1750 def __hash__(self):
1751 tzoff = self.utcoffset()
1752 if tzoff is None:
1753 return hash(self._getstate()[0])
1754 days = _ymd2ord(self.year, self.month, self.day)
1755 seconds = self.hour * 3600 + self.minute * 60 + self.second
1756 return hash(timedelta(days, seconds, self.microsecond) - tzoff)
1757
1758 # Pickle support.
1759
1760 def _getstate(self):
1761 yhi, ylo = divmod(self._year, 256)
1762 us2, us3 = divmod(self._microsecond, 256)
1763 us1, us2 = divmod(us2, 256)
1764 basestate = bytes([yhi, ylo, self._month, self._day,
1765 self._hour, self._minute, self._second,
1766 us1, us2, us3])
1767 if self._tzinfo is None:
1768 return (basestate,)
1769 else:
1770 return (basestate, self._tzinfo)
1771
1772 def __setstate(self, string, tzinfo):
1773 (yhi, ylo, self._month, self._day, self._hour,
1774 self._minute, self._second, us1, us2, us3) = string
1775 self._year = yhi * 256 + ylo
1776 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1777 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1778 self._tzinfo = tzinfo
1779 else:
1780 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1781
1782 def __reduce__(self):
1783 return (self.__class__, self._getstate())
1784
1785
1786datetime.min = datetime(1, 1, 1)
1787datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
1788datetime.resolution = timedelta(microseconds=1)
1789
1790
1791def _isoweek1monday(year):
1792 # Helper to calculate the day number of the Monday starting week 1
1793 # XXX This could be done more efficiently
1794 THURSDAY = 3
1795 firstday = _ymd2ord(year, 1, 1)
1796 firstweekday = (firstday + 6) % 7 # See weekday() above
1797 week1monday = firstday - firstweekday
1798 if firstweekday > THURSDAY:
1799 week1monday += 7
1800 return week1monday
1801
1802class timezone(tzinfo):
1803 __slots__ = '_offset', '_name'
1804
1805 # Sentinel value to disallow None
1806 _Omitted = object()
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001807 def __new__(cls, offset, name=_Omitted):
1808 if not isinstance(offset, timedelta):
1809 raise TypeError("offset must be a timedelta")
1810 if name is cls._Omitted:
1811 if not offset:
1812 return cls.utc
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001813 name = None
1814 elif not isinstance(name, str):
1815 raise TypeError("name must be a string")
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001816 if not cls._minoffset <= offset <= cls._maxoffset:
1817 raise ValueError("offset must be a timedelta"
1818 " strictly between -timedelta(hours=24) and"
1819 " timedelta(hours=24).")
1820 if (offset.microseconds != 0 or
1821 offset.seconds % 60 != 0):
1822 raise ValueError("offset must be a timedelta"
1823 " representing a whole number of minutes")
1824 return cls._create(offset, name)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001825
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001826 @classmethod
1827 def _create(cls, offset, name=None):
1828 self = tzinfo.__new__(cls)
1829 self._offset = offset
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001830 self._name = name
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001831 return self
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001832
1833 def __getinitargs__(self):
1834 """pickle support"""
1835 if self._name is None:
1836 return (self._offset,)
1837 return (self._offset, self._name)
1838
1839 def __eq__(self, other):
Georg Brandl0085a242012-09-22 09:23:12 +02001840 if type(other) != timezone:
1841 return False
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001842 return self._offset == other._offset
1843
1844 def __hash__(self):
1845 return hash(self._offset)
1846
1847 def __repr__(self):
1848 """Convert to formal string, for repr().
1849
1850 >>> tz = timezone.utc
1851 >>> repr(tz)
1852 'datetime.timezone.utc'
1853 >>> tz = timezone(timedelta(hours=-5), 'EST')
1854 >>> repr(tz)
1855 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
1856 """
1857 if self is self.utc:
1858 return 'datetime.timezone.utc'
1859 if self._name is None:
1860 return "%s(%r)" % ('datetime.' + self.__class__.__name__,
1861 self._offset)
1862 return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__,
1863 self._offset, self._name)
1864
1865 def __str__(self):
1866 return self.tzname(None)
1867
1868 def utcoffset(self, dt):
1869 if isinstance(dt, datetime) or dt is None:
1870 return self._offset
1871 raise TypeError("utcoffset() argument must be a datetime instance"
1872 " or None")
1873
1874 def tzname(self, dt):
1875 if isinstance(dt, datetime) or dt is None:
1876 if self._name is None:
1877 return self._name_from_offset(self._offset)
1878 return self._name
1879 raise TypeError("tzname() argument must be a datetime instance"
1880 " or None")
1881
1882 def dst(self, dt):
1883 if isinstance(dt, datetime) or dt is None:
1884 return None
1885 raise TypeError("dst() argument must be a datetime instance"
1886 " or None")
1887
1888 def fromutc(self, dt):
1889 if isinstance(dt, datetime):
1890 if dt.tzinfo is not self:
1891 raise ValueError("fromutc: dt.tzinfo "
1892 "is not self")
1893 return dt + self._offset
1894 raise TypeError("fromutc() argument must be a datetime instance"
1895 " or None")
1896
1897 _maxoffset = timedelta(hours=23, minutes=59)
1898 _minoffset = -_maxoffset
1899
1900 @staticmethod
1901 def _name_from_offset(delta):
1902 if delta < timedelta(0):
1903 sign = '-'
1904 delta = -delta
1905 else:
1906 sign = '+'
1907 hours, rest = divmod(delta, timedelta(hours=1))
1908 minutes = rest // timedelta(minutes=1)
1909 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
1910
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001911timezone.utc = timezone._create(timedelta(0))
1912timezone.min = timezone._create(timezone._minoffset)
1913timezone.max = timezone._create(timezone._maxoffset)
Alexander Belopolskya4415142012-06-08 12:33:09 -04001914_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001915
Victor Stinner765531d2013-03-26 01:11:54 +01001916# Some time zone algebra. For a datetime x, let
1917# x.n = x stripped of its timezone -- its naive time.
1918# x.o = x.utcoffset(), and assuming that doesn't raise an exception or
1919# return None
1920# x.d = x.dst(), and assuming that doesn't raise an exception or
1921# return None
1922# x.s = x's standard offset, x.o - x.d
1923#
1924# Now some derived rules, where k is a duration (timedelta).
1925#
1926# 1. x.o = x.s + x.d
1927# This follows from the definition of x.s.
1928#
1929# 2. If x and y have the same tzinfo member, x.s = y.s.
1930# This is actually a requirement, an assumption we need to make about
1931# sane tzinfo classes.
1932#
1933# 3. The naive UTC time corresponding to x is x.n - x.o.
1934# This is again a requirement for a sane tzinfo class.
1935#
1936# 4. (x+k).s = x.s
1937# This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
1938#
1939# 5. (x+k).n = x.n + k
1940# Again follows from how arithmetic is defined.
1941#
1942# Now we can explain tz.fromutc(x). Let's assume it's an interesting case
1943# (meaning that the various tzinfo methods exist, and don't blow up or return
1944# None when called).
1945#
1946# The function wants to return a datetime y with timezone tz, equivalent to x.
1947# x is already in UTC.
1948#
1949# By #3, we want
1950#
1951# y.n - y.o = x.n [1]
1952#
1953# The algorithm starts by attaching tz to x.n, and calling that y. So
1954# x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
1955# becomes true; in effect, we want to solve [2] for k:
1956#
1957# (y+k).n - (y+k).o = x.n [2]
1958#
1959# By #1, this is the same as
1960#
1961# (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
1962#
1963# By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
1964# Substituting that into [3],
1965#
1966# x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
1967# k - (y+k).s - (y+k).d = 0; rearranging,
1968# k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
1969# k = y.s - (y+k).d
1970#
1971# On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
1972# approximate k by ignoring the (y+k).d term at first. Note that k can't be
1973# very large, since all offset-returning methods return a duration of magnitude
1974# less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
1975# be 0, so ignoring it has no consequence then.
1976#
1977# In any case, the new value is
1978#
1979# z = y + y.s [4]
1980#
1981# It's helpful to step back at look at [4] from a higher level: it's simply
1982# mapping from UTC to tz's standard time.
1983#
1984# At this point, if
1985#
1986# z.n - z.o = x.n [5]
1987#
1988# we have an equivalent time, and are almost done. The insecurity here is
1989# at the start of daylight time. Picture US Eastern for concreteness. The wall
1990# time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
1991# sense then. The docs ask that an Eastern tzinfo class consider such a time to
1992# be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
1993# on the day DST starts. We want to return the 1:MM EST spelling because that's
1994# the only spelling that makes sense on the local wall clock.
1995#
1996# In fact, if [5] holds at this point, we do have the standard-time spelling,
1997# but that takes a bit of proof. We first prove a stronger result. What's the
1998# difference between the LHS and RHS of [5]? Let
1999#
2000# diff = x.n - (z.n - z.o) [6]
2001#
2002# Now
2003# z.n = by [4]
2004# (y + y.s).n = by #5
2005# y.n + y.s = since y.n = x.n
2006# x.n + y.s = since z and y are have the same tzinfo member,
2007# y.s = z.s by #2
2008# x.n + z.s
2009#
2010# Plugging that back into [6] gives
2011#
2012# diff =
2013# x.n - ((x.n + z.s) - z.o) = expanding
2014# x.n - x.n - z.s + z.o = cancelling
2015# - z.s + z.o = by #2
2016# z.d
2017#
2018# So diff = z.d.
2019#
2020# If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
2021# spelling we wanted in the endcase described above. We're done. Contrarily,
2022# if z.d = 0, then we have a UTC equivalent, and are also done.
2023#
2024# If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
2025# add to z (in effect, z is in tz's standard time, and we need to shift the
2026# local clock into tz's daylight time).
2027#
2028# Let
2029#
2030# z' = z + z.d = z + diff [7]
2031#
2032# and we can again ask whether
2033#
2034# z'.n - z'.o = x.n [8]
2035#
2036# If so, we're done. If not, the tzinfo class is insane, according to the
2037# assumptions we've made. This also requires a bit of proof. As before, let's
2038# compute the difference between the LHS and RHS of [8] (and skipping some of
2039# the justifications for the kinds of substitutions we've done several times
2040# already):
2041#
2042# diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
2043# x.n - (z.n + diff - z'.o) = replacing diff via [6]
2044# x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
2045# x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
2046# - z.n + z.n - z.o + z'.o = cancel z.n
2047# - z.o + z'.o = #1 twice
2048# -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
2049# z'.d - z.d
2050#
2051# So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
2052# we've found the UTC-equivalent so are done. In fact, we stop with [7] and
2053# return z', not bothering to compute z'.d.
2054#
2055# How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
2056# a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
2057# would have to change the result dst() returns: we start in DST, and moving
2058# a little further into it takes us out of DST.
2059#
2060# There isn't a sane case where this can happen. The closest it gets is at
2061# the end of DST, where there's an hour in UTC with no spelling in a hybrid
2062# tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
2063# that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
2064# UTC) because the docs insist on that, but 0:MM is taken as being in daylight
2065# time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
2066# clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
2067# standard time. Since that's what the local clock *does*, we want to map both
2068# UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
2069# in local time, but so it goes -- it's the way the local clock works.
2070#
2071# When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
2072# so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
2073# z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
2074# (correctly) concludes that z' is not UTC-equivalent to x.
2075#
2076# Because we know z.d said z was in daylight time (else [5] would have held and
2077# we would have stopped then), and we know z.d != z'.d (else [8] would have held
2078# and we have stopped then), and there are only 2 possible values dst() can
2079# return in Eastern, it follows that z'.d must be 0 (which it is in the example,
2080# but the reasoning doesn't depend on the example -- it depends on there being
2081# two possible dst() outcomes, one zero and the other non-zero). Therefore
2082# z' must be in standard time, and is the spelling we want in this case.
2083#
2084# Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
2085# concerned (because it takes z' as being in standard time rather than the
2086# daylight time we intend here), but returning it gives the real-life "local
2087# clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
2088# tz.
2089#
2090# When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
2091# the 1:MM standard time spelling we want.
2092#
2093# So how can this break? One of the assumptions must be violated. Two
2094# possibilities:
2095#
2096# 1) [2] effectively says that y.s is invariant across all y belong to a given
2097# time zone. This isn't true if, for political reasons or continental drift,
2098# a region decides to change its base offset from UTC.
2099#
2100# 2) There may be versions of "double daylight" time where the tail end of
2101# the analysis gives up a step too early. I haven't thought about that
2102# enough to say.
2103#
2104# In any case, it's clear that the default fromutc() is strong enough to handle
2105# "almost all" time zones: so long as the standard offset is invariant, it
2106# doesn't matter if daylight time transition points change from year to year, or
2107# if daylight time is skipped in some years; it doesn't matter how large or
2108# small dst() may get within its bounds; and it doesn't even matter if some
2109# perverse time zone returns a negative dst()). So a breaking case must be
2110# pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002111
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002112try:
2113 from _datetime import *
Brett Cannoncd171c82013-07-04 17:43:24 -04002114except ImportError:
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002115 pass
2116else:
2117 # Clean up unused names
2118 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH,
2119 _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES,
2120 _build_struct_time, _call_tzinfo_method, _check_date_fields,
2121 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2122 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2123 _days_before_year, _days_in_month, _format_time, _is_leap,
2124 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2125 _wrap_strftime, _ymd2ord)
Alexander Belopolskya5658742010-07-23 20:03:53 +00002126 # XXX Since import * above excludes names that start with _,
2127 # docstring does not get overwritten. In the future, it may be
2128 # appropriate to maintain a single module level docstring and
2129 # remove the following line.
2130 from _datetime import __doc__