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