blob: 34e5d387cfac237489a6bfd600fe778d96e3c91e [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
Alexander Belopolsky24d3dee2015-02-28 10:41:57 -0500283def _divide_and_round(a, b):
284 """divide a by b and round result to the nearest integer
285
286 When the ratio is exactly half-way between two integers,
287 the even integer is returned.
288 """
289 # Based on the reference implementation for divmod_near
290 # in Objects/longobject.c.
291 q, r = divmod(a, b)
292 # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
293 # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
294 # positive, 2 * r < b if b negative.
295 r *= 2
296 greater_than_half = r > b if b > 0 else r < b
297 if greater_than_half or r == b and q % 2 == 1:
298 q += 1
299
300 return q
301
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000302class timedelta:
303 """Represent the difference between two datetime objects.
304
305 Supported operators:
306
307 - add, subtract timedelta
308 - unary plus, minus, abs
309 - compare to timedelta
Serhiy Storchaka95949422013-08-27 19:40:23 +0300310 - multiply, divide by int
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000311
312 In addition, datetime supports subtraction of two datetime objects
313 returning a timedelta, and addition or subtraction of a datetime
314 and a timedelta giving a datetime.
315
316 Representation: (days, seconds, microseconds). Why? Because I
317 felt like it.
318 """
319 __slots__ = '_days', '_seconds', '_microseconds'
320
321 def __new__(cls, days=0, seconds=0, microseconds=0,
322 milliseconds=0, minutes=0, hours=0, weeks=0):
323 # Doing this efficiently and accurately in C is going to be difficult
324 # and error-prone, due to ubiquitous overflow possibilities, and that
325 # C double doesn't have enough bits of precision to represent
326 # microseconds over 10K years faithfully. The code here tries to make
327 # explicit where go-fast assumptions can be relied on, in order to
328 # guide the C implementation; it's way more convoluted than speed-
329 # ignoring auto-overflow-to-long idiomatic Python could be.
330
331 # XXX Check that all inputs are ints or floats.
332
333 # Final values, all integer.
334 # s and us fit in 32-bit signed ints; d isn't bounded.
335 d = s = us = 0
336
337 # Normalize everything to days, seconds, microseconds.
338 days += weeks*7
339 seconds += minutes*60 + hours*3600
340 microseconds += milliseconds*1000
341
342 # Get rid of all fractions, and normalize s and us.
343 # Take a deep breath <wink>.
344 if isinstance(days, float):
345 dayfrac, days = _math.modf(days)
346 daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
347 assert daysecondswhole == int(daysecondswhole) # can't overflow
348 s = int(daysecondswhole)
349 assert days == int(days)
350 d = int(days)
351 else:
352 daysecondsfrac = 0.0
353 d = days
354 assert isinstance(daysecondsfrac, float)
355 assert abs(daysecondsfrac) <= 1.0
356 assert isinstance(d, int)
357 assert abs(s) <= 24 * 3600
358 # days isn't referenced again before redefinition
359
360 if isinstance(seconds, float):
361 secondsfrac, seconds = _math.modf(seconds)
362 assert seconds == int(seconds)
363 seconds = int(seconds)
364 secondsfrac += daysecondsfrac
365 assert abs(secondsfrac) <= 2.0
366 else:
367 secondsfrac = daysecondsfrac
368 # daysecondsfrac isn't referenced again
369 assert isinstance(secondsfrac, float)
370 assert abs(secondsfrac) <= 2.0
371
372 assert isinstance(seconds, int)
373 days, seconds = divmod(seconds, 24*3600)
374 d += days
375 s += int(seconds) # can't overflow
376 assert isinstance(s, int)
377 assert abs(s) <= 2 * 24 * 3600
378 # seconds isn't referenced again before redefinition
379
380 usdouble = secondsfrac * 1e6
381 assert abs(usdouble) < 2.1e6 # exact value not critical
382 # secondsfrac isn't referenced again
383
384 if isinstance(microseconds, float):
385 microseconds += usdouble
386 microseconds = round(microseconds, 0)
387 seconds, microseconds = divmod(microseconds, 1e6)
388 assert microseconds == int(microseconds)
389 assert seconds == int(seconds)
390 days, seconds = divmod(seconds, 24.*3600.)
391 assert days == int(days)
392 assert seconds == int(seconds)
393 d += int(days)
394 s += int(seconds) # can't overflow
395 assert isinstance(s, int)
396 assert abs(s) <= 3 * 24 * 3600
397 else:
398 seconds, microseconds = divmod(microseconds, 1000000)
399 days, seconds = divmod(seconds, 24*3600)
400 d += days
401 s += int(seconds) # can't overflow
402 assert isinstance(s, int)
403 assert abs(s) <= 3 * 24 * 3600
404 microseconds = float(microseconds)
405 microseconds += usdouble
406 microseconds = round(microseconds, 0)
407 assert abs(s) <= 3 * 24 * 3600
408 assert abs(microseconds) < 3.1e6
409
410 # Just a little bit of carrying possible for microseconds and seconds.
411 assert isinstance(microseconds, float)
412 assert int(microseconds) == microseconds
413 us = int(microseconds)
414 seconds, us = divmod(us, 1000000)
415 s += seconds # cant't overflow
416 assert isinstance(s, int)
417 days, s = divmod(s, 24*3600)
418 d += days
419
420 assert isinstance(d, int)
421 assert isinstance(s, int) and 0 <= s < 24*3600
422 assert isinstance(us, int) and 0 <= us < 1000000
423
424 self = object.__new__(cls)
425
426 self._days = d
427 self._seconds = s
428 self._microseconds = us
429 if abs(d) > 999999999:
430 raise OverflowError("timedelta # of days is too large: %d" % d)
431
432 return self
433
434 def __repr__(self):
435 if self._microseconds:
436 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
437 self._days,
438 self._seconds,
439 self._microseconds)
440 if self._seconds:
441 return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__,
442 self._days,
443 self._seconds)
444 return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days)
445
446 def __str__(self):
447 mm, ss = divmod(self._seconds, 60)
448 hh, mm = divmod(mm, 60)
449 s = "%d:%02d:%02d" % (hh, mm, ss)
450 if self._days:
451 def plural(n):
452 return n, abs(n) != 1 and "s" or ""
453 s = ("%d day%s, " % plural(self._days)) + s
454 if self._microseconds:
455 s = s + ".%06d" % self._microseconds
456 return s
457
458 def total_seconds(self):
459 """Total seconds in the duration."""
460 return ((self.days * 86400 + self.seconds)*10**6 +
461 self.microseconds) / 10**6
462
463 # Read-only field accessors
464 @property
465 def days(self):
466 """days"""
467 return self._days
468
469 @property
470 def seconds(self):
471 """seconds"""
472 return self._seconds
473
474 @property
475 def microseconds(self):
476 """microseconds"""
477 return self._microseconds
478
479 def __add__(self, other):
480 if isinstance(other, timedelta):
481 # for CPython compatibility, we cannot use
482 # our __class__ here, but need a real timedelta
483 return timedelta(self._days + other._days,
484 self._seconds + other._seconds,
485 self._microseconds + other._microseconds)
486 return NotImplemented
487
488 __radd__ = __add__
489
490 def __sub__(self, other):
491 if isinstance(other, timedelta):
Alexander Belopolskyb6f5ec72011-04-05 20:07:38 -0400492 # for CPython compatibility, we cannot use
493 # our __class__ here, but need a real timedelta
494 return timedelta(self._days - other._days,
495 self._seconds - other._seconds,
496 self._microseconds - other._microseconds)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000497 return NotImplemented
498
499 def __rsub__(self, other):
500 if isinstance(other, timedelta):
501 return -self + other
502 return NotImplemented
503
504 def __neg__(self):
505 # for CPython compatibility, we cannot use
506 # our __class__ here, but need a real timedelta
507 return timedelta(-self._days,
508 -self._seconds,
509 -self._microseconds)
510
511 def __pos__(self):
512 return self
513
514 def __abs__(self):
515 if self._days < 0:
516 return -self
517 else:
518 return self
519
520 def __mul__(self, other):
521 if isinstance(other, int):
522 # for CPython compatibility, we cannot use
523 # our __class__ here, but need a real timedelta
524 return timedelta(self._days * other,
525 self._seconds * other,
526 self._microseconds * other)
527 if isinstance(other, float):
Alexander Belopolsky24d3dee2015-02-28 10:41:57 -0500528 usec = self._to_microseconds()
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000529 a, b = other.as_integer_ratio()
Alexander Belopolsky24d3dee2015-02-28 10:41:57 -0500530 return timedelta(0, 0, _divide_and_round(usec * a, b))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000531 return NotImplemented
532
533 __rmul__ = __mul__
534
535 def _to_microseconds(self):
536 return ((self._days * (24*3600) + self._seconds) * 1000000 +
537 self._microseconds)
538
539 def __floordiv__(self, other):
540 if not isinstance(other, (int, timedelta)):
541 return NotImplemented
542 usec = self._to_microseconds()
543 if isinstance(other, timedelta):
544 return usec // other._to_microseconds()
545 if isinstance(other, int):
546 return timedelta(0, 0, usec // other)
547
548 def __truediv__(self, other):
549 if not isinstance(other, (int, float, timedelta)):
550 return NotImplemented
551 usec = self._to_microseconds()
552 if isinstance(other, timedelta):
553 return usec / other._to_microseconds()
554 if isinstance(other, int):
Alexander Belopolsky24d3dee2015-02-28 10:41:57 -0500555 return timedelta(0, 0, _divide_and_round(usec, other))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000556 if isinstance(other, float):
557 a, b = other.as_integer_ratio()
Alexander Belopolsky24d3dee2015-02-28 10:41:57 -0500558 return timedelta(0, 0, _divide_and_round(b * usec, a))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000559
560 def __mod__(self, other):
561 if isinstance(other, timedelta):
562 r = self._to_microseconds() % other._to_microseconds()
563 return timedelta(0, 0, r)
564 return NotImplemented
565
566 def __divmod__(self, other):
567 if isinstance(other, timedelta):
568 q, r = divmod(self._to_microseconds(),
569 other._to_microseconds())
570 return q, timedelta(0, 0, r)
571 return NotImplemented
572
573 # Comparisons of timedelta objects with other.
574
575 def __eq__(self, other):
576 if isinstance(other, timedelta):
577 return self._cmp(other) == 0
578 else:
579 return False
580
581 def __ne__(self, other):
582 if isinstance(other, timedelta):
583 return self._cmp(other) != 0
584 else:
585 return True
586
587 def __le__(self, other):
588 if isinstance(other, timedelta):
589 return self._cmp(other) <= 0
590 else:
591 _cmperror(self, other)
592
593 def __lt__(self, other):
594 if isinstance(other, timedelta):
595 return self._cmp(other) < 0
596 else:
597 _cmperror(self, other)
598
599 def __ge__(self, other):
600 if isinstance(other, timedelta):
601 return self._cmp(other) >= 0
602 else:
603 _cmperror(self, other)
604
605 def __gt__(self, other):
606 if isinstance(other, timedelta):
607 return self._cmp(other) > 0
608 else:
609 _cmperror(self, other)
610
611 def _cmp(self, other):
612 assert isinstance(other, timedelta)
613 return _cmp(self._getstate(), other._getstate())
614
615 def __hash__(self):
616 return hash(self._getstate())
617
618 def __bool__(self):
619 return (self._days != 0 or
620 self._seconds != 0 or
621 self._microseconds != 0)
622
623 # Pickle support.
624
625 def _getstate(self):
626 return (self._days, self._seconds, self._microseconds)
627
628 def __reduce__(self):
629 return (self.__class__, self._getstate())
630
631timedelta.min = timedelta(-999999999)
632timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
633 microseconds=999999)
634timedelta.resolution = timedelta(microseconds=1)
635
636class date:
637 """Concrete date type.
638
639 Constructors:
640
641 __new__()
642 fromtimestamp()
643 today()
644 fromordinal()
645
646 Operators:
647
648 __repr__, __str__
Serhiy Storchakaa60c2fe2015-03-12 21:56:08 +0200649 __eq__, __le__, __lt__, __ge__, __gt__, __hash__
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000650 __add__, __radd__, __sub__ (add/radd only with timedelta arg)
651
652 Methods:
653
654 timetuple()
655 toordinal()
656 weekday()
657 isoweekday(), isocalendar(), isoformat()
658 ctime()
659 strftime()
660
661 Properties (readonly):
662 year, month, day
663 """
664 __slots__ = '_year', '_month', '_day'
665
666 def __new__(cls, year, month=None, day=None):
667 """Constructor.
668
669 Arguments:
670
671 year, month, day (required, base 1)
672 """
673 if (isinstance(year, bytes) and len(year) == 4 and
674 1 <= year[2] <= 12 and month is None): # Month is sane
675 # Pickle support
676 self = object.__new__(cls)
677 self.__setstate(year)
678 return self
679 _check_date_fields(year, month, day)
680 self = object.__new__(cls)
681 self._year = year
682 self._month = month
683 self._day = day
684 return self
685
686 # Additional constructors
687
688 @classmethod
689 def fromtimestamp(cls, t):
690 "Construct a date from a POSIX timestamp (like time.time())."
691 y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
692 return cls(y, m, d)
693
694 @classmethod
695 def today(cls):
696 "Construct a date from time.time()."
697 t = _time.time()
698 return cls.fromtimestamp(t)
699
700 @classmethod
701 def fromordinal(cls, n):
702 """Contruct a date from a proleptic Gregorian ordinal.
703
704 January 1 of year 1 is day 1. Only the year, month and day are
705 non-zero in the result.
706 """
707 y, m, d = _ord2ymd(n)
708 return cls(y, m, d)
709
710 # Conversions to string
711
712 def __repr__(self):
713 """Convert to formal string, for repr().
714
715 >>> dt = datetime(2010, 1, 1)
716 >>> repr(dt)
717 'datetime.datetime(2010, 1, 1, 0, 0)'
718
719 >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
720 >>> repr(dt)
721 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'
722 """
723 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
724 self._year,
725 self._month,
726 self._day)
727 # XXX These shouldn't depend on time.localtime(), because that
728 # clips the usable dates to [1970 .. 2038). At least ctime() is
729 # easily done without using strftime() -- that's better too because
730 # strftime("%c", ...) is locale specific.
731
732
733 def ctime(self):
734 "Return ctime() style string."
735 weekday = self.toordinal() % 7 or 7
736 return "%s %s %2d 00:00:00 %04d" % (
737 _DAYNAMES[weekday],
738 _MONTHNAMES[self._month],
739 self._day, self._year)
740
741 def strftime(self, fmt):
742 "Format using strftime()."
743 return _wrap_strftime(self, fmt, self.timetuple())
744
745 def __format__(self, fmt):
746 if len(fmt) != 0:
747 return self.strftime(fmt)
748 return str(self)
749
750 def isoformat(self):
751 """Return the date formatted according to ISO.
752
753 This is 'YYYY-MM-DD'.
754
755 References:
756 - http://www.w3.org/TR/NOTE-datetime
757 - http://www.cl.cam.ac.uk/~mgk25/iso-time.html
758 """
759 return "%04d-%02d-%02d" % (self._year, self._month, self._day)
760
761 __str__ = isoformat
762
763 # Read-only field accessors
764 @property
765 def year(self):
766 """year (1-9999)"""
767 return self._year
768
769 @property
770 def month(self):
771 """month (1-12)"""
772 return self._month
773
774 @property
775 def day(self):
776 """day (1-31)"""
777 return self._day
778
Serhiy Storchakaa60c2fe2015-03-12 21:56:08 +0200779 # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,
780 # __hash__ (and helpers)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000781
782 def timetuple(self):
783 "Return local time tuple compatible with time.localtime()."
784 return _build_struct_time(self._year, self._month, self._day,
785 0, 0, 0, -1)
786
787 def toordinal(self):
788 """Return proleptic Gregorian ordinal for the year, month and day.
789
790 January 1 of year 1 is day 1. Only the year, month and day values
791 contribute to the result.
792 """
793 return _ymd2ord(self._year, self._month, self._day)
794
795 def replace(self, year=None, month=None, day=None):
796 """Return a new date with new values for the specified fields."""
797 if year is None:
798 year = self._year
799 if month is None:
800 month = self._month
801 if day is None:
802 day = self._day
803 _check_date_fields(year, month, day)
804 return date(year, month, day)
805
806 # Comparisons of date objects with other.
807
808 def __eq__(self, other):
809 if isinstance(other, date):
810 return self._cmp(other) == 0
811 return NotImplemented
812
813 def __ne__(self, other):
814 if isinstance(other, date):
815 return self._cmp(other) != 0
816 return NotImplemented
817
818 def __le__(self, other):
819 if isinstance(other, date):
820 return self._cmp(other) <= 0
821 return NotImplemented
822
823 def __lt__(self, other):
824 if isinstance(other, date):
825 return self._cmp(other) < 0
826 return NotImplemented
827
828 def __ge__(self, other):
829 if isinstance(other, date):
830 return self._cmp(other) >= 0
831 return NotImplemented
832
833 def __gt__(self, other):
834 if isinstance(other, date):
835 return self._cmp(other) > 0
836 return NotImplemented
837
838 def _cmp(self, other):
839 assert isinstance(other, date)
840 y, m, d = self._year, self._month, self._day
841 y2, m2, d2 = other._year, other._month, other._day
842 return _cmp((y, m, d), (y2, m2, d2))
843
844 def __hash__(self):
845 "Hash."
846 return hash(self._getstate())
847
848 # Computations
849
850 def __add__(self, other):
851 "Add a date to a timedelta."
852 if isinstance(other, timedelta):
853 o = self.toordinal() + other.days
854 if 0 < o <= _MAXORDINAL:
855 return date.fromordinal(o)
856 raise OverflowError("result out of range")
857 return NotImplemented
858
859 __radd__ = __add__
860
861 def __sub__(self, other):
862 """Subtract two dates, or a date and a timedelta."""
863 if isinstance(other, timedelta):
864 return self + timedelta(-other.days)
865 if isinstance(other, date):
866 days1 = self.toordinal()
867 days2 = other.toordinal()
868 return timedelta(days1 - days2)
869 return NotImplemented
870
871 def weekday(self):
872 "Return day of the week, where Monday == 0 ... Sunday == 6."
873 return (self.toordinal() + 6) % 7
874
875 # Day-of-the-week and week-of-the-year, according to ISO
876
877 def isoweekday(self):
878 "Return day of the week, where Monday == 1 ... Sunday == 7."
879 # 1-Jan-0001 is a Monday
880 return self.toordinal() % 7 or 7
881
882 def isocalendar(self):
883 """Return a 3-tuple containing ISO year, week number, and weekday.
884
885 The first ISO week of the year is the (Mon-Sun) week
886 containing the year's first Thursday; everything else derives
887 from that.
888
889 The first week is 1; Monday is 1 ... Sunday is 7.
890
891 ISO calendar algorithm taken from
892 http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
893 """
894 year = self._year
895 week1monday = _isoweek1monday(year)
896 today = _ymd2ord(self._year, self._month, self._day)
897 # Internally, week and day have origin 0
898 week, day = divmod(today - week1monday, 7)
899 if week < 0:
900 year -= 1
901 week1monday = _isoweek1monday(year)
902 week, day = divmod(today - week1monday, 7)
903 elif week >= 52:
904 if today >= _isoweek1monday(year+1):
905 year += 1
906 week = 0
907 return year, week+1, day+1
908
909 # Pickle support.
910
911 def _getstate(self):
912 yhi, ylo = divmod(self._year, 256)
913 return bytes([yhi, ylo, self._month, self._day]),
914
915 def __setstate(self, string):
916 if len(string) != 4 or not (1 <= string[2] <= 12):
917 raise TypeError("not enough arguments")
918 yhi, ylo, self._month, self._day = string
919 self._year = yhi * 256 + ylo
920
921 def __reduce__(self):
922 return (self.__class__, self._getstate())
923
924_date_class = date # so functions w/ args named "date" can get at the class
925
926date.min = date(1, 1, 1)
927date.max = date(9999, 12, 31)
928date.resolution = timedelta(days=1)
929
930class tzinfo:
931 """Abstract base class for time zone info classes.
932
933 Subclasses must override the name(), utcoffset() and dst() methods.
934 """
935 __slots__ = ()
936 def tzname(self, dt):
937 "datetime -> string name of time zone."
938 raise NotImplementedError("tzinfo subclass must override tzname()")
939
940 def utcoffset(self, dt):
941 "datetime -> minutes east of UTC (negative for west of UTC)"
942 raise NotImplementedError("tzinfo subclass must override utcoffset()")
943
944 def dst(self, dt):
945 """datetime -> DST offset in minutes east of UTC.
946
947 Return 0 if DST not in effect. utcoffset() must include the DST
948 offset.
949 """
950 raise NotImplementedError("tzinfo subclass must override dst()")
951
952 def fromutc(self, dt):
953 "datetime in UTC -> datetime in local time."
954
955 if not isinstance(dt, datetime):
956 raise TypeError("fromutc() requires a datetime argument")
957 if dt.tzinfo is not self:
958 raise ValueError("dt.tzinfo is not self")
959
960 dtoff = dt.utcoffset()
961 if dtoff is None:
962 raise ValueError("fromutc() requires a non-None utcoffset() "
963 "result")
964
965 # See the long comment block at the end of this file for an
966 # explanation of this algorithm.
967 dtdst = dt.dst()
968 if dtdst is None:
969 raise ValueError("fromutc() requires a non-None dst() result")
970 delta = dtoff - dtdst
971 if delta:
972 dt += delta
973 dtdst = dt.dst()
974 if dtdst is None:
975 raise ValueError("fromutc(): dt.dst gave inconsistent "
976 "results; cannot convert")
977 return dt + dtdst
978
979 # Pickle support.
980
981 def __reduce__(self):
982 getinitargs = getattr(self, "__getinitargs__", None)
983 if getinitargs:
984 args = getinitargs()
985 else:
986 args = ()
987 getstate = getattr(self, "__getstate__", None)
988 if getstate:
989 state = getstate()
990 else:
991 state = getattr(self, "__dict__", None) or None
992 if state is None:
993 return (self.__class__, args)
994 else:
995 return (self.__class__, args, state)
996
997_tzinfo_class = tzinfo
998
999class time:
1000 """Time with time zone.
1001
1002 Constructors:
1003
1004 __new__()
1005
1006 Operators:
1007
1008 __repr__, __str__
Serhiy Storchakaa60c2fe2015-03-12 21:56:08 +02001009 __eq__, __le__, __lt__, __ge__, __gt__, __hash__
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001010
1011 Methods:
1012
1013 strftime()
1014 isoformat()
1015 utcoffset()
1016 tzname()
1017 dst()
1018
1019 Properties (readonly):
1020 hour, minute, second, microsecond, tzinfo
1021 """
1022
1023 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
1024 """Constructor.
1025
1026 Arguments:
1027
1028 hour, minute (required)
1029 second, microsecond (default to zero)
1030 tzinfo (default to None)
1031 """
1032 self = object.__new__(cls)
1033 if isinstance(hour, bytes) and len(hour) == 6:
1034 # Pickle support
1035 self.__setstate(hour, minute or None)
1036 return self
1037 _check_tzinfo_arg(tzinfo)
1038 _check_time_fields(hour, minute, second, microsecond)
1039 self._hour = hour
1040 self._minute = minute
1041 self._second = second
1042 self._microsecond = microsecond
1043 self._tzinfo = tzinfo
1044 return self
1045
1046 # Read-only field accessors
1047 @property
1048 def hour(self):
1049 """hour (0-23)"""
1050 return self._hour
1051
1052 @property
1053 def minute(self):
1054 """minute (0-59)"""
1055 return self._minute
1056
1057 @property
1058 def second(self):
1059 """second (0-59)"""
1060 return self._second
1061
1062 @property
1063 def microsecond(self):
1064 """microsecond (0-999999)"""
1065 return self._microsecond
1066
1067 @property
1068 def tzinfo(self):
1069 """timezone info object"""
1070 return self._tzinfo
1071
1072 # Standard conversions, __hash__ (and helpers)
1073
1074 # Comparisons of time objects with other.
1075
1076 def __eq__(self, other):
1077 if isinstance(other, time):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001078 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001079 else:
1080 return False
1081
1082 def __ne__(self, other):
1083 if isinstance(other, time):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001084 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001085 else:
1086 return True
1087
1088 def __le__(self, other):
1089 if isinstance(other, time):
1090 return self._cmp(other) <= 0
1091 else:
1092 _cmperror(self, other)
1093
1094 def __lt__(self, other):
1095 if isinstance(other, time):
1096 return self._cmp(other) < 0
1097 else:
1098 _cmperror(self, other)
1099
1100 def __ge__(self, other):
1101 if isinstance(other, time):
1102 return self._cmp(other) >= 0
1103 else:
1104 _cmperror(self, other)
1105
1106 def __gt__(self, other):
1107 if isinstance(other, time):
1108 return self._cmp(other) > 0
1109 else:
1110 _cmperror(self, other)
1111
Alexander Belopolsky08313822012-06-15 20:19:47 -04001112 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001113 assert isinstance(other, time)
1114 mytz = self._tzinfo
1115 ottz = other._tzinfo
1116 myoff = otoff = None
1117
1118 if mytz is ottz:
1119 base_compare = True
1120 else:
1121 myoff = self.utcoffset()
1122 otoff = other.utcoffset()
1123 base_compare = myoff == otoff
1124
1125 if base_compare:
1126 return _cmp((self._hour, self._minute, self._second,
1127 self._microsecond),
1128 (other._hour, other._minute, other._second,
1129 other._microsecond))
1130 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001131 if allow_mixed:
1132 return 2 # arbitrary non-zero value
1133 else:
1134 raise TypeError("cannot compare naive and aware times")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001135 myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
1136 othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
1137 return _cmp((myhhmm, self._second, self._microsecond),
1138 (othhmm, other._second, other._microsecond))
1139
1140 def __hash__(self):
1141 """Hash."""
1142 tzoff = self.utcoffset()
1143 if not tzoff: # zero or None
1144 return hash(self._getstate()[0])
1145 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
1146 timedelta(hours=1))
1147 assert not m % timedelta(minutes=1), "whole minute"
1148 m //= timedelta(minutes=1)
1149 if 0 <= h < 24:
1150 return hash(time(h, m, self.second, self.microsecond))
1151 return hash((h, m, self.second, self.microsecond))
1152
1153 # Conversion to string
1154
1155 def _tzstr(self, sep=":"):
1156 """Return formatted timezone offset (+xx:xx) or None."""
1157 off = self.utcoffset()
1158 if off is not None:
1159 if off.days < 0:
1160 sign = "-"
1161 off = -off
1162 else:
1163 sign = "+"
1164 hh, mm = divmod(off, timedelta(hours=1))
1165 assert not mm % timedelta(minutes=1), "whole minute"
1166 mm //= timedelta(minutes=1)
1167 assert 0 <= hh < 24
1168 off = "%s%02d%s%02d" % (sign, hh, sep, mm)
1169 return off
1170
1171 def __repr__(self):
1172 """Convert to formal string, for repr()."""
1173 if self._microsecond != 0:
1174 s = ", %d, %d" % (self._second, self._microsecond)
1175 elif self._second != 0:
1176 s = ", %d" % self._second
1177 else:
1178 s = ""
1179 s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__,
1180 self._hour, self._minute, s)
1181 if self._tzinfo is not None:
1182 assert s[-1:] == ")"
1183 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1184 return s
1185
1186 def isoformat(self):
1187 """Return the time formatted according to ISO.
1188
1189 This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if
1190 self.microsecond == 0.
1191 """
1192 s = _format_time(self._hour, self._minute, self._second,
1193 self._microsecond)
1194 tz = self._tzstr()
1195 if tz:
1196 s += tz
1197 return s
1198
1199 __str__ = isoformat
1200
1201 def strftime(self, fmt):
1202 """Format using strftime(). The date part of the timestamp passed
1203 to underlying strftime should not be used.
1204 """
Alexander Belopolskyb8bb4662011-01-08 00:13:34 +00001205 # The year must be >= 1000 else Python's strftime implementation
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001206 # can raise a bogus exception.
1207 timetuple = (1900, 1, 1,
1208 self._hour, self._minute, self._second,
1209 0, 1, -1)
1210 return _wrap_strftime(self, fmt, timetuple)
1211
1212 def __format__(self, fmt):
1213 if len(fmt) != 0:
1214 return self.strftime(fmt)
1215 return str(self)
1216
1217 # Timezone functions
1218
1219 def utcoffset(self):
1220 """Return the timezone offset in minutes east of UTC (negative west of
1221 UTC)."""
1222 if self._tzinfo is None:
1223 return None
1224 offset = self._tzinfo.utcoffset(None)
1225 _check_utc_offset("utcoffset", offset)
1226 return offset
1227
1228 def tzname(self):
1229 """Return the timezone name.
1230
1231 Note that the name is 100% informational -- there's no requirement that
1232 it mean anything in particular. For example, "GMT", "UTC", "-500",
1233 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1234 """
1235 if self._tzinfo is None:
1236 return None
1237 name = self._tzinfo.tzname(None)
1238 _check_tzname(name)
1239 return name
1240
1241 def dst(self):
1242 """Return 0 if DST is not in effect, or the DST offset (in minutes
1243 eastward) if DST is in effect.
1244
1245 This is purely informational; the DST offset has already been added to
1246 the UTC offset returned by utcoffset() if applicable, so there's no
1247 need to consult dst() unless you're interested in displaying the DST
1248 info.
1249 """
1250 if self._tzinfo is None:
1251 return None
1252 offset = self._tzinfo.dst(None)
1253 _check_utc_offset("dst", offset)
1254 return offset
1255
1256 def replace(self, hour=None, minute=None, second=None, microsecond=None,
1257 tzinfo=True):
1258 """Return a new time with new values for the specified fields."""
1259 if hour is None:
1260 hour = self.hour
1261 if minute is None:
1262 minute = self.minute
1263 if second is None:
1264 second = self.second
1265 if microsecond is None:
1266 microsecond = self.microsecond
1267 if tzinfo is True:
1268 tzinfo = self.tzinfo
1269 _check_time_fields(hour, minute, second, microsecond)
1270 _check_tzinfo_arg(tzinfo)
1271 return time(hour, minute, second, microsecond, tzinfo)
1272
1273 def __bool__(self):
1274 if self.second or self.microsecond:
1275 return True
1276 offset = self.utcoffset() or timedelta(0)
1277 return timedelta(hours=self.hour, minutes=self.minute) != offset
1278
1279 # Pickle support.
1280
1281 def _getstate(self):
1282 us2, us3 = divmod(self._microsecond, 256)
1283 us1, us2 = divmod(us2, 256)
1284 basestate = bytes([self._hour, self._minute, self._second,
1285 us1, us2, us3])
1286 if self._tzinfo is None:
1287 return (basestate,)
1288 else:
1289 return (basestate, self._tzinfo)
1290
1291 def __setstate(self, string, tzinfo):
1292 if len(string) != 6 or string[0] >= 24:
1293 raise TypeError("an integer is required")
1294 (self._hour, self._minute, self._second,
1295 us1, us2, us3) = string
1296 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1297 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1298 self._tzinfo = tzinfo
1299 else:
1300 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1301
1302 def __reduce__(self):
1303 return (time, self._getstate())
1304
1305_time_class = time # so functions w/ args named "time" can get at the class
1306
1307time.min = time(0, 0, 0)
1308time.max = time(23, 59, 59, 999999)
1309time.resolution = timedelta(microseconds=1)
1310
1311class datetime(date):
1312 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
1313
1314 The year, month and day arguments are required. tzinfo may be None, or an
Serhiy Storchaka95949422013-08-27 19:40:23 +03001315 instance of a tzinfo subclass. The remaining arguments may be ints.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001316 """
1317
1318 __slots__ = date.__slots__ + (
1319 '_hour', '_minute', '_second',
1320 '_microsecond', '_tzinfo')
1321 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1322 microsecond=0, tzinfo=None):
1323 if isinstance(year, bytes) and len(year) == 10:
1324 # Pickle support
1325 self = date.__new__(cls, year[:4])
1326 self.__setstate(year, month)
1327 return self
1328 _check_tzinfo_arg(tzinfo)
1329 _check_time_fields(hour, minute, second, microsecond)
1330 self = date.__new__(cls, year, month, day)
1331 self._hour = hour
1332 self._minute = minute
1333 self._second = second
1334 self._microsecond = microsecond
1335 self._tzinfo = tzinfo
1336 return self
1337
1338 # Read-only field accessors
1339 @property
1340 def hour(self):
1341 """hour (0-23)"""
1342 return self._hour
1343
1344 @property
1345 def minute(self):
1346 """minute (0-59)"""
1347 return self._minute
1348
1349 @property
1350 def second(self):
1351 """second (0-59)"""
1352 return self._second
1353
1354 @property
1355 def microsecond(self):
1356 """microsecond (0-999999)"""
1357 return self._microsecond
1358
1359 @property
1360 def tzinfo(self):
1361 """timezone info object"""
1362 return self._tzinfo
1363
1364 @classmethod
1365 def fromtimestamp(cls, t, tz=None):
1366 """Construct a datetime from a POSIX timestamp (like time.time()).
1367
1368 A timezone info object may be passed in as well.
1369 """
1370
1371 _check_tzinfo_arg(tz)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001372
1373 converter = _time.localtime if tz is None else _time.gmtime
1374
1375 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001376 us = int(frac * 1e6)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001377
1378 # If timestamp is less than one microsecond smaller than a
1379 # full second, us can be rounded up to 1000000. In this case,
1380 # roll over to seconds, otherwise, ValueError is raised
1381 # by the constructor.
1382 if us == 1000000:
1383 t += 1
1384 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001385 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001386 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1387 result = cls(y, m, d, hh, mm, ss, us, tz)
1388 if tz is not None:
1389 result = tz.fromutc(result)
1390 return result
1391
1392 @classmethod
1393 def utcfromtimestamp(cls, t):
1394 "Construct a UTC datetime from a POSIX timestamp (like time.time())."
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001395 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001396 us = int(frac * 1e6)
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001397
1398 # If timestamp is less than one microsecond smaller than a
1399 # full second, us can be rounded up to 1000000. In this case,
1400 # roll over to seconds, otherwise, ValueError is raised
1401 # by the constructor.
1402 if us == 1000000:
1403 t += 1
1404 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001405 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001406 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1407 return cls(y, m, d, hh, mm, ss, us)
1408
1409 # XXX This is supposed to do better than we *can* do by using time.time(),
1410 # XXX if the platform supports a more accurate way. The C implementation
1411 # XXX uses gettimeofday on platforms that have it, but that isn't
1412 # XXX available from Python. So now() may return different results
1413 # XXX across the implementations.
1414 @classmethod
1415 def now(cls, tz=None):
1416 "Construct a datetime from time.time() and optional time zone info."
1417 t = _time.time()
1418 return cls.fromtimestamp(t, tz)
1419
1420 @classmethod
1421 def utcnow(cls):
1422 "Construct a UTC datetime from time.time()."
1423 t = _time.time()
1424 return cls.utcfromtimestamp(t)
1425
1426 @classmethod
1427 def combine(cls, date, time):
1428 "Construct a datetime from a given date and a given time."
1429 if not isinstance(date, _date_class):
1430 raise TypeError("date argument must be a date instance")
1431 if not isinstance(time, _time_class):
1432 raise TypeError("time argument must be a time instance")
1433 return cls(date.year, date.month, date.day,
1434 time.hour, time.minute, time.second, time.microsecond,
1435 time.tzinfo)
1436
1437 def timetuple(self):
1438 "Return local time tuple compatible with time.localtime()."
1439 dst = self.dst()
1440 if dst is None:
1441 dst = -1
1442 elif dst:
1443 dst = 1
1444 else:
1445 dst = 0
1446 return _build_struct_time(self.year, self.month, self.day,
1447 self.hour, self.minute, self.second,
1448 dst)
1449
Alexander Belopolskya4415142012-06-08 12:33:09 -04001450 def timestamp(self):
1451 "Return POSIX timestamp as float"
1452 if self._tzinfo is None:
1453 return _time.mktime((self.year, self.month, self.day,
1454 self.hour, self.minute, self.second,
1455 -1, -1, -1)) + self.microsecond / 1e6
1456 else:
1457 return (self - _EPOCH).total_seconds()
1458
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001459 def utctimetuple(self):
1460 "Return UTC time tuple compatible with time.gmtime()."
1461 offset = self.utcoffset()
1462 if offset:
1463 self -= offset
1464 y, m, d = self.year, self.month, self.day
1465 hh, mm, ss = self.hour, self.minute, self.second
1466 return _build_struct_time(y, m, d, hh, mm, ss, 0)
1467
1468 def date(self):
1469 "Return the date part."
1470 return date(self._year, self._month, self._day)
1471
1472 def time(self):
1473 "Return the time part, with tzinfo None."
1474 return time(self.hour, self.minute, self.second, self.microsecond)
1475
1476 def timetz(self):
1477 "Return the time part, with same tzinfo."
1478 return time(self.hour, self.minute, self.second, self.microsecond,
1479 self._tzinfo)
1480
1481 def replace(self, year=None, month=None, day=None, hour=None,
1482 minute=None, second=None, microsecond=None, tzinfo=True):
1483 """Return a new datetime with new values for the specified fields."""
1484 if year is None:
1485 year = self.year
1486 if month is None:
1487 month = self.month
1488 if day is None:
1489 day = self.day
1490 if hour is None:
1491 hour = self.hour
1492 if minute is None:
1493 minute = self.minute
1494 if second is None:
1495 second = self.second
1496 if microsecond is None:
1497 microsecond = self.microsecond
1498 if tzinfo is True:
1499 tzinfo = self.tzinfo
1500 _check_date_fields(year, month, day)
1501 _check_time_fields(hour, minute, second, microsecond)
1502 _check_tzinfo_arg(tzinfo)
1503 return datetime(year, month, day, hour, minute, second,
1504 microsecond, tzinfo)
1505
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001506 def astimezone(self, tz=None):
1507 if tz is None:
1508 if self.tzinfo is None:
1509 raise ValueError("astimezone() requires an aware datetime")
1510 ts = (self - _EPOCH) // timedelta(seconds=1)
1511 localtm = _time.localtime(ts)
1512 local = datetime(*localtm[:6])
1513 try:
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001514 # Extract TZ data if available
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001515 gmtoff = localtm.tm_gmtoff
1516 zone = localtm.tm_zone
1517 except AttributeError:
1518 # Compute UTC offset and compare with the value implied
1519 # by tm_isdst. If the values match, use the zone name
1520 # implied by tm_isdst.
1521 delta = local - datetime(*_time.gmtime(ts)[:6])
1522 dst = _time.daylight and localtm.tm_isdst > 0
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001523 gmtoff = -(_time.altzone if dst else _time.timezone)
1524 if delta == timedelta(seconds=gmtoff):
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001525 tz = timezone(delta, _time.tzname[dst])
1526 else:
1527 tz = timezone(delta)
1528 else:
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001529 tz = timezone(timedelta(seconds=gmtoff), zone)
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001530
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001531 elif not isinstance(tz, tzinfo):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001532 raise TypeError("tz argument must be an instance of tzinfo")
1533
1534 mytz = self.tzinfo
1535 if mytz is None:
1536 raise ValueError("astimezone() requires an aware datetime")
1537
1538 if tz is mytz:
1539 return self
1540
1541 # Convert self to UTC, and attach the new time zone object.
1542 myoffset = self.utcoffset()
1543 if myoffset is None:
1544 raise ValueError("astimezone() requires an aware datetime")
1545 utc = (self - myoffset).replace(tzinfo=tz)
1546
1547 # Convert from UTC to tz's local time.
1548 return tz.fromutc(utc)
1549
1550 # Ways to produce a string.
1551
1552 def ctime(self):
1553 "Return ctime() style string."
1554 weekday = self.toordinal() % 7 or 7
1555 return "%s %s %2d %02d:%02d:%02d %04d" % (
1556 _DAYNAMES[weekday],
1557 _MONTHNAMES[self._month],
1558 self._day,
1559 self._hour, self._minute, self._second,
1560 self._year)
1561
1562 def isoformat(self, sep='T'):
1563 """Return the time formatted according to ISO.
1564
1565 This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if
1566 self.microsecond == 0.
1567
1568 If self.tzinfo is not None, the UTC offset is also attached, giving
1569 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.
1570
1571 Optional argument sep specifies the separator between date and
1572 time, default 'T'.
1573 """
1574 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day,
1575 sep) +
1576 _format_time(self._hour, self._minute, self._second,
1577 self._microsecond))
1578 off = self.utcoffset()
1579 if off is not None:
1580 if off.days < 0:
1581 sign = "-"
1582 off = -off
1583 else:
1584 sign = "+"
1585 hh, mm = divmod(off, timedelta(hours=1))
1586 assert not mm % timedelta(minutes=1), "whole minute"
1587 mm //= timedelta(minutes=1)
1588 s += "%s%02d:%02d" % (sign, hh, mm)
1589 return s
1590
1591 def __repr__(self):
1592 """Convert to formal string, for repr()."""
1593 L = [self._year, self._month, self._day, # These are never zero
1594 self._hour, self._minute, self._second, self._microsecond]
1595 if L[-1] == 0:
1596 del L[-1]
1597 if L[-1] == 0:
1598 del L[-1]
1599 s = ", ".join(map(str, L))
1600 s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
1601 if self._tzinfo is not None:
1602 assert s[-1:] == ")"
1603 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1604 return s
1605
1606 def __str__(self):
1607 "Convert to string, for str()."
1608 return self.isoformat(sep=' ')
1609
1610 @classmethod
1611 def strptime(cls, date_string, format):
1612 'string, format -> new datetime parsed from a string (like time.strptime()).'
1613 import _strptime
1614 return _strptime._strptime_datetime(cls, date_string, format)
1615
1616 def utcoffset(self):
1617 """Return the timezone offset in minutes east of UTC (negative west of
1618 UTC)."""
1619 if self._tzinfo is None:
1620 return None
1621 offset = self._tzinfo.utcoffset(self)
1622 _check_utc_offset("utcoffset", offset)
1623 return offset
1624
1625 def tzname(self):
1626 """Return the timezone name.
1627
1628 Note that the name is 100% informational -- there's no requirement that
1629 it mean anything in particular. For example, "GMT", "UTC", "-500",
1630 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1631 """
1632 name = _call_tzinfo_method(self._tzinfo, "tzname", self)
1633 _check_tzname(name)
1634 return name
1635
1636 def dst(self):
1637 """Return 0 if DST is not in effect, or the DST offset (in minutes
1638 eastward) if DST is in effect.
1639
1640 This is purely informational; the DST offset has already been added to
1641 the UTC offset returned by utcoffset() if applicable, so there's no
1642 need to consult dst() unless you're interested in displaying the DST
1643 info.
1644 """
1645 if self._tzinfo is None:
1646 return None
1647 offset = self._tzinfo.dst(self)
1648 _check_utc_offset("dst", offset)
1649 return offset
1650
1651 # Comparisons of datetime objects with other.
1652
1653 def __eq__(self, other):
1654 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001655 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001656 elif not isinstance(other, date):
1657 return NotImplemented
1658 else:
1659 return False
1660
1661 def __ne__(self, other):
1662 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001663 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001664 elif not isinstance(other, date):
1665 return NotImplemented
1666 else:
1667 return True
1668
1669 def __le__(self, other):
1670 if isinstance(other, datetime):
1671 return self._cmp(other) <= 0
1672 elif not isinstance(other, date):
1673 return NotImplemented
1674 else:
1675 _cmperror(self, other)
1676
1677 def __lt__(self, other):
1678 if isinstance(other, datetime):
1679 return self._cmp(other) < 0
1680 elif not isinstance(other, date):
1681 return NotImplemented
1682 else:
1683 _cmperror(self, other)
1684
1685 def __ge__(self, other):
1686 if isinstance(other, datetime):
1687 return self._cmp(other) >= 0
1688 elif not isinstance(other, date):
1689 return NotImplemented
1690 else:
1691 _cmperror(self, other)
1692
1693 def __gt__(self, other):
1694 if isinstance(other, datetime):
1695 return self._cmp(other) > 0
1696 elif not isinstance(other, date):
1697 return NotImplemented
1698 else:
1699 _cmperror(self, other)
1700
Alexander Belopolsky08313822012-06-15 20:19:47 -04001701 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001702 assert isinstance(other, datetime)
1703 mytz = self._tzinfo
1704 ottz = other._tzinfo
1705 myoff = otoff = None
1706
1707 if mytz is ottz:
1708 base_compare = True
1709 else:
Alexander Belopolsky016ef552012-06-15 18:15:25 -04001710 myoff = self.utcoffset()
1711 otoff = other.utcoffset()
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001712 base_compare = myoff == otoff
1713
1714 if base_compare:
1715 return _cmp((self._year, self._month, self._day,
1716 self._hour, self._minute, self._second,
1717 self._microsecond),
1718 (other._year, other._month, other._day,
1719 other._hour, other._minute, other._second,
1720 other._microsecond))
1721 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001722 if allow_mixed:
1723 return 2 # arbitrary non-zero value
1724 else:
1725 raise TypeError("cannot compare naive and aware datetimes")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001726 # XXX What follows could be done more efficiently...
1727 diff = self - other # this will take offsets into account
1728 if diff.days < 0:
1729 return -1
1730 return diff and 1 or 0
1731
1732 def __add__(self, other):
1733 "Add a datetime and a timedelta."
1734 if not isinstance(other, timedelta):
1735 return NotImplemented
1736 delta = timedelta(self.toordinal(),
1737 hours=self._hour,
1738 minutes=self._minute,
1739 seconds=self._second,
1740 microseconds=self._microsecond)
1741 delta += other
1742 hour, rem = divmod(delta.seconds, 3600)
1743 minute, second = divmod(rem, 60)
1744 if 0 < delta.days <= _MAXORDINAL:
1745 return datetime.combine(date.fromordinal(delta.days),
1746 time(hour, minute, second,
1747 delta.microseconds,
1748 tzinfo=self._tzinfo))
1749 raise OverflowError("result out of range")
1750
1751 __radd__ = __add__
1752
1753 def __sub__(self, other):
1754 "Subtract two datetimes, or a datetime and a timedelta."
1755 if not isinstance(other, datetime):
1756 if isinstance(other, timedelta):
1757 return self + -other
1758 return NotImplemented
1759
1760 days1 = self.toordinal()
1761 days2 = other.toordinal()
1762 secs1 = self._second + self._minute * 60 + self._hour * 3600
1763 secs2 = other._second + other._minute * 60 + other._hour * 3600
1764 base = timedelta(days1 - days2,
1765 secs1 - secs2,
1766 self._microsecond - other._microsecond)
1767 if self._tzinfo is other._tzinfo:
1768 return base
1769 myoff = self.utcoffset()
1770 otoff = other.utcoffset()
1771 if myoff == otoff:
1772 return base
1773 if myoff is None or otoff is None:
1774 raise TypeError("cannot mix naive and timezone-aware time")
1775 return base + otoff - myoff
1776
1777 def __hash__(self):
1778 tzoff = self.utcoffset()
1779 if tzoff is None:
1780 return hash(self._getstate()[0])
1781 days = _ymd2ord(self.year, self.month, self.day)
1782 seconds = self.hour * 3600 + self.minute * 60 + self.second
1783 return hash(timedelta(days, seconds, self.microsecond) - tzoff)
1784
1785 # Pickle support.
1786
1787 def _getstate(self):
1788 yhi, ylo = divmod(self._year, 256)
1789 us2, us3 = divmod(self._microsecond, 256)
1790 us1, us2 = divmod(us2, 256)
1791 basestate = bytes([yhi, ylo, self._month, self._day,
1792 self._hour, self._minute, self._second,
1793 us1, us2, us3])
1794 if self._tzinfo is None:
1795 return (basestate,)
1796 else:
1797 return (basestate, self._tzinfo)
1798
1799 def __setstate(self, string, tzinfo):
1800 (yhi, ylo, self._month, self._day, self._hour,
1801 self._minute, self._second, us1, us2, us3) = string
1802 self._year = yhi * 256 + ylo
1803 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1804 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1805 self._tzinfo = tzinfo
1806 else:
1807 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1808
1809 def __reduce__(self):
1810 return (self.__class__, self._getstate())
1811
1812
1813datetime.min = datetime(1, 1, 1)
1814datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
1815datetime.resolution = timedelta(microseconds=1)
1816
1817
1818def _isoweek1monday(year):
1819 # Helper to calculate the day number of the Monday starting week 1
1820 # XXX This could be done more efficiently
1821 THURSDAY = 3
1822 firstday = _ymd2ord(year, 1, 1)
1823 firstweekday = (firstday + 6) % 7 # See weekday() above
1824 week1monday = firstday - firstweekday
1825 if firstweekday > THURSDAY:
1826 week1monday += 7
1827 return week1monday
1828
1829class timezone(tzinfo):
1830 __slots__ = '_offset', '_name'
1831
1832 # Sentinel value to disallow None
1833 _Omitted = object()
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001834 def __new__(cls, offset, name=_Omitted):
1835 if not isinstance(offset, timedelta):
1836 raise TypeError("offset must be a timedelta")
1837 if name is cls._Omitted:
1838 if not offset:
1839 return cls.utc
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001840 name = None
1841 elif not isinstance(name, str):
1842 raise TypeError("name must be a string")
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001843 if not cls._minoffset <= offset <= cls._maxoffset:
1844 raise ValueError("offset must be a timedelta"
1845 " strictly between -timedelta(hours=24) and"
1846 " timedelta(hours=24).")
1847 if (offset.microseconds != 0 or
1848 offset.seconds % 60 != 0):
1849 raise ValueError("offset must be a timedelta"
1850 " representing a whole number of minutes")
1851 return cls._create(offset, name)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001852
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001853 @classmethod
1854 def _create(cls, offset, name=None):
1855 self = tzinfo.__new__(cls)
1856 self._offset = offset
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001857 self._name = name
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001858 return self
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001859
1860 def __getinitargs__(self):
1861 """pickle support"""
1862 if self._name is None:
1863 return (self._offset,)
1864 return (self._offset, self._name)
1865
1866 def __eq__(self, other):
Georg Brandl0085a242012-09-22 09:23:12 +02001867 if type(other) != timezone:
1868 return False
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001869 return self._offset == other._offset
1870
1871 def __hash__(self):
1872 return hash(self._offset)
1873
1874 def __repr__(self):
1875 """Convert to formal string, for repr().
1876
1877 >>> tz = timezone.utc
1878 >>> repr(tz)
1879 'datetime.timezone.utc'
1880 >>> tz = timezone(timedelta(hours=-5), 'EST')
1881 >>> repr(tz)
1882 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
1883 """
1884 if self is self.utc:
1885 return 'datetime.timezone.utc'
1886 if self._name is None:
1887 return "%s(%r)" % ('datetime.' + self.__class__.__name__,
1888 self._offset)
1889 return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__,
1890 self._offset, self._name)
1891
1892 def __str__(self):
1893 return self.tzname(None)
1894
1895 def utcoffset(self, dt):
1896 if isinstance(dt, datetime) or dt is None:
1897 return self._offset
1898 raise TypeError("utcoffset() argument must be a datetime instance"
1899 " or None")
1900
1901 def tzname(self, dt):
1902 if isinstance(dt, datetime) or dt is None:
1903 if self._name is None:
1904 return self._name_from_offset(self._offset)
1905 return self._name
1906 raise TypeError("tzname() argument must be a datetime instance"
1907 " or None")
1908
1909 def dst(self, dt):
1910 if isinstance(dt, datetime) or dt is None:
1911 return None
1912 raise TypeError("dst() argument must be a datetime instance"
1913 " or None")
1914
1915 def fromutc(self, dt):
1916 if isinstance(dt, datetime):
1917 if dt.tzinfo is not self:
1918 raise ValueError("fromutc: dt.tzinfo "
1919 "is not self")
1920 return dt + self._offset
1921 raise TypeError("fromutc() argument must be a datetime instance"
1922 " or None")
1923
1924 _maxoffset = timedelta(hours=23, minutes=59)
1925 _minoffset = -_maxoffset
1926
1927 @staticmethod
1928 def _name_from_offset(delta):
1929 if delta < timedelta(0):
1930 sign = '-'
1931 delta = -delta
1932 else:
1933 sign = '+'
1934 hours, rest = divmod(delta, timedelta(hours=1))
1935 minutes = rest // timedelta(minutes=1)
1936 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
1937
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001938timezone.utc = timezone._create(timedelta(0))
1939timezone.min = timezone._create(timezone._minoffset)
1940timezone.max = timezone._create(timezone._maxoffset)
Alexander Belopolskya4415142012-06-08 12:33:09 -04001941_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001942
Victor Stinner765531d2013-03-26 01:11:54 +01001943# Some time zone algebra. For a datetime x, let
1944# x.n = x stripped of its timezone -- its naive time.
1945# x.o = x.utcoffset(), and assuming that doesn't raise an exception or
1946# return None
1947# x.d = x.dst(), and assuming that doesn't raise an exception or
1948# return None
1949# x.s = x's standard offset, x.o - x.d
1950#
1951# Now some derived rules, where k is a duration (timedelta).
1952#
1953# 1. x.o = x.s + x.d
1954# This follows from the definition of x.s.
1955#
1956# 2. If x and y have the same tzinfo member, x.s = y.s.
1957# This is actually a requirement, an assumption we need to make about
1958# sane tzinfo classes.
1959#
1960# 3. The naive UTC time corresponding to x is x.n - x.o.
1961# This is again a requirement for a sane tzinfo class.
1962#
1963# 4. (x+k).s = x.s
1964# This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
1965#
1966# 5. (x+k).n = x.n + k
1967# Again follows from how arithmetic is defined.
1968#
1969# Now we can explain tz.fromutc(x). Let's assume it's an interesting case
1970# (meaning that the various tzinfo methods exist, and don't blow up or return
1971# None when called).
1972#
1973# The function wants to return a datetime y with timezone tz, equivalent to x.
1974# x is already in UTC.
1975#
1976# By #3, we want
1977#
1978# y.n - y.o = x.n [1]
1979#
1980# The algorithm starts by attaching tz to x.n, and calling that y. So
1981# x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
1982# becomes true; in effect, we want to solve [2] for k:
1983#
1984# (y+k).n - (y+k).o = x.n [2]
1985#
1986# By #1, this is the same as
1987#
1988# (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
1989#
1990# By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
1991# Substituting that into [3],
1992#
1993# x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
1994# k - (y+k).s - (y+k).d = 0; rearranging,
1995# k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
1996# k = y.s - (y+k).d
1997#
1998# On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
1999# approximate k by ignoring the (y+k).d term at first. Note that k can't be
2000# very large, since all offset-returning methods return a duration of magnitude
2001# less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
2002# be 0, so ignoring it has no consequence then.
2003#
2004# In any case, the new value is
2005#
2006# z = y + y.s [4]
2007#
2008# It's helpful to step back at look at [4] from a higher level: it's simply
2009# mapping from UTC to tz's standard time.
2010#
2011# At this point, if
2012#
2013# z.n - z.o = x.n [5]
2014#
2015# we have an equivalent time, and are almost done. The insecurity here is
2016# at the start of daylight time. Picture US Eastern for concreteness. The wall
2017# time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
2018# sense then. The docs ask that an Eastern tzinfo class consider such a time to
2019# be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
2020# on the day DST starts. We want to return the 1:MM EST spelling because that's
2021# the only spelling that makes sense on the local wall clock.
2022#
2023# In fact, if [5] holds at this point, we do have the standard-time spelling,
2024# but that takes a bit of proof. We first prove a stronger result. What's the
2025# difference between the LHS and RHS of [5]? Let
2026#
2027# diff = x.n - (z.n - z.o) [6]
2028#
2029# Now
2030# z.n = by [4]
2031# (y + y.s).n = by #5
2032# y.n + y.s = since y.n = x.n
2033# x.n + y.s = since z and y are have the same tzinfo member,
2034# y.s = z.s by #2
2035# x.n + z.s
2036#
2037# Plugging that back into [6] gives
2038#
2039# diff =
2040# x.n - ((x.n + z.s) - z.o) = expanding
2041# x.n - x.n - z.s + z.o = cancelling
2042# - z.s + z.o = by #2
2043# z.d
2044#
2045# So diff = z.d.
2046#
2047# If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
2048# spelling we wanted in the endcase described above. We're done. Contrarily,
2049# if z.d = 0, then we have a UTC equivalent, and are also done.
2050#
2051# If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
2052# add to z (in effect, z is in tz's standard time, and we need to shift the
2053# local clock into tz's daylight time).
2054#
2055# Let
2056#
2057# z' = z + z.d = z + diff [7]
2058#
2059# and we can again ask whether
2060#
2061# z'.n - z'.o = x.n [8]
2062#
2063# If so, we're done. If not, the tzinfo class is insane, according to the
2064# assumptions we've made. This also requires a bit of proof. As before, let's
2065# compute the difference between the LHS and RHS of [8] (and skipping some of
2066# the justifications for the kinds of substitutions we've done several times
2067# already):
2068#
2069# diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
2070# x.n - (z.n + diff - z'.o) = replacing diff via [6]
2071# x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
2072# x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
2073# - z.n + z.n - z.o + z'.o = cancel z.n
2074# - z.o + z'.o = #1 twice
2075# -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
2076# z'.d - z.d
2077#
2078# So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
2079# we've found the UTC-equivalent so are done. In fact, we stop with [7] and
2080# return z', not bothering to compute z'.d.
2081#
2082# How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
2083# a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
2084# would have to change the result dst() returns: we start in DST, and moving
2085# a little further into it takes us out of DST.
2086#
2087# There isn't a sane case where this can happen. The closest it gets is at
2088# the end of DST, where there's an hour in UTC with no spelling in a hybrid
2089# tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
2090# that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
2091# UTC) because the docs insist on that, but 0:MM is taken as being in daylight
2092# time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
2093# clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
2094# standard time. Since that's what the local clock *does*, we want to map both
2095# UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
2096# in local time, but so it goes -- it's the way the local clock works.
2097#
2098# When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
2099# so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
2100# z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
2101# (correctly) concludes that z' is not UTC-equivalent to x.
2102#
2103# Because we know z.d said z was in daylight time (else [5] would have held and
2104# we would have stopped then), and we know z.d != z'.d (else [8] would have held
2105# and we have stopped then), and there are only 2 possible values dst() can
2106# return in Eastern, it follows that z'.d must be 0 (which it is in the example,
2107# but the reasoning doesn't depend on the example -- it depends on there being
2108# two possible dst() outcomes, one zero and the other non-zero). Therefore
2109# z' must be in standard time, and is the spelling we want in this case.
2110#
2111# Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
2112# concerned (because it takes z' as being in standard time rather than the
2113# daylight time we intend here), but returning it gives the real-life "local
2114# clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
2115# tz.
2116#
2117# When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
2118# the 1:MM standard time spelling we want.
2119#
2120# So how can this break? One of the assumptions must be violated. Two
2121# possibilities:
2122#
2123# 1) [2] effectively says that y.s is invariant across all y belong to a given
2124# time zone. This isn't true if, for political reasons or continental drift,
2125# a region decides to change its base offset from UTC.
2126#
2127# 2) There may be versions of "double daylight" time where the tail end of
2128# the analysis gives up a step too early. I haven't thought about that
2129# enough to say.
2130#
2131# In any case, it's clear that the default fromutc() is strong enough to handle
2132# "almost all" time zones: so long as the standard offset is invariant, it
2133# doesn't matter if daylight time transition points change from year to year, or
2134# if daylight time is skipped in some years; it doesn't matter how large or
2135# small dst() may get within its bounds; and it doesn't even matter if some
2136# perverse time zone returns a negative dst()). So a breaking case must be
2137# pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002138
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002139try:
2140 from _datetime import *
Brett Cannoncd171c82013-07-04 17:43:24 -04002141except ImportError:
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002142 pass
2143else:
2144 # Clean up unused names
2145 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH,
2146 _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES,
2147 _build_struct_time, _call_tzinfo_method, _check_date_fields,
2148 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2149 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2150 _days_before_year, _days_in_month, _format_time, _is_leap,
2151 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2152 _wrap_strftime, _ymd2ord)
Alexander Belopolskya5658742010-07-23 20:03:53 +00002153 # XXX Since import * above excludes names that start with _,
2154 # docstring does not get overwritten. In the future, it may be
2155 # appropriate to maintain a single module level docstring and
2156 # remove the following line.
2157 from _datetime import __doc__