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