blob: 4c442f2139022a0c180ee2e616674252bd4b12a0 [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__
649 __cmp__, __hash__
650 __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
779 # Standard conversions, __cmp__, __hash__ (and helpers)
780
781 def timetuple(self):
782 "Return local time tuple compatible with time.localtime()."
783 return _build_struct_time(self._year, self._month, self._day,
784 0, 0, 0, -1)
785
786 def toordinal(self):
787 """Return proleptic Gregorian ordinal for the year, month and day.
788
789 January 1 of year 1 is day 1. Only the year, month and day values
790 contribute to the result.
791 """
792 return _ymd2ord(self._year, self._month, self._day)
793
794 def replace(self, year=None, month=None, day=None):
795 """Return a new date with new values for the specified fields."""
796 if year is None:
797 year = self._year
798 if month is None:
799 month = self._month
800 if day is None:
801 day = self._day
802 _check_date_fields(year, month, day)
803 return date(year, month, day)
804
805 # Comparisons of date objects with other.
806
807 def __eq__(self, other):
808 if isinstance(other, date):
809 return self._cmp(other) == 0
810 return NotImplemented
811
812 def __ne__(self, other):
813 if isinstance(other, date):
814 return self._cmp(other) != 0
815 return NotImplemented
816
817 def __le__(self, other):
818 if isinstance(other, date):
819 return self._cmp(other) <= 0
820 return NotImplemented
821
822 def __lt__(self, other):
823 if isinstance(other, date):
824 return self._cmp(other) < 0
825 return NotImplemented
826
827 def __ge__(self, other):
828 if isinstance(other, date):
829 return self._cmp(other) >= 0
830 return NotImplemented
831
832 def __gt__(self, other):
833 if isinstance(other, date):
834 return self._cmp(other) > 0
835 return NotImplemented
836
837 def _cmp(self, other):
838 assert isinstance(other, date)
839 y, m, d = self._year, self._month, self._day
840 y2, m2, d2 = other._year, other._month, other._day
841 return _cmp((y, m, d), (y2, m2, d2))
842
843 def __hash__(self):
844 "Hash."
845 return hash(self._getstate())
846
847 # Computations
848
849 def __add__(self, other):
850 "Add a date to a timedelta."
851 if isinstance(other, timedelta):
852 o = self.toordinal() + other.days
853 if 0 < o <= _MAXORDINAL:
854 return date.fromordinal(o)
855 raise OverflowError("result out of range")
856 return NotImplemented
857
858 __radd__ = __add__
859
860 def __sub__(self, other):
861 """Subtract two dates, or a date and a timedelta."""
862 if isinstance(other, timedelta):
863 return self + timedelta(-other.days)
864 if isinstance(other, date):
865 days1 = self.toordinal()
866 days2 = other.toordinal()
867 return timedelta(days1 - days2)
868 return NotImplemented
869
870 def weekday(self):
871 "Return day of the week, where Monday == 0 ... Sunday == 6."
872 return (self.toordinal() + 6) % 7
873
874 # Day-of-the-week and week-of-the-year, according to ISO
875
876 def isoweekday(self):
877 "Return day of the week, where Monday == 1 ... Sunday == 7."
878 # 1-Jan-0001 is a Monday
879 return self.toordinal() % 7 or 7
880
881 def isocalendar(self):
882 """Return a 3-tuple containing ISO year, week number, and weekday.
883
884 The first ISO week of the year is the (Mon-Sun) week
885 containing the year's first Thursday; everything else derives
886 from that.
887
888 The first week is 1; Monday is 1 ... Sunday is 7.
889
890 ISO calendar algorithm taken from
891 http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
892 """
893 year = self._year
894 week1monday = _isoweek1monday(year)
895 today = _ymd2ord(self._year, self._month, self._day)
896 # Internally, week and day have origin 0
897 week, day = divmod(today - week1monday, 7)
898 if week < 0:
899 year -= 1
900 week1monday = _isoweek1monday(year)
901 week, day = divmod(today - week1monday, 7)
902 elif week >= 52:
903 if today >= _isoweek1monday(year+1):
904 year += 1
905 week = 0
906 return year, week+1, day+1
907
908 # Pickle support.
909
910 def _getstate(self):
911 yhi, ylo = divmod(self._year, 256)
912 return bytes([yhi, ylo, self._month, self._day]),
913
914 def __setstate(self, string):
915 if len(string) != 4 or not (1 <= string[2] <= 12):
916 raise TypeError("not enough arguments")
917 yhi, ylo, self._month, self._day = string
918 self._year = yhi * 256 + ylo
919
920 def __reduce__(self):
921 return (self.__class__, self._getstate())
922
923_date_class = date # so functions w/ args named "date" can get at the class
924
925date.min = date(1, 1, 1)
926date.max = date(9999, 12, 31)
927date.resolution = timedelta(days=1)
928
929class tzinfo:
930 """Abstract base class for time zone info classes.
931
932 Subclasses must override the name(), utcoffset() and dst() methods.
933 """
934 __slots__ = ()
935 def tzname(self, dt):
936 "datetime -> string name of time zone."
937 raise NotImplementedError("tzinfo subclass must override tzname()")
938
939 def utcoffset(self, dt):
940 "datetime -> minutes east of UTC (negative for west of UTC)"
941 raise NotImplementedError("tzinfo subclass must override utcoffset()")
942
943 def dst(self, dt):
944 """datetime -> DST offset in minutes east of UTC.
945
946 Return 0 if DST not in effect. utcoffset() must include the DST
947 offset.
948 """
949 raise NotImplementedError("tzinfo subclass must override dst()")
950
951 def fromutc(self, dt):
952 "datetime in UTC -> datetime in local time."
953
954 if not isinstance(dt, datetime):
955 raise TypeError("fromutc() requires a datetime argument")
956 if dt.tzinfo is not self:
957 raise ValueError("dt.tzinfo is not self")
958
959 dtoff = dt.utcoffset()
960 if dtoff is None:
961 raise ValueError("fromutc() requires a non-None utcoffset() "
962 "result")
963
964 # See the long comment block at the end of this file for an
965 # explanation of this algorithm.
966 dtdst = dt.dst()
967 if dtdst is None:
968 raise ValueError("fromutc() requires a non-None dst() result")
969 delta = dtoff - dtdst
970 if delta:
971 dt += delta
972 dtdst = dt.dst()
973 if dtdst is None:
974 raise ValueError("fromutc(): dt.dst gave inconsistent "
975 "results; cannot convert")
976 return dt + dtdst
977
978 # Pickle support.
979
980 def __reduce__(self):
981 getinitargs = getattr(self, "__getinitargs__", None)
982 if getinitargs:
983 args = getinitargs()
984 else:
985 args = ()
986 getstate = getattr(self, "__getstate__", None)
987 if getstate:
988 state = getstate()
989 else:
990 state = getattr(self, "__dict__", None) or None
991 if state is None:
992 return (self.__class__, args)
993 else:
994 return (self.__class__, args, state)
995
996_tzinfo_class = tzinfo
997
998class time:
999 """Time with time zone.
1000
1001 Constructors:
1002
1003 __new__()
1004
1005 Operators:
1006
1007 __repr__, __str__
1008 __cmp__, __hash__
1009
1010 Methods:
1011
1012 strftime()
1013 isoformat()
1014 utcoffset()
1015 tzname()
1016 dst()
1017
1018 Properties (readonly):
1019 hour, minute, second, microsecond, tzinfo
1020 """
1021
1022 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
1023 """Constructor.
1024
1025 Arguments:
1026
1027 hour, minute (required)
1028 second, microsecond (default to zero)
1029 tzinfo (default to None)
1030 """
1031 self = object.__new__(cls)
1032 if isinstance(hour, bytes) and len(hour) == 6:
1033 # Pickle support
1034 self.__setstate(hour, minute or None)
1035 return self
1036 _check_tzinfo_arg(tzinfo)
1037 _check_time_fields(hour, minute, second, microsecond)
1038 self._hour = hour
1039 self._minute = minute
1040 self._second = second
1041 self._microsecond = microsecond
1042 self._tzinfo = tzinfo
1043 return self
1044
1045 # Read-only field accessors
1046 @property
1047 def hour(self):
1048 """hour (0-23)"""
1049 return self._hour
1050
1051 @property
1052 def minute(self):
1053 """minute (0-59)"""
1054 return self._minute
1055
1056 @property
1057 def second(self):
1058 """second (0-59)"""
1059 return self._second
1060
1061 @property
1062 def microsecond(self):
1063 """microsecond (0-999999)"""
1064 return self._microsecond
1065
1066 @property
1067 def tzinfo(self):
1068 """timezone info object"""
1069 return self._tzinfo
1070
1071 # Standard conversions, __hash__ (and helpers)
1072
1073 # Comparisons of time objects with other.
1074
1075 def __eq__(self, other):
1076 if isinstance(other, time):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001077 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001078 else:
1079 return False
1080
1081 def __ne__(self, other):
1082 if isinstance(other, time):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001083 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001084 else:
1085 return True
1086
1087 def __le__(self, other):
1088 if isinstance(other, time):
1089 return self._cmp(other) <= 0
1090 else:
1091 _cmperror(self, other)
1092
1093 def __lt__(self, other):
1094 if isinstance(other, time):
1095 return self._cmp(other) < 0
1096 else:
1097 _cmperror(self, other)
1098
1099 def __ge__(self, other):
1100 if isinstance(other, time):
1101 return self._cmp(other) >= 0
1102 else:
1103 _cmperror(self, other)
1104
1105 def __gt__(self, other):
1106 if isinstance(other, time):
1107 return self._cmp(other) > 0
1108 else:
1109 _cmperror(self, other)
1110
Alexander Belopolsky08313822012-06-15 20:19:47 -04001111 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001112 assert isinstance(other, time)
1113 mytz = self._tzinfo
1114 ottz = other._tzinfo
1115 myoff = otoff = None
1116
1117 if mytz is ottz:
1118 base_compare = True
1119 else:
1120 myoff = self.utcoffset()
1121 otoff = other.utcoffset()
1122 base_compare = myoff == otoff
1123
1124 if base_compare:
1125 return _cmp((self._hour, self._minute, self._second,
1126 self._microsecond),
1127 (other._hour, other._minute, other._second,
1128 other._microsecond))
1129 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001130 if allow_mixed:
1131 return 2 # arbitrary non-zero value
1132 else:
1133 raise TypeError("cannot compare naive and aware times")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001134 myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
1135 othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
1136 return _cmp((myhhmm, self._second, self._microsecond),
1137 (othhmm, other._second, other._microsecond))
1138
1139 def __hash__(self):
1140 """Hash."""
1141 tzoff = self.utcoffset()
1142 if not tzoff: # zero or None
1143 return hash(self._getstate()[0])
1144 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
1145 timedelta(hours=1))
1146 assert not m % timedelta(minutes=1), "whole minute"
1147 m //= timedelta(minutes=1)
1148 if 0 <= h < 24:
1149 return hash(time(h, m, self.second, self.microsecond))
1150 return hash((h, m, self.second, self.microsecond))
1151
1152 # Conversion to string
1153
1154 def _tzstr(self, sep=":"):
1155 """Return formatted timezone offset (+xx:xx) or None."""
1156 off = self.utcoffset()
1157 if off is not None:
1158 if off.days < 0:
1159 sign = "-"
1160 off = -off
1161 else:
1162 sign = "+"
1163 hh, mm = divmod(off, timedelta(hours=1))
1164 assert not mm % timedelta(minutes=1), "whole minute"
1165 mm //= timedelta(minutes=1)
1166 assert 0 <= hh < 24
1167 off = "%s%02d%s%02d" % (sign, hh, sep, mm)
1168 return off
1169
1170 def __repr__(self):
1171 """Convert to formal string, for repr()."""
1172 if self._microsecond != 0:
1173 s = ", %d, %d" % (self._second, self._microsecond)
1174 elif self._second != 0:
1175 s = ", %d" % self._second
1176 else:
1177 s = ""
1178 s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__,
1179 self._hour, self._minute, s)
1180 if self._tzinfo is not None:
1181 assert s[-1:] == ")"
1182 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1183 return s
1184
1185 def isoformat(self):
1186 """Return the time formatted according to ISO.
1187
1188 This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if
1189 self.microsecond == 0.
1190 """
1191 s = _format_time(self._hour, self._minute, self._second,
1192 self._microsecond)
1193 tz = self._tzstr()
1194 if tz:
1195 s += tz
1196 return s
1197
1198 __str__ = isoformat
1199
1200 def strftime(self, fmt):
1201 """Format using strftime(). The date part of the timestamp passed
1202 to underlying strftime should not be used.
1203 """
Alexander Belopolskyb8bb4662011-01-08 00:13:34 +00001204 # The year must be >= 1000 else Python's strftime implementation
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001205 # can raise a bogus exception.
1206 timetuple = (1900, 1, 1,
1207 self._hour, self._minute, self._second,
1208 0, 1, -1)
1209 return _wrap_strftime(self, fmt, timetuple)
1210
1211 def __format__(self, fmt):
1212 if len(fmt) != 0:
1213 return self.strftime(fmt)
1214 return str(self)
1215
1216 # Timezone functions
1217
1218 def utcoffset(self):
1219 """Return the timezone offset in minutes east of UTC (negative west of
1220 UTC)."""
1221 if self._tzinfo is None:
1222 return None
1223 offset = self._tzinfo.utcoffset(None)
1224 _check_utc_offset("utcoffset", offset)
1225 return offset
1226
1227 def tzname(self):
1228 """Return the timezone name.
1229
1230 Note that the name is 100% informational -- there's no requirement that
1231 it mean anything in particular. For example, "GMT", "UTC", "-500",
1232 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1233 """
1234 if self._tzinfo is None:
1235 return None
1236 name = self._tzinfo.tzname(None)
1237 _check_tzname(name)
1238 return name
1239
1240 def dst(self):
1241 """Return 0 if DST is not in effect, or the DST offset (in minutes
1242 eastward) if DST is in effect.
1243
1244 This is purely informational; the DST offset has already been added to
1245 the UTC offset returned by utcoffset() if applicable, so there's no
1246 need to consult dst() unless you're interested in displaying the DST
1247 info.
1248 """
1249 if self._tzinfo is None:
1250 return None
1251 offset = self._tzinfo.dst(None)
1252 _check_utc_offset("dst", offset)
1253 return offset
1254
1255 def replace(self, hour=None, minute=None, second=None, microsecond=None,
1256 tzinfo=True):
1257 """Return a new time with new values for the specified fields."""
1258 if hour is None:
1259 hour = self.hour
1260 if minute is None:
1261 minute = self.minute
1262 if second is None:
1263 second = self.second
1264 if microsecond is None:
1265 microsecond = self.microsecond
1266 if tzinfo is True:
1267 tzinfo = self.tzinfo
1268 _check_time_fields(hour, minute, second, microsecond)
1269 _check_tzinfo_arg(tzinfo)
1270 return time(hour, minute, second, microsecond, tzinfo)
1271
1272 def __bool__(self):
1273 if self.second or self.microsecond:
1274 return True
1275 offset = self.utcoffset() or timedelta(0)
1276 return timedelta(hours=self.hour, minutes=self.minute) != offset
1277
1278 # Pickle support.
1279
1280 def _getstate(self):
1281 us2, us3 = divmod(self._microsecond, 256)
1282 us1, us2 = divmod(us2, 256)
1283 basestate = bytes([self._hour, self._minute, self._second,
1284 us1, us2, us3])
1285 if self._tzinfo is None:
1286 return (basestate,)
1287 else:
1288 return (basestate, self._tzinfo)
1289
1290 def __setstate(self, string, tzinfo):
1291 if len(string) != 6 or string[0] >= 24:
1292 raise TypeError("an integer is required")
1293 (self._hour, self._minute, self._second,
1294 us1, us2, us3) = string
1295 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1296 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1297 self._tzinfo = tzinfo
1298 else:
1299 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1300
1301 def __reduce__(self):
1302 return (time, self._getstate())
1303
1304_time_class = time # so functions w/ args named "time" can get at the class
1305
1306time.min = time(0, 0, 0)
1307time.max = time(23, 59, 59, 999999)
1308time.resolution = timedelta(microseconds=1)
1309
1310class datetime(date):
1311 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
1312
1313 The year, month and day arguments are required. tzinfo may be None, or an
Serhiy Storchaka95949422013-08-27 19:40:23 +03001314 instance of a tzinfo subclass. The remaining arguments may be ints.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001315 """
1316
1317 __slots__ = date.__slots__ + (
1318 '_hour', '_minute', '_second',
1319 '_microsecond', '_tzinfo')
1320 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1321 microsecond=0, tzinfo=None):
1322 if isinstance(year, bytes) and len(year) == 10:
1323 # Pickle support
1324 self = date.__new__(cls, year[:4])
1325 self.__setstate(year, month)
1326 return self
1327 _check_tzinfo_arg(tzinfo)
1328 _check_time_fields(hour, minute, second, microsecond)
1329 self = date.__new__(cls, year, month, day)
1330 self._hour = hour
1331 self._minute = minute
1332 self._second = second
1333 self._microsecond = microsecond
1334 self._tzinfo = tzinfo
1335 return self
1336
1337 # Read-only field accessors
1338 @property
1339 def hour(self):
1340 """hour (0-23)"""
1341 return self._hour
1342
1343 @property
1344 def minute(self):
1345 """minute (0-59)"""
1346 return self._minute
1347
1348 @property
1349 def second(self):
1350 """second (0-59)"""
1351 return self._second
1352
1353 @property
1354 def microsecond(self):
1355 """microsecond (0-999999)"""
1356 return self._microsecond
1357
1358 @property
1359 def tzinfo(self):
1360 """timezone info object"""
1361 return self._tzinfo
1362
1363 @classmethod
1364 def fromtimestamp(cls, t, tz=None):
1365 """Construct a datetime from a POSIX timestamp (like time.time()).
1366
1367 A timezone info object may be passed in as well.
1368 """
1369
1370 _check_tzinfo_arg(tz)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001371
1372 converter = _time.localtime if tz is None else _time.gmtime
1373
1374 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001375 us = int(frac * 1e6)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001376
1377 # If timestamp is less than one microsecond smaller than a
1378 # full second, us can be rounded up to 1000000. In this case,
1379 # roll over to seconds, otherwise, ValueError is raised
1380 # by the constructor.
1381 if us == 1000000:
1382 t += 1
1383 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001384 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001385 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1386 result = cls(y, m, d, hh, mm, ss, us, tz)
1387 if tz is not None:
1388 result = tz.fromutc(result)
1389 return result
1390
1391 @classmethod
1392 def utcfromtimestamp(cls, t):
1393 "Construct a UTC datetime from a POSIX timestamp (like time.time())."
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001394 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001395 us = int(frac * 1e6)
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001396
1397 # If timestamp is less than one microsecond smaller than a
1398 # full second, us can be rounded up to 1000000. In this case,
1399 # roll over to seconds, otherwise, ValueError is raised
1400 # by the constructor.
1401 if us == 1000000:
1402 t += 1
1403 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001404 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001405 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1406 return cls(y, m, d, hh, mm, ss, us)
1407
1408 # XXX This is supposed to do better than we *can* do by using time.time(),
1409 # XXX if the platform supports a more accurate way. The C implementation
1410 # XXX uses gettimeofday on platforms that have it, but that isn't
1411 # XXX available from Python. So now() may return different results
1412 # XXX across the implementations.
1413 @classmethod
1414 def now(cls, tz=None):
1415 "Construct a datetime from time.time() and optional time zone info."
1416 t = _time.time()
1417 return cls.fromtimestamp(t, tz)
1418
1419 @classmethod
1420 def utcnow(cls):
1421 "Construct a UTC datetime from time.time()."
1422 t = _time.time()
1423 return cls.utcfromtimestamp(t)
1424
1425 @classmethod
1426 def combine(cls, date, time):
1427 "Construct a datetime from a given date and a given time."
1428 if not isinstance(date, _date_class):
1429 raise TypeError("date argument must be a date instance")
1430 if not isinstance(time, _time_class):
1431 raise TypeError("time argument must be a time instance")
1432 return cls(date.year, date.month, date.day,
1433 time.hour, time.minute, time.second, time.microsecond,
1434 time.tzinfo)
1435
1436 def timetuple(self):
1437 "Return local time tuple compatible with time.localtime()."
1438 dst = self.dst()
1439 if dst is None:
1440 dst = -1
1441 elif dst:
1442 dst = 1
1443 else:
1444 dst = 0
1445 return _build_struct_time(self.year, self.month, self.day,
1446 self.hour, self.minute, self.second,
1447 dst)
1448
Alexander Belopolskya4415142012-06-08 12:33:09 -04001449 def timestamp(self):
1450 "Return POSIX timestamp as float"
1451 if self._tzinfo is None:
1452 return _time.mktime((self.year, self.month, self.day,
1453 self.hour, self.minute, self.second,
1454 -1, -1, -1)) + self.microsecond / 1e6
1455 else:
1456 return (self - _EPOCH).total_seconds()
1457
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001458 def utctimetuple(self):
1459 "Return UTC time tuple compatible with time.gmtime()."
1460 offset = self.utcoffset()
1461 if offset:
1462 self -= offset
1463 y, m, d = self.year, self.month, self.day
1464 hh, mm, ss = self.hour, self.minute, self.second
1465 return _build_struct_time(y, m, d, hh, mm, ss, 0)
1466
1467 def date(self):
1468 "Return the date part."
1469 return date(self._year, self._month, self._day)
1470
1471 def time(self):
1472 "Return the time part, with tzinfo None."
1473 return time(self.hour, self.minute, self.second, self.microsecond)
1474
1475 def timetz(self):
1476 "Return the time part, with same tzinfo."
1477 return time(self.hour, self.minute, self.second, self.microsecond,
1478 self._tzinfo)
1479
1480 def replace(self, year=None, month=None, day=None, hour=None,
1481 minute=None, second=None, microsecond=None, tzinfo=True):
1482 """Return a new datetime with new values for the specified fields."""
1483 if year is None:
1484 year = self.year
1485 if month is None:
1486 month = self.month
1487 if day is None:
1488 day = self.day
1489 if hour is None:
1490 hour = self.hour
1491 if minute is None:
1492 minute = self.minute
1493 if second is None:
1494 second = self.second
1495 if microsecond is None:
1496 microsecond = self.microsecond
1497 if tzinfo is True:
1498 tzinfo = self.tzinfo
1499 _check_date_fields(year, month, day)
1500 _check_time_fields(hour, minute, second, microsecond)
1501 _check_tzinfo_arg(tzinfo)
1502 return datetime(year, month, day, hour, minute, second,
1503 microsecond, tzinfo)
1504
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001505 def astimezone(self, tz=None):
1506 if tz is None:
1507 if self.tzinfo is None:
1508 raise ValueError("astimezone() requires an aware datetime")
1509 ts = (self - _EPOCH) // timedelta(seconds=1)
1510 localtm = _time.localtime(ts)
1511 local = datetime(*localtm[:6])
1512 try:
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001513 # Extract TZ data if available
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001514 gmtoff = localtm.tm_gmtoff
1515 zone = localtm.tm_zone
1516 except AttributeError:
1517 # Compute UTC offset and compare with the value implied
1518 # by tm_isdst. If the values match, use the zone name
1519 # implied by tm_isdst.
1520 delta = local - datetime(*_time.gmtime(ts)[:6])
1521 dst = _time.daylight and localtm.tm_isdst > 0
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001522 gmtoff = -(_time.altzone if dst else _time.timezone)
1523 if delta == timedelta(seconds=gmtoff):
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001524 tz = timezone(delta, _time.tzname[dst])
1525 else:
1526 tz = timezone(delta)
1527 else:
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04001528 tz = timezone(timedelta(seconds=gmtoff), zone)
Alexander Belopolskyff493c92012-06-22 12:25:57 -04001529
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04001530 elif not isinstance(tz, tzinfo):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001531 raise TypeError("tz argument must be an instance of tzinfo")
1532
1533 mytz = self.tzinfo
1534 if mytz is None:
1535 raise ValueError("astimezone() requires an aware datetime")
1536
1537 if tz is mytz:
1538 return self
1539
1540 # Convert self to UTC, and attach the new time zone object.
1541 myoffset = self.utcoffset()
1542 if myoffset is None:
1543 raise ValueError("astimezone() requires an aware datetime")
1544 utc = (self - myoffset).replace(tzinfo=tz)
1545
1546 # Convert from UTC to tz's local time.
1547 return tz.fromutc(utc)
1548
1549 # Ways to produce a string.
1550
1551 def ctime(self):
1552 "Return ctime() style string."
1553 weekday = self.toordinal() % 7 or 7
1554 return "%s %s %2d %02d:%02d:%02d %04d" % (
1555 _DAYNAMES[weekday],
1556 _MONTHNAMES[self._month],
1557 self._day,
1558 self._hour, self._minute, self._second,
1559 self._year)
1560
1561 def isoformat(self, sep='T'):
1562 """Return the time formatted according to ISO.
1563
1564 This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if
1565 self.microsecond == 0.
1566
1567 If self.tzinfo is not None, the UTC offset is also attached, giving
1568 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.
1569
1570 Optional argument sep specifies the separator between date and
1571 time, default 'T'.
1572 """
1573 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day,
1574 sep) +
1575 _format_time(self._hour, self._minute, self._second,
1576 self._microsecond))
1577 off = self.utcoffset()
1578 if off is not None:
1579 if off.days < 0:
1580 sign = "-"
1581 off = -off
1582 else:
1583 sign = "+"
1584 hh, mm = divmod(off, timedelta(hours=1))
1585 assert not mm % timedelta(minutes=1), "whole minute"
1586 mm //= timedelta(minutes=1)
1587 s += "%s%02d:%02d" % (sign, hh, mm)
1588 return s
1589
1590 def __repr__(self):
1591 """Convert to formal string, for repr()."""
1592 L = [self._year, self._month, self._day, # These are never zero
1593 self._hour, self._minute, self._second, self._microsecond]
1594 if L[-1] == 0:
1595 del L[-1]
1596 if L[-1] == 0:
1597 del L[-1]
1598 s = ", ".join(map(str, L))
1599 s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
1600 if self._tzinfo is not None:
1601 assert s[-1:] == ")"
1602 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1603 return s
1604
1605 def __str__(self):
1606 "Convert to string, for str()."
1607 return self.isoformat(sep=' ')
1608
1609 @classmethod
1610 def strptime(cls, date_string, format):
1611 'string, format -> new datetime parsed from a string (like time.strptime()).'
1612 import _strptime
1613 return _strptime._strptime_datetime(cls, date_string, format)
1614
1615 def utcoffset(self):
1616 """Return the timezone offset in minutes east of UTC (negative west of
1617 UTC)."""
1618 if self._tzinfo is None:
1619 return None
1620 offset = self._tzinfo.utcoffset(self)
1621 _check_utc_offset("utcoffset", offset)
1622 return offset
1623
1624 def tzname(self):
1625 """Return the timezone name.
1626
1627 Note that the name is 100% informational -- there's no requirement that
1628 it mean anything in particular. For example, "GMT", "UTC", "-500",
1629 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1630 """
1631 name = _call_tzinfo_method(self._tzinfo, "tzname", self)
1632 _check_tzname(name)
1633 return name
1634
1635 def dst(self):
1636 """Return 0 if DST is not in effect, or the DST offset (in minutes
1637 eastward) if DST is in effect.
1638
1639 This is purely informational; the DST offset has already been added to
1640 the UTC offset returned by utcoffset() if applicable, so there's no
1641 need to consult dst() unless you're interested in displaying the DST
1642 info.
1643 """
1644 if self._tzinfo is None:
1645 return None
1646 offset = self._tzinfo.dst(self)
1647 _check_utc_offset("dst", offset)
1648 return offset
1649
1650 # Comparisons of datetime objects with other.
1651
1652 def __eq__(self, other):
1653 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001654 return self._cmp(other, allow_mixed=True) == 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001655 elif not isinstance(other, date):
1656 return NotImplemented
1657 else:
1658 return False
1659
1660 def __ne__(self, other):
1661 if isinstance(other, datetime):
Alexander Belopolsky08313822012-06-15 20:19:47 -04001662 return self._cmp(other, allow_mixed=True) != 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001663 elif not isinstance(other, date):
1664 return NotImplemented
1665 else:
1666 return True
1667
1668 def __le__(self, other):
1669 if isinstance(other, datetime):
1670 return self._cmp(other) <= 0
1671 elif not isinstance(other, date):
1672 return NotImplemented
1673 else:
1674 _cmperror(self, other)
1675
1676 def __lt__(self, other):
1677 if isinstance(other, datetime):
1678 return self._cmp(other) < 0
1679 elif not isinstance(other, date):
1680 return NotImplemented
1681 else:
1682 _cmperror(self, other)
1683
1684 def __ge__(self, other):
1685 if isinstance(other, datetime):
1686 return self._cmp(other) >= 0
1687 elif not isinstance(other, date):
1688 return NotImplemented
1689 else:
1690 _cmperror(self, other)
1691
1692 def __gt__(self, other):
1693 if isinstance(other, datetime):
1694 return self._cmp(other) > 0
1695 elif not isinstance(other, date):
1696 return NotImplemented
1697 else:
1698 _cmperror(self, other)
1699
Alexander Belopolsky08313822012-06-15 20:19:47 -04001700 def _cmp(self, other, allow_mixed=False):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001701 assert isinstance(other, datetime)
1702 mytz = self._tzinfo
1703 ottz = other._tzinfo
1704 myoff = otoff = None
1705
1706 if mytz is ottz:
1707 base_compare = True
1708 else:
Alexander Belopolsky016ef552012-06-15 18:15:25 -04001709 myoff = self.utcoffset()
1710 otoff = other.utcoffset()
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001711 base_compare = myoff == otoff
1712
1713 if base_compare:
1714 return _cmp((self._year, self._month, self._day,
1715 self._hour, self._minute, self._second,
1716 self._microsecond),
1717 (other._year, other._month, other._day,
1718 other._hour, other._minute, other._second,
1719 other._microsecond))
1720 if myoff is None or otoff is None:
Alexander Belopolsky08313822012-06-15 20:19:47 -04001721 if allow_mixed:
1722 return 2 # arbitrary non-zero value
1723 else:
1724 raise TypeError("cannot compare naive and aware datetimes")
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001725 # XXX What follows could be done more efficiently...
1726 diff = self - other # this will take offsets into account
1727 if diff.days < 0:
1728 return -1
1729 return diff and 1 or 0
1730
1731 def __add__(self, other):
1732 "Add a datetime and a timedelta."
1733 if not isinstance(other, timedelta):
1734 return NotImplemented
1735 delta = timedelta(self.toordinal(),
1736 hours=self._hour,
1737 minutes=self._minute,
1738 seconds=self._second,
1739 microseconds=self._microsecond)
1740 delta += other
1741 hour, rem = divmod(delta.seconds, 3600)
1742 minute, second = divmod(rem, 60)
1743 if 0 < delta.days <= _MAXORDINAL:
1744 return datetime.combine(date.fromordinal(delta.days),
1745 time(hour, minute, second,
1746 delta.microseconds,
1747 tzinfo=self._tzinfo))
1748 raise OverflowError("result out of range")
1749
1750 __radd__ = __add__
1751
1752 def __sub__(self, other):
1753 "Subtract two datetimes, or a datetime and a timedelta."
1754 if not isinstance(other, datetime):
1755 if isinstance(other, timedelta):
1756 return self + -other
1757 return NotImplemented
1758
1759 days1 = self.toordinal()
1760 days2 = other.toordinal()
1761 secs1 = self._second + self._minute * 60 + self._hour * 3600
1762 secs2 = other._second + other._minute * 60 + other._hour * 3600
1763 base = timedelta(days1 - days2,
1764 secs1 - secs2,
1765 self._microsecond - other._microsecond)
1766 if self._tzinfo is other._tzinfo:
1767 return base
1768 myoff = self.utcoffset()
1769 otoff = other.utcoffset()
1770 if myoff == otoff:
1771 return base
1772 if myoff is None or otoff is None:
1773 raise TypeError("cannot mix naive and timezone-aware time")
1774 return base + otoff - myoff
1775
1776 def __hash__(self):
1777 tzoff = self.utcoffset()
1778 if tzoff is None:
1779 return hash(self._getstate()[0])
1780 days = _ymd2ord(self.year, self.month, self.day)
1781 seconds = self.hour * 3600 + self.minute * 60 + self.second
1782 return hash(timedelta(days, seconds, self.microsecond) - tzoff)
1783
1784 # Pickle support.
1785
1786 def _getstate(self):
1787 yhi, ylo = divmod(self._year, 256)
1788 us2, us3 = divmod(self._microsecond, 256)
1789 us1, us2 = divmod(us2, 256)
1790 basestate = bytes([yhi, ylo, self._month, self._day,
1791 self._hour, self._minute, self._second,
1792 us1, us2, us3])
1793 if self._tzinfo is None:
1794 return (basestate,)
1795 else:
1796 return (basestate, self._tzinfo)
1797
1798 def __setstate(self, string, tzinfo):
1799 (yhi, ylo, self._month, self._day, self._hour,
1800 self._minute, self._second, us1, us2, us3) = string
1801 self._year = yhi * 256 + ylo
1802 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1803 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1804 self._tzinfo = tzinfo
1805 else:
1806 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1807
1808 def __reduce__(self):
1809 return (self.__class__, self._getstate())
1810
1811
1812datetime.min = datetime(1, 1, 1)
1813datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
1814datetime.resolution = timedelta(microseconds=1)
1815
1816
1817def _isoweek1monday(year):
1818 # Helper to calculate the day number of the Monday starting week 1
1819 # XXX This could be done more efficiently
1820 THURSDAY = 3
1821 firstday = _ymd2ord(year, 1, 1)
1822 firstweekday = (firstday + 6) % 7 # See weekday() above
1823 week1monday = firstday - firstweekday
1824 if firstweekday > THURSDAY:
1825 week1monday += 7
1826 return week1monday
1827
1828class timezone(tzinfo):
1829 __slots__ = '_offset', '_name'
1830
1831 # Sentinel value to disallow None
1832 _Omitted = object()
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001833 def __new__(cls, offset, name=_Omitted):
1834 if not isinstance(offset, timedelta):
1835 raise TypeError("offset must be a timedelta")
1836 if name is cls._Omitted:
1837 if not offset:
1838 return cls.utc
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001839 name = None
1840 elif not isinstance(name, str):
1841 raise TypeError("name must be a string")
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001842 if not cls._minoffset <= offset <= cls._maxoffset:
1843 raise ValueError("offset must be a timedelta"
1844 " strictly between -timedelta(hours=24) and"
1845 " timedelta(hours=24).")
1846 if (offset.microseconds != 0 or
1847 offset.seconds % 60 != 0):
1848 raise ValueError("offset must be a timedelta"
1849 " representing a whole number of minutes")
1850 return cls._create(offset, name)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001851
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001852 @classmethod
1853 def _create(cls, offset, name=None):
1854 self = tzinfo.__new__(cls)
1855 self._offset = offset
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001856 self._name = name
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001857 return self
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001858
1859 def __getinitargs__(self):
1860 """pickle support"""
1861 if self._name is None:
1862 return (self._offset,)
1863 return (self._offset, self._name)
1864
1865 def __eq__(self, other):
Georg Brandl0085a242012-09-22 09:23:12 +02001866 if type(other) != timezone:
1867 return False
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001868 return self._offset == other._offset
1869
1870 def __hash__(self):
1871 return hash(self._offset)
1872
1873 def __repr__(self):
1874 """Convert to formal string, for repr().
1875
1876 >>> tz = timezone.utc
1877 >>> repr(tz)
1878 'datetime.timezone.utc'
1879 >>> tz = timezone(timedelta(hours=-5), 'EST')
1880 >>> repr(tz)
1881 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
1882 """
1883 if self is self.utc:
1884 return 'datetime.timezone.utc'
1885 if self._name is None:
1886 return "%s(%r)" % ('datetime.' + self.__class__.__name__,
1887 self._offset)
1888 return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__,
1889 self._offset, self._name)
1890
1891 def __str__(self):
1892 return self.tzname(None)
1893
1894 def utcoffset(self, dt):
1895 if isinstance(dt, datetime) or dt is None:
1896 return self._offset
1897 raise TypeError("utcoffset() argument must be a datetime instance"
1898 " or None")
1899
1900 def tzname(self, dt):
1901 if isinstance(dt, datetime) or dt is None:
1902 if self._name is None:
1903 return self._name_from_offset(self._offset)
1904 return self._name
1905 raise TypeError("tzname() argument must be a datetime instance"
1906 " or None")
1907
1908 def dst(self, dt):
1909 if isinstance(dt, datetime) or dt is None:
1910 return None
1911 raise TypeError("dst() argument must be a datetime instance"
1912 " or None")
1913
1914 def fromutc(self, dt):
1915 if isinstance(dt, datetime):
1916 if dt.tzinfo is not self:
1917 raise ValueError("fromutc: dt.tzinfo "
1918 "is not self")
1919 return dt + self._offset
1920 raise TypeError("fromutc() argument must be a datetime instance"
1921 " or None")
1922
1923 _maxoffset = timedelta(hours=23, minutes=59)
1924 _minoffset = -_maxoffset
1925
1926 @staticmethod
1927 def _name_from_offset(delta):
1928 if delta < timedelta(0):
1929 sign = '-'
1930 delta = -delta
1931 else:
1932 sign = '+'
1933 hours, rest = divmod(delta, timedelta(hours=1))
1934 minutes = rest // timedelta(minutes=1)
1935 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
1936
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001937timezone.utc = timezone._create(timedelta(0))
1938timezone.min = timezone._create(timezone._minoffset)
1939timezone.max = timezone._create(timezone._maxoffset)
Alexander Belopolskya4415142012-06-08 12:33:09 -04001940_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001941
Victor Stinner765531d2013-03-26 01:11:54 +01001942# Some time zone algebra. For a datetime x, let
1943# x.n = x stripped of its timezone -- its naive time.
1944# x.o = x.utcoffset(), and assuming that doesn't raise an exception or
1945# return None
1946# x.d = x.dst(), and assuming that doesn't raise an exception or
1947# return None
1948# x.s = x's standard offset, x.o - x.d
1949#
1950# Now some derived rules, where k is a duration (timedelta).
1951#
1952# 1. x.o = x.s + x.d
1953# This follows from the definition of x.s.
1954#
1955# 2. If x and y have the same tzinfo member, x.s = y.s.
1956# This is actually a requirement, an assumption we need to make about
1957# sane tzinfo classes.
1958#
1959# 3. The naive UTC time corresponding to x is x.n - x.o.
1960# This is again a requirement for a sane tzinfo class.
1961#
1962# 4. (x+k).s = x.s
1963# This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
1964#
1965# 5. (x+k).n = x.n + k
1966# Again follows from how arithmetic is defined.
1967#
1968# Now we can explain tz.fromutc(x). Let's assume it's an interesting case
1969# (meaning that the various tzinfo methods exist, and don't blow up or return
1970# None when called).
1971#
1972# The function wants to return a datetime y with timezone tz, equivalent to x.
1973# x is already in UTC.
1974#
1975# By #3, we want
1976#
1977# y.n - y.o = x.n [1]
1978#
1979# The algorithm starts by attaching tz to x.n, and calling that y. So
1980# x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
1981# becomes true; in effect, we want to solve [2] for k:
1982#
1983# (y+k).n - (y+k).o = x.n [2]
1984#
1985# By #1, this is the same as
1986#
1987# (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
1988#
1989# By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
1990# Substituting that into [3],
1991#
1992# x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
1993# k - (y+k).s - (y+k).d = 0; rearranging,
1994# k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
1995# k = y.s - (y+k).d
1996#
1997# On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
1998# approximate k by ignoring the (y+k).d term at first. Note that k can't be
1999# very large, since all offset-returning methods return a duration of magnitude
2000# less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
2001# be 0, so ignoring it has no consequence then.
2002#
2003# In any case, the new value is
2004#
2005# z = y + y.s [4]
2006#
2007# It's helpful to step back at look at [4] from a higher level: it's simply
2008# mapping from UTC to tz's standard time.
2009#
2010# At this point, if
2011#
2012# z.n - z.o = x.n [5]
2013#
2014# we have an equivalent time, and are almost done. The insecurity here is
2015# at the start of daylight time. Picture US Eastern for concreteness. The wall
2016# time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
2017# sense then. The docs ask that an Eastern tzinfo class consider such a time to
2018# be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
2019# on the day DST starts. We want to return the 1:MM EST spelling because that's
2020# the only spelling that makes sense on the local wall clock.
2021#
2022# In fact, if [5] holds at this point, we do have the standard-time spelling,
2023# but that takes a bit of proof. We first prove a stronger result. What's the
2024# difference between the LHS and RHS of [5]? Let
2025#
2026# diff = x.n - (z.n - z.o) [6]
2027#
2028# Now
2029# z.n = by [4]
2030# (y + y.s).n = by #5
2031# y.n + y.s = since y.n = x.n
2032# x.n + y.s = since z and y are have the same tzinfo member,
2033# y.s = z.s by #2
2034# x.n + z.s
2035#
2036# Plugging that back into [6] gives
2037#
2038# diff =
2039# x.n - ((x.n + z.s) - z.o) = expanding
2040# x.n - x.n - z.s + z.o = cancelling
2041# - z.s + z.o = by #2
2042# z.d
2043#
2044# So diff = z.d.
2045#
2046# If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
2047# spelling we wanted in the endcase described above. We're done. Contrarily,
2048# if z.d = 0, then we have a UTC equivalent, and are also done.
2049#
2050# If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
2051# add to z (in effect, z is in tz's standard time, and we need to shift the
2052# local clock into tz's daylight time).
2053#
2054# Let
2055#
2056# z' = z + z.d = z + diff [7]
2057#
2058# and we can again ask whether
2059#
2060# z'.n - z'.o = x.n [8]
2061#
2062# If so, we're done. If not, the tzinfo class is insane, according to the
2063# assumptions we've made. This also requires a bit of proof. As before, let's
2064# compute the difference between the LHS and RHS of [8] (and skipping some of
2065# the justifications for the kinds of substitutions we've done several times
2066# already):
2067#
2068# diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
2069# x.n - (z.n + diff - z'.o) = replacing diff via [6]
2070# x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
2071# x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
2072# - z.n + z.n - z.o + z'.o = cancel z.n
2073# - z.o + z'.o = #1 twice
2074# -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
2075# z'.d - z.d
2076#
2077# So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
2078# we've found the UTC-equivalent so are done. In fact, we stop with [7] and
2079# return z', not bothering to compute z'.d.
2080#
2081# How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
2082# a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
2083# would have to change the result dst() returns: we start in DST, and moving
2084# a little further into it takes us out of DST.
2085#
2086# There isn't a sane case where this can happen. The closest it gets is at
2087# the end of DST, where there's an hour in UTC with no spelling in a hybrid
2088# tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
2089# that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
2090# UTC) because the docs insist on that, but 0:MM is taken as being in daylight
2091# time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
2092# clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
2093# standard time. Since that's what the local clock *does*, we want to map both
2094# UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
2095# in local time, but so it goes -- it's the way the local clock works.
2096#
2097# When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
2098# so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
2099# z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
2100# (correctly) concludes that z' is not UTC-equivalent to x.
2101#
2102# Because we know z.d said z was in daylight time (else [5] would have held and
2103# we would have stopped then), and we know z.d != z'.d (else [8] would have held
2104# and we have stopped then), and there are only 2 possible values dst() can
2105# return in Eastern, it follows that z'.d must be 0 (which it is in the example,
2106# but the reasoning doesn't depend on the example -- it depends on there being
2107# two possible dst() outcomes, one zero and the other non-zero). Therefore
2108# z' must be in standard time, and is the spelling we want in this case.
2109#
2110# Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
2111# concerned (because it takes z' as being in standard time rather than the
2112# daylight time we intend here), but returning it gives the real-life "local
2113# clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
2114# tz.
2115#
2116# When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
2117# the 1:MM standard time spelling we want.
2118#
2119# So how can this break? One of the assumptions must be violated. Two
2120# possibilities:
2121#
2122# 1) [2] effectively says that y.s is invariant across all y belong to a given
2123# time zone. This isn't true if, for political reasons or continental drift,
2124# a region decides to change its base offset from UTC.
2125#
2126# 2) There may be versions of "double daylight" time where the tail end of
2127# the analysis gives up a step too early. I haven't thought about that
2128# enough to say.
2129#
2130# In any case, it's clear that the default fromutc() is strong enough to handle
2131# "almost all" time zones: so long as the standard offset is invariant, it
2132# doesn't matter if daylight time transition points change from year to year, or
2133# if daylight time is skipped in some years; it doesn't matter how large or
2134# small dst() may get within its bounds; and it doesn't even matter if some
2135# perverse time zone returns a negative dst()). So a breaking case must be
2136# pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002137
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002138try:
2139 from _datetime import *
Brett Cannoncd171c82013-07-04 17:43:24 -04002140except ImportError:
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002141 pass
2142else:
2143 # Clean up unused names
2144 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH,
2145 _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES,
2146 _build_struct_time, _call_tzinfo_method, _check_date_fields,
2147 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2148 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2149 _days_before_year, _days_in_month, _format_time, _is_leap,
2150 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2151 _wrap_strftime, _ymd2ord)
Alexander Belopolskya5658742010-07-23 20:03:53 +00002152 # XXX Since import * above excludes names that start with _,
2153 # docstring does not get overwritten. In the future, it may be
2154 # appropriate to maintain a single module level docstring and
2155 # remove the following line.
2156 from _datetime import __doc__