blob: 1789714034c97fd3333e4c9d27cf2caebbc29f02 [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
1252 def __bool__(self):
1253 if self.second or self.microsecond:
1254 return True
1255 offset = self.utcoffset() or timedelta(0)
1256 return timedelta(hours=self.hour, minutes=self.minute) != offset
1257
1258 # Pickle support.
1259
1260 def _getstate(self):
1261 us2, us3 = divmod(self._microsecond, 256)
1262 us1, us2 = divmod(us2, 256)
1263 basestate = bytes([self._hour, self._minute, self._second,
1264 us1, us2, us3])
1265 if self._tzinfo is None:
1266 return (basestate,)
1267 else:
1268 return (basestate, self._tzinfo)
1269
1270 def __setstate(self, string, tzinfo):
1271 if len(string) != 6 or string[0] >= 24:
1272 raise TypeError("an integer is required")
1273 (self._hour, self._minute, self._second,
1274 us1, us2, us3) = string
1275 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1276 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1277 self._tzinfo = tzinfo
1278 else:
1279 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1280
1281 def __reduce__(self):
1282 return (time, self._getstate())
1283
1284_time_class = time # so functions w/ args named "time" can get at the class
1285
1286time.min = time(0, 0, 0)
1287time.max = time(23, 59, 59, 999999)
1288time.resolution = timedelta(microseconds=1)
1289
1290class datetime(date):
1291 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
1292
1293 The year, month and day arguments are required. tzinfo may be None, or an
Serhiy Storchaka95949422013-08-27 19:40:23 +03001294 instance of a tzinfo subclass. The remaining arguments may be ints.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001295 """
1296
1297 __slots__ = date.__slots__ + (
1298 '_hour', '_minute', '_second',
1299 '_microsecond', '_tzinfo')
1300 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1301 microsecond=0, tzinfo=None):
1302 if isinstance(year, bytes) and len(year) == 10:
1303 # Pickle support
1304 self = date.__new__(cls, year[:4])
1305 self.__setstate(year, month)
1306 return self
1307 _check_tzinfo_arg(tzinfo)
1308 _check_time_fields(hour, minute, second, microsecond)
1309 self = date.__new__(cls, year, month, day)
1310 self._hour = hour
1311 self._minute = minute
1312 self._second = second
1313 self._microsecond = microsecond
1314 self._tzinfo = tzinfo
1315 return self
1316
1317 # Read-only field accessors
1318 @property
1319 def hour(self):
1320 """hour (0-23)"""
1321 return self._hour
1322
1323 @property
1324 def minute(self):
1325 """minute (0-59)"""
1326 return self._minute
1327
1328 @property
1329 def second(self):
1330 """second (0-59)"""
1331 return self._second
1332
1333 @property
1334 def microsecond(self):
1335 """microsecond (0-999999)"""
1336 return self._microsecond
1337
1338 @property
1339 def tzinfo(self):
1340 """timezone info object"""
1341 return self._tzinfo
1342
1343 @classmethod
1344 def fromtimestamp(cls, t, tz=None):
1345 """Construct a datetime from a POSIX timestamp (like time.time()).
1346
1347 A timezone info object may be passed in as well.
1348 """
1349
1350 _check_tzinfo_arg(tz)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001351
1352 converter = _time.localtime if tz is None else _time.gmtime
1353
1354 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001355 us = int(frac * 1e6)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001356
1357 # If timestamp is less than one microsecond smaller than a
1358 # full second, us can be rounded up to 1000000. In this case,
1359 # roll over to seconds, otherwise, ValueError is raised
1360 # by the constructor.
1361 if us == 1000000:
1362 t += 1
1363 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001364 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001365 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1366 result = cls(y, m, d, hh, mm, ss, us, tz)
1367 if tz is not None:
1368 result = tz.fromutc(result)
1369 return result
1370
1371 @classmethod
1372 def utcfromtimestamp(cls, t):
1373 "Construct a UTC datetime from a POSIX timestamp (like time.time())."
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001374 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001375 us = int(frac * 1e6)
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001376
1377 # If timestamp is less than one microsecond smaller than a
1378 # full second, us can be rounded up to 1000000. In this case,
1379 # roll over to seconds, otherwise, ValueError is raised
1380 # by the constructor.
1381 if us == 1000000:
1382 t += 1
1383 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001384 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001385 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1386 return cls(y, m, d, hh, mm, ss, us)
1387
1388 # XXX This is supposed to do better than we *can* do by using time.time(),
1389 # XXX if the platform supports a more accurate way. The C implementation
1390 # XXX uses gettimeofday on platforms that have it, but that isn't
1391 # XXX available from Python. So now() may return different results
1392 # XXX across the implementations.
1393 @classmethod
1394 def now(cls, tz=None):
1395 "Construct a datetime from time.time() and optional time zone info."
1396 t = _time.time()
1397 return cls.fromtimestamp(t, tz)
1398
1399 @classmethod
1400 def utcnow(cls):
1401 "Construct a UTC datetime from time.time()."
1402 t = _time.time()
1403 return cls.utcfromtimestamp(t)
1404
1405 @classmethod
1406 def combine(cls, date, time):
1407 "Construct a datetime from a given date and a given time."
1408 if not isinstance(date, _date_class):
1409 raise TypeError("date argument must be a date instance")
1410 if not isinstance(time, _time_class):
1411 raise TypeError("time argument must be a time instance")
1412 return cls(date.year, date.month, date.day,
1413 time.hour, time.minute, time.second, time.microsecond,
1414 time.tzinfo)
1415
1416 def timetuple(self):
1417 "Return local time tuple compatible with time.localtime()."
1418 dst = self.dst()
1419 if dst is None:
1420 dst = -1
1421 elif dst:
1422 dst = 1
1423 else:
1424 dst = 0
1425 return _build_struct_time(self.year, self.month, self.day,
1426 self.hour, self.minute, self.second,
1427 dst)
1428
Alexander Belopolskya4415142012-06-08 12:33:09 -04001429 def timestamp(self):
1430 "Return POSIX timestamp as float"
1431 if self._tzinfo is None:
1432 return _time.mktime((self.year, self.month, self.day,
1433 self.hour, self.minute, self.second,
1434 -1, -1, -1)) + self.microsecond / 1e6
1435 else:
1436 return (self - _EPOCH).total_seconds()
1437
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001438 def utctimetuple(self):
1439 "Return UTC time tuple compatible with time.gmtime()."
1440 offset = self.utcoffset()
1441 if offset:
1442 self -= offset
1443 y, m, d = self.year, self.month, self.day
1444 hh, mm, ss = self.hour, self.minute, self.second
1445 return _build_struct_time(y, m, d, hh, mm, ss, 0)
1446
1447 def date(self):
1448 "Return the date part."
1449 return date(self._year, self._month, self._day)
1450
1451 def time(self):
1452 "Return the time part, with tzinfo None."
1453 return time(self.hour, self.minute, self.second, self.microsecond)
1454
1455 def timetz(self):
1456 "Return the time part, with same tzinfo."
1457 return time(self.hour, self.minute, self.second, self.microsecond,
1458 self._tzinfo)
1459
1460 def replace(self, year=None, month=None, day=None, hour=None,
1461 minute=None, second=None, microsecond=None, tzinfo=True):
1462 """Return a new datetime with new values for the specified fields."""
1463 if year is None:
1464 year = self.year
1465 if month is None:
1466 month = self.month
1467 if day is None:
1468 day = self.day
1469 if hour is None:
1470 hour = self.hour
1471 if minute is None:
1472 minute = self.minute
1473 if second is None:
1474 second = self.second
1475 if microsecond is None:
1476 microsecond = self.microsecond
1477 if tzinfo is True:
1478 tzinfo = self.tzinfo
1479 _check_date_fields(year, month, day)
1480 _check_time_fields(hour, minute, second, microsecond)
1481 _check_tzinfo_arg(tzinfo)
1482 return datetime(year, month, day, hour, minute, second,
1483 microsecond, tzinfo)
1484
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001485 def astimezone(self, tz=None):
1486 if tz is None:
1487 if self.tzinfo is None:
1488 raise ValueError("astimezone() requires an aware datetime")
1489 ts = (self - _EPOCH) // timedelta(seconds=1)
1490 localtm = _time.localtime(ts)
1491 local = datetime(*localtm[:6])
1492 try:
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001493 # Extract TZ data if available
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001494 gmtoff = localtm.tm_gmtoff
1495 zone = localtm.tm_zone
1496 except AttributeError:
1497 # Compute UTC offset and compare with the value implied
1498 # by tm_isdst. If the values match, use the zone name
1499 # implied by tm_isdst.
1500 delta = local - datetime(*_time.gmtime(ts)[:6])
1501 dst = _time.daylight and localtm.tm_isdst > 0
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001502 gmtoff = -(_time.altzone if dst else _time.timezone)
1503 if delta == timedelta(seconds=gmtoff):
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001504 tz = timezone(delta, _time.tzname[dst])
1505 else:
1506 tz = timezone(delta)
1507 else:
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001508 tz = timezone(timedelta(seconds=gmtoff), zone)
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001509
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001510 elif not isinstance(tz, tzinfo):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001511 raise TypeError("tz argument must be an instance of tzinfo")
1512
1513 mytz = self.tzinfo
1514 if mytz is None:
1515 raise ValueError("astimezone() requires an aware datetime")
1516
1517 if tz is mytz:
1518 return self
1519
1520 # Convert self to UTC, and attach the new time zone object.
1521 myoffset = self.utcoffset()
1522 if myoffset is None:
1523 raise ValueError("astimezone() requires an aware datetime")
1524 utc = (self - myoffset).replace(tzinfo=tz)
1525
1526 # Convert from UTC to tz's local time.
1527 return tz.fromutc(utc)
1528
1529 # Ways to produce a string.
1530
1531 def ctime(self):
1532 "Return ctime() style string."
1533 weekday = self.toordinal() % 7 or 7
1534 return "%s %s %2d %02d:%02d:%02d %04d" % (
1535 _DAYNAMES[weekday],
1536 _MONTHNAMES[self._month],
1537 self._day,
1538 self._hour, self._minute, self._second,
1539 self._year)
1540
1541 def isoformat(self, sep='T'):
1542 """Return the time formatted according to ISO.
1543
1544 This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if
1545 self.microsecond == 0.
1546
1547 If self.tzinfo is not None, the UTC offset is also attached, giving
1548 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.
1549
1550 Optional argument sep specifies the separator between date and
1551 time, default 'T'.
1552 """
1553 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day,
1554 sep) +
1555 _format_time(self._hour, self._minute, self._second,
1556 self._microsecond))
1557 off = self.utcoffset()
1558 if off is not None:
1559 if off.days < 0:
1560 sign = "-"
1561 off = -off
1562 else:
1563 sign = "+"
1564 hh, mm = divmod(off, timedelta(hours=1))
1565 assert not mm % timedelta(minutes=1), "whole minute"
1566 mm //= timedelta(minutes=1)
1567 s += "%s%02d:%02d" % (sign, hh, mm)
1568 return s
1569
1570 def __repr__(self):
1571 """Convert to formal string, for repr()."""
1572 L = [self._year, self._month, self._day, # These are never zero
1573 self._hour, self._minute, self._second, self._microsecond]
1574 if L[-1] == 0:
1575 del L[-1]
1576 if L[-1] == 0:
1577 del L[-1]
1578 s = ", ".join(map(str, L))
1579 s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
1580 if self._tzinfo is not None:
1581 assert s[-1:] == ")"
1582 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1583 return s
1584
1585 def __str__(self):
1586 "Convert to string, for str()."
1587 return self.isoformat(sep=' ')
1588
1589 @classmethod
1590 def strptime(cls, date_string, format):
1591 'string, format -> new datetime parsed from a string (like time.strptime()).'
1592 import _strptime
1593 return _strptime._strptime_datetime(cls, date_string, format)
1594
1595 def utcoffset(self):
1596 """Return the timezone offset in minutes east of UTC (negative west of
1597 UTC)."""
1598 if self._tzinfo is None:
1599 return None
1600 offset = self._tzinfo.utcoffset(self)
1601 _check_utc_offset("utcoffset", offset)
1602 return offset
1603
1604 def tzname(self):
1605 """Return the timezone name.
1606
1607 Note that the name is 100% informational -- there's no requirement that
1608 it mean anything in particular. For example, "GMT", "UTC", "-500",
1609 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1610 """
1611 name = _call_tzinfo_method(self._tzinfo, "tzname", self)
1612 _check_tzname(name)
1613 return name
1614
1615 def dst(self):
1616 """Return 0 if DST is not in effect, or the DST offset (in minutes
1617 eastward) if DST is in effect.
1618
1619 This is purely informational; the DST offset has already been added to
1620 the UTC offset returned by utcoffset() if applicable, so there's no
1621 need to consult dst() unless you're interested in displaying the DST
1622 info.
1623 """
1624 if self._tzinfo is None:
1625 return None
1626 offset = self._tzinfo.dst(self)
1627 _check_utc_offset("dst", offset)
1628 return offset
1629
1630 # Comparisons of datetime objects with other.
1631
1632 def __eq__(self, other):
1633 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001634 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001635 elif not isinstance(other, date):
1636 return NotImplemented
1637 else:
1638 return False
1639
1640 def __ne__(self, other):
1641 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001642 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001643 elif not isinstance(other, date):
1644 return NotImplemented
1645 else:
1646 return True
1647
1648 def __le__(self, other):
1649 if isinstance(other, datetime):
1650 return self._cmp(other) <= 0
1651 elif not isinstance(other, date):
1652 return NotImplemented
1653 else:
1654 _cmperror(self, other)
1655
1656 def __lt__(self, other):
1657 if isinstance(other, datetime):
1658 return self._cmp(other) < 0
1659 elif not isinstance(other, date):
1660 return NotImplemented
1661 else:
1662 _cmperror(self, other)
1663
1664 def __ge__(self, other):
1665 if isinstance(other, datetime):
1666 return self._cmp(other) >= 0
1667 elif not isinstance(other, date):
1668 return NotImplemented
1669 else:
1670 _cmperror(self, other)
1671
1672 def __gt__(self, other):
1673 if isinstance(other, datetime):
1674 return self._cmp(other) > 0
1675 elif not isinstance(other, date):
1676 return NotImplemented
1677 else:
1678 _cmperror(self, other)
1679
Alexander Belopolsky08313822012-06-15 20:19:47 -04001680 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001681 assert isinstance(other, datetime)
1682 mytz = self._tzinfo
1683 ottz = other._tzinfo
1684 myoff = otoff = None
1685
1686 if mytz is ottz:
1687 base_compare = True
1688 else:
Alexander Belopolsky016ef552012-06-15 18:15:25 -04001689 myoff = self.utcoffset()
1690 otoff = other.utcoffset()
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001691 base_compare = myoff == otoff
1692
1693 if base_compare:
1694 return _cmp((self._year, self._month, self._day,
1695 self._hour, self._minute, self._second,
1696 self._microsecond),
1697 (other._year, other._month, other._day,
1698 other._hour, other._minute, other._second,
1699 other._microsecond))
1700 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001701 if allow_mixed:
1702 return 2 # arbitrary non-zero value
1703 else:
1704 raise TypeError("cannot compare naive and aware datetimes")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001705 # XXX What follows could be done more efficiently...
1706 diff = self - other # this will take offsets into account
1707 if diff.days < 0:
1708 return -1
1709 return diff and 1 or 0
1710
1711 def __add__(self, other):
1712 "Add a datetime and a timedelta."
1713 if not isinstance(other, timedelta):
1714 return NotImplemented
1715 delta = timedelta(self.toordinal(),
1716 hours=self._hour,
1717 minutes=self._minute,
1718 seconds=self._second,
1719 microseconds=self._microsecond)
1720 delta += other
1721 hour, rem = divmod(delta.seconds, 3600)
1722 minute, second = divmod(rem, 60)
1723 if 0 < delta.days <= _MAXORDINAL:
1724 return datetime.combine(date.fromordinal(delta.days),
1725 time(hour, minute, second,
1726 delta.microseconds,
1727 tzinfo=self._tzinfo))
1728 raise OverflowError("result out of range")
1729
1730 __radd__ = __add__
1731
1732 def __sub__(self, other):
1733 "Subtract two datetimes, or a datetime and a timedelta."
1734 if not isinstance(other, datetime):
1735 if isinstance(other, timedelta):
1736 return self + -other
1737 return NotImplemented
1738
1739 days1 = self.toordinal()
1740 days2 = other.toordinal()
1741 secs1 = self._second + self._minute * 60 + self._hour * 3600
1742 secs2 = other._second + other._minute * 60 + other._hour * 3600
1743 base = timedelta(days1 - days2,
1744 secs1 - secs2,
1745 self._microsecond - other._microsecond)
1746 if self._tzinfo is other._tzinfo:
1747 return base
1748 myoff = self.utcoffset()
1749 otoff = other.utcoffset()
1750 if myoff == otoff:
1751 return base
1752 if myoff is None or otoff is None:
1753 raise TypeError("cannot mix naive and timezone-aware time")
1754 return base + otoff - myoff
1755
1756 def __hash__(self):
1757 tzoff = self.utcoffset()
1758 if tzoff is None:
1759 return hash(self._getstate()[0])
1760 days = _ymd2ord(self.year, self.month, self.day)
1761 seconds = self.hour * 3600 + self.minute * 60 + self.second
1762 return hash(timedelta(days, seconds, self.microsecond) - tzoff)
1763
1764 # Pickle support.
1765
1766 def _getstate(self):
1767 yhi, ylo = divmod(self._year, 256)
1768 us2, us3 = divmod(self._microsecond, 256)
1769 us1, us2 = divmod(us2, 256)
1770 basestate = bytes([yhi, ylo, self._month, self._day,
1771 self._hour, self._minute, self._second,
1772 us1, us2, us3])
1773 if self._tzinfo is None:
1774 return (basestate,)
1775 else:
1776 return (basestate, self._tzinfo)
1777
1778 def __setstate(self, string, tzinfo):
1779 (yhi, ylo, self._month, self._day, self._hour,
1780 self._minute, self._second, us1, us2, us3) = string
1781 self._year = yhi * 256 + ylo
1782 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1783 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1784 self._tzinfo = tzinfo
1785 else:
1786 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1787
1788 def __reduce__(self):
1789 return (self.__class__, self._getstate())
1790
1791
1792datetime.min = datetime(1, 1, 1)
1793datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
1794datetime.resolution = timedelta(microseconds=1)
1795
1796
1797def _isoweek1monday(year):
1798 # Helper to calculate the day number of the Monday starting week 1
1799 # XXX This could be done more efficiently
1800 THURSDAY = 3
1801 firstday = _ymd2ord(year, 1, 1)
1802 firstweekday = (firstday + 6) % 7 # See weekday() above
1803 week1monday = firstday - firstweekday
1804 if firstweekday > THURSDAY:
1805 week1monday += 7
1806 return week1monday
1807
1808class timezone(tzinfo):
1809 __slots__ = '_offset', '_name'
1810
1811 # Sentinel value to disallow None
1812 _Omitted = object()
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001813 def __new__(cls, offset, name=_Omitted):
1814 if not isinstance(offset, timedelta):
1815 raise TypeError("offset must be a timedelta")
1816 if name is cls._Omitted:
1817 if not offset:
1818 return cls.utc
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001819 name = None
1820 elif not isinstance(name, str):
1821 raise TypeError("name must be a string")
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001822 if not cls._minoffset <= offset <= cls._maxoffset:
1823 raise ValueError("offset must be a timedelta"
1824 " strictly between -timedelta(hours=24) and"
1825 " timedelta(hours=24).")
1826 if (offset.microseconds != 0 or
1827 offset.seconds % 60 != 0):
1828 raise ValueError("offset must be a timedelta"
1829 " representing a whole number of minutes")
1830 return cls._create(offset, name)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001831
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001832 @classmethod
1833 def _create(cls, offset, name=None):
1834 self = tzinfo.__new__(cls)
1835 self._offset = offset
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001836 self._name = name
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001837 return self
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001838
1839 def __getinitargs__(self):
1840 """pickle support"""
1841 if self._name is None:
1842 return (self._offset,)
1843 return (self._offset, self._name)
1844
1845 def __eq__(self, other):
Georg Brandl0085a242012-09-22 09:23:12 +02001846 if type(other) != timezone:
1847 return False
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001848 return self._offset == other._offset
1849
1850 def __hash__(self):
1851 return hash(self._offset)
1852
1853 def __repr__(self):
1854 """Convert to formal string, for repr().
1855
1856 >>> tz = timezone.utc
1857 >>> repr(tz)
1858 'datetime.timezone.utc'
1859 >>> tz = timezone(timedelta(hours=-5), 'EST')
1860 >>> repr(tz)
1861 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
1862 """
1863 if self is self.utc:
1864 return 'datetime.timezone.utc'
1865 if self._name is None:
1866 return "%s(%r)" % ('datetime.' + self.__class__.__name__,
1867 self._offset)
1868 return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__,
1869 self._offset, self._name)
1870
1871 def __str__(self):
1872 return self.tzname(None)
1873
1874 def utcoffset(self, dt):
1875 if isinstance(dt, datetime) or dt is None:
1876 return self._offset
1877 raise TypeError("utcoffset() argument must be a datetime instance"
1878 " or None")
1879
1880 def tzname(self, dt):
1881 if isinstance(dt, datetime) or dt is None:
1882 if self._name is None:
1883 return self._name_from_offset(self._offset)
1884 return self._name
1885 raise TypeError("tzname() argument must be a datetime instance"
1886 " or None")
1887
1888 def dst(self, dt):
1889 if isinstance(dt, datetime) or dt is None:
1890 return None
1891 raise TypeError("dst() argument must be a datetime instance"
1892 " or None")
1893
1894 def fromutc(self, dt):
1895 if isinstance(dt, datetime):
1896 if dt.tzinfo is not self:
1897 raise ValueError("fromutc: dt.tzinfo "
1898 "is not self")
1899 return dt + self._offset
1900 raise TypeError("fromutc() argument must be a datetime instance"
1901 " or None")
1902
1903 _maxoffset = timedelta(hours=23, minutes=59)
1904 _minoffset = -_maxoffset
1905
1906 @staticmethod
1907 def _name_from_offset(delta):
1908 if delta < timedelta(0):
1909 sign = '-'
1910 delta = -delta
1911 else:
1912 sign = '+'
1913 hours, rest = divmod(delta, timedelta(hours=1))
1914 minutes = rest // timedelta(minutes=1)
1915 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
1916
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001917timezone.utc = timezone._create(timedelta(0))
1918timezone.min = timezone._create(timezone._minoffset)
1919timezone.max = timezone._create(timezone._maxoffset)
Alexander Belopolskya4415142012-06-08 12:33:09 -04001920_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001921
Victor Stinner765531d2013-03-26 01:11:54 +01001922# Some time zone algebra. For a datetime x, let
1923# x.n = x stripped of its timezone -- its naive time.
1924# x.o = x.utcoffset(), and assuming that doesn't raise an exception or
1925# return None
1926# x.d = x.dst(), and assuming that doesn't raise an exception or
1927# return None
1928# x.s = x's standard offset, x.o - x.d
1929#
1930# Now some derived rules, where k is a duration (timedelta).
1931#
1932# 1. x.o = x.s + x.d
1933# This follows from the definition of x.s.
1934#
1935# 2. If x and y have the same tzinfo member, x.s = y.s.
1936# This is actually a requirement, an assumption we need to make about
1937# sane tzinfo classes.
1938#
1939# 3. The naive UTC time corresponding to x is x.n - x.o.
1940# This is again a requirement for a sane tzinfo class.
1941#
1942# 4. (x+k).s = x.s
1943# This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
1944#
1945# 5. (x+k).n = x.n + k
1946# Again follows from how arithmetic is defined.
1947#
1948# Now we can explain tz.fromutc(x). Let's assume it's an interesting case
1949# (meaning that the various tzinfo methods exist, and don't blow up or return
1950# None when called).
1951#
1952# The function wants to return a datetime y with timezone tz, equivalent to x.
1953# x is already in UTC.
1954#
1955# By #3, we want
1956#
1957# y.n - y.o = x.n [1]
1958#
1959# The algorithm starts by attaching tz to x.n, and calling that y. So
1960# x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
1961# becomes true; in effect, we want to solve [2] for k:
1962#
1963# (y+k).n - (y+k).o = x.n [2]
1964#
1965# By #1, this is the same as
1966#
1967# (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
1968#
1969# By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
1970# Substituting that into [3],
1971#
1972# x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
1973# k - (y+k).s - (y+k).d = 0; rearranging,
1974# k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
1975# k = y.s - (y+k).d
1976#
1977# On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
1978# approximate k by ignoring the (y+k).d term at first. Note that k can't be
1979# very large, since all offset-returning methods return a duration of magnitude
1980# less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
1981# be 0, so ignoring it has no consequence then.
1982#
1983# In any case, the new value is
1984#
1985# z = y + y.s [4]
1986#
1987# It's helpful to step back at look at [4] from a higher level: it's simply
1988# mapping from UTC to tz's standard time.
1989#
1990# At this point, if
1991#
1992# z.n - z.o = x.n [5]
1993#
1994# we have an equivalent time, and are almost done. The insecurity here is
1995# at the start of daylight time. Picture US Eastern for concreteness. The wall
1996# time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
1997# sense then. The docs ask that an Eastern tzinfo class consider such a time to
1998# be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
1999# on the day DST starts. We want to return the 1:MM EST spelling because that's
2000# the only spelling that makes sense on the local wall clock.
2001#
2002# In fact, if [5] holds at this point, we do have the standard-time spelling,
2003# but that takes a bit of proof. We first prove a stronger result. What's the
2004# difference between the LHS and RHS of [5]? Let
2005#
2006# diff = x.n - (z.n - z.o) [6]
2007#
2008# Now
2009# z.n = by [4]
2010# (y + y.s).n = by #5
2011# y.n + y.s = since y.n = x.n
2012# x.n + y.s = since z and y are have the same tzinfo member,
2013# y.s = z.s by #2
2014# x.n + z.s
2015#
2016# Plugging that back into [6] gives
2017#
2018# diff =
2019# x.n - ((x.n + z.s) - z.o) = expanding
2020# x.n - x.n - z.s + z.o = cancelling
2021# - z.s + z.o = by #2
2022# z.d
2023#
2024# So diff = z.d.
2025#
2026# If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
2027# spelling we wanted in the endcase described above. We're done. Contrarily,
2028# if z.d = 0, then we have a UTC equivalent, and are also done.
2029#
2030# If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
2031# add to z (in effect, z is in tz's standard time, and we need to shift the
2032# local clock into tz's daylight time).
2033#
2034# Let
2035#
2036# z' = z + z.d = z + diff [7]
2037#
2038# and we can again ask whether
2039#
2040# z'.n - z'.o = x.n [8]
2041#
2042# If so, we're done. If not, the tzinfo class is insane, according to the
2043# assumptions we've made. This also requires a bit of proof. As before, let's
2044# compute the difference between the LHS and RHS of [8] (and skipping some of
2045# the justifications for the kinds of substitutions we've done several times
2046# already):
2047#
2048# diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
2049# x.n - (z.n + diff - z'.o) = replacing diff via [6]
2050# x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
2051# x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
2052# - z.n + z.n - z.o + z'.o = cancel z.n
2053# - z.o + z'.o = #1 twice
2054# -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
2055# z'.d - z.d
2056#
2057# So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
2058# we've found the UTC-equivalent so are done. In fact, we stop with [7] and
2059# return z', not bothering to compute z'.d.
2060#
2061# How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
2062# a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
2063# would have to change the result dst() returns: we start in DST, and moving
2064# a little further into it takes us out of DST.
2065#
2066# There isn't a sane case where this can happen. The closest it gets is at
2067# the end of DST, where there's an hour in UTC with no spelling in a hybrid
2068# tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
2069# that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
2070# UTC) because the docs insist on that, but 0:MM is taken as being in daylight
2071# time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
2072# clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
2073# standard time. Since that's what the local clock *does*, we want to map both
2074# UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
2075# in local time, but so it goes -- it's the way the local clock works.
2076#
2077# When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
2078# so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
2079# z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
2080# (correctly) concludes that z' is not UTC-equivalent to x.
2081#
2082# Because we know z.d said z was in daylight time (else [5] would have held and
2083# we would have stopped then), and we know z.d != z'.d (else [8] would have held
2084# and we have stopped then), and there are only 2 possible values dst() can
2085# return in Eastern, it follows that z'.d must be 0 (which it is in the example,
2086# but the reasoning doesn't depend on the example -- it depends on there being
2087# two possible dst() outcomes, one zero and the other non-zero). Therefore
2088# z' must be in standard time, and is the spelling we want in this case.
2089#
2090# Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
2091# concerned (because it takes z' as being in standard time rather than the
2092# daylight time we intend here), but returning it gives the real-life "local
2093# clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
2094# tz.
2095#
2096# When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
2097# the 1:MM standard time spelling we want.
2098#
2099# So how can this break? One of the assumptions must be violated. Two
2100# possibilities:
2101#
2102# 1) [2] effectively says that y.s is invariant across all y belong to a given
2103# time zone. This isn't true if, for political reasons or continental drift,
2104# a region decides to change its base offset from UTC.
2105#
2106# 2) There may be versions of "double daylight" time where the tail end of
2107# the analysis gives up a step too early. I haven't thought about that
2108# enough to say.
2109#
2110# In any case, it's clear that the default fromutc() is strong enough to handle
2111# "almost all" time zones: so long as the standard offset is invariant, it
2112# doesn't matter if daylight time transition points change from year to year, or
2113# if daylight time is skipped in some years; it doesn't matter how large or
2114# small dst() may get within its bounds; and it doesn't even matter if some
2115# perverse time zone returns a negative dst()). So a breaking case must be
2116# pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002117
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002118try:
2119 from _datetime import *
Brett Cannoncd171c82013-07-04 17:43:24 -04002120except ImportError:
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002121 pass
2122else:
2123 # Clean up unused names
2124 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH,
2125 _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES,
2126 _build_struct_time, _call_tzinfo_method, _check_date_fields,
2127 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2128 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2129 _days_before_year, _days_in_month, _format_time, _is_leap,
2130 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2131 _wrap_strftime, _ymd2ord)
Alexander Belopolskya5658742010-07-23 20:03:53 +00002132 # XXX Since import * above excludes names that start with _,
2133 # docstring does not get overwritten. In the future, it may be
2134 # appropriate to maintain a single module level docstring and
2135 # remove the following line.
2136 from _datetime import __doc__