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