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