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