blob: 5d8d9b3dfcec235881186bc1e59a378aa81b6b75 [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):
1068 return self._cmp(other) == 0
1069 else:
1070 return False
1071
1072 def __ne__(self, other):
1073 if isinstance(other, time):
1074 return self._cmp(other) != 0
1075 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
1102 def _cmp(self, other):
1103 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:
1121 raise TypeError("cannot compare naive and aware times")
1122 myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
1123 othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
1124 return _cmp((myhhmm, self._second, self._microsecond),
1125 (othhmm, other._second, other._microsecond))
1126
1127 def __hash__(self):
1128 """Hash."""
1129 tzoff = self.utcoffset()
1130 if not tzoff: # zero or None
1131 return hash(self._getstate()[0])
1132 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
1133 timedelta(hours=1))
1134 assert not m % timedelta(minutes=1), "whole minute"
1135 m //= timedelta(minutes=1)
1136 if 0 <= h < 24:
1137 return hash(time(h, m, self.second, self.microsecond))
1138 return hash((h, m, self.second, self.microsecond))
1139
1140 # Conversion to string
1141
1142 def _tzstr(self, sep=":"):
1143 """Return formatted timezone offset (+xx:xx) or None."""
1144 off = self.utcoffset()
1145 if off is not None:
1146 if off.days < 0:
1147 sign = "-"
1148 off = -off
1149 else:
1150 sign = "+"
1151 hh, mm = divmod(off, timedelta(hours=1))
1152 assert not mm % timedelta(minutes=1), "whole minute"
1153 mm //= timedelta(minutes=1)
1154 assert 0 <= hh < 24
1155 off = "%s%02d%s%02d" % (sign, hh, sep, mm)
1156 return off
1157
1158 def __repr__(self):
1159 """Convert to formal string, for repr()."""
1160 if self._microsecond != 0:
1161 s = ", %d, %d" % (self._second, self._microsecond)
1162 elif self._second != 0:
1163 s = ", %d" % self._second
1164 else:
1165 s = ""
1166 s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__,
1167 self._hour, self._minute, s)
1168 if self._tzinfo is not None:
1169 assert s[-1:] == ")"
1170 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1171 return s
1172
1173 def isoformat(self):
1174 """Return the time formatted according to ISO.
1175
1176 This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if
1177 self.microsecond == 0.
1178 """
1179 s = _format_time(self._hour, self._minute, self._second,
1180 self._microsecond)
1181 tz = self._tzstr()
1182 if tz:
1183 s += tz
1184 return s
1185
1186 __str__ = isoformat
1187
1188 def strftime(self, fmt):
1189 """Format using strftime(). The date part of the timestamp passed
1190 to underlying strftime should not be used.
1191 """
Alexander Belopolskyb8bb4662011-01-08 00:13:34 +00001192 # The year must be >= 1000 else Python's strftime implementation
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001193 # can raise a bogus exception.
1194 timetuple = (1900, 1, 1,
1195 self._hour, self._minute, self._second,
1196 0, 1, -1)
1197 return _wrap_strftime(self, fmt, timetuple)
1198
1199 def __format__(self, fmt):
1200 if len(fmt) != 0:
1201 return self.strftime(fmt)
1202 return str(self)
1203
1204 # Timezone functions
1205
1206 def utcoffset(self):
1207 """Return the timezone offset in minutes east of UTC (negative west of
1208 UTC)."""
1209 if self._tzinfo is None:
1210 return None
1211 offset = self._tzinfo.utcoffset(None)
1212 _check_utc_offset("utcoffset", offset)
1213 return offset
1214
1215 def tzname(self):
1216 """Return the timezone name.
1217
1218 Note that the name is 100% informational -- there's no requirement that
1219 it mean anything in particular. For example, "GMT", "UTC", "-500",
1220 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1221 """
1222 if self._tzinfo is None:
1223 return None
1224 name = self._tzinfo.tzname(None)
1225 _check_tzname(name)
1226 return name
1227
1228 def dst(self):
1229 """Return 0 if DST is not in effect, or the DST offset (in minutes
1230 eastward) if DST is in effect.
1231
1232 This is purely informational; the DST offset has already been added to
1233 the UTC offset returned by utcoffset() if applicable, so there's no
1234 need to consult dst() unless you're interested in displaying the DST
1235 info.
1236 """
1237 if self._tzinfo is None:
1238 return None
1239 offset = self._tzinfo.dst(None)
1240 _check_utc_offset("dst", offset)
1241 return offset
1242
1243 def replace(self, hour=None, minute=None, second=None, microsecond=None,
1244 tzinfo=True):
1245 """Return a new time with new values for the specified fields."""
1246 if hour is None:
1247 hour = self.hour
1248 if minute is None:
1249 minute = self.minute
1250 if second is None:
1251 second = self.second
1252 if microsecond is None:
1253 microsecond = self.microsecond
1254 if tzinfo is True:
1255 tzinfo = self.tzinfo
1256 _check_time_fields(hour, minute, second, microsecond)
1257 _check_tzinfo_arg(tzinfo)
1258 return time(hour, minute, second, microsecond, tzinfo)
1259
1260 def __bool__(self):
1261 if self.second or self.microsecond:
1262 return True
1263 offset = self.utcoffset() or timedelta(0)
1264 return timedelta(hours=self.hour, minutes=self.minute) != offset
1265
1266 # Pickle support.
1267
1268 def _getstate(self):
1269 us2, us3 = divmod(self._microsecond, 256)
1270 us1, us2 = divmod(us2, 256)
1271 basestate = bytes([self._hour, self._minute, self._second,
1272 us1, us2, us3])
1273 if self._tzinfo is None:
1274 return (basestate,)
1275 else:
1276 return (basestate, self._tzinfo)
1277
1278 def __setstate(self, string, tzinfo):
1279 if len(string) != 6 or string[0] >= 24:
1280 raise TypeError("an integer is required")
1281 (self._hour, self._minute, self._second,
1282 us1, us2, us3) = string
1283 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1284 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1285 self._tzinfo = tzinfo
1286 else:
1287 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1288
1289 def __reduce__(self):
1290 return (time, self._getstate())
1291
1292_time_class = time # so functions w/ args named "time" can get at the class
1293
1294time.min = time(0, 0, 0)
1295time.max = time(23, 59, 59, 999999)
1296time.resolution = timedelta(microseconds=1)
1297
1298class datetime(date):
1299 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
1300
1301 The year, month and day arguments are required. tzinfo may be None, or an
1302 instance of a tzinfo subclass. The remaining arguments may be ints or longs.
1303 """
1304
1305 __slots__ = date.__slots__ + (
1306 '_hour', '_minute', '_second',
1307 '_microsecond', '_tzinfo')
1308 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1309 microsecond=0, tzinfo=None):
1310 if isinstance(year, bytes) and len(year) == 10:
1311 # Pickle support
1312 self = date.__new__(cls, year[:4])
1313 self.__setstate(year, month)
1314 return self
1315 _check_tzinfo_arg(tzinfo)
1316 _check_time_fields(hour, minute, second, microsecond)
1317 self = date.__new__(cls, year, month, day)
1318 self._hour = hour
1319 self._minute = minute
1320 self._second = second
1321 self._microsecond = microsecond
1322 self._tzinfo = tzinfo
1323 return self
1324
1325 # Read-only field accessors
1326 @property
1327 def hour(self):
1328 """hour (0-23)"""
1329 return self._hour
1330
1331 @property
1332 def minute(self):
1333 """minute (0-59)"""
1334 return self._minute
1335
1336 @property
1337 def second(self):
1338 """second (0-59)"""
1339 return self._second
1340
1341 @property
1342 def microsecond(self):
1343 """microsecond (0-999999)"""
1344 return self._microsecond
1345
1346 @property
1347 def tzinfo(self):
1348 """timezone info object"""
1349 return self._tzinfo
1350
1351 @classmethod
1352 def fromtimestamp(cls, t, tz=None):
1353 """Construct a datetime from a POSIX timestamp (like time.time()).
1354
1355 A timezone info object may be passed in as well.
1356 """
1357
1358 _check_tzinfo_arg(tz)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001359
1360 converter = _time.localtime if tz is None else _time.gmtime
1361
1362 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001363 us = int(frac * 1e6)
Alexander Belopolskyaeb03982010-07-26 02:36:41 +00001364
1365 # If timestamp is less than one microsecond smaller than a
1366 # full second, us can be rounded up to 1000000. In this case,
1367 # roll over to seconds, otherwise, ValueError is raised
1368 # by the constructor.
1369 if us == 1000000:
1370 t += 1
1371 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001372 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001373 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1374 result = cls(y, m, d, hh, mm, ss, us, tz)
1375 if tz is not None:
1376 result = tz.fromutc(result)
1377 return result
1378
1379 @classmethod
1380 def utcfromtimestamp(cls, t):
1381 "Construct a UTC datetime from a POSIX timestamp (like time.time())."
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001382 t, frac = divmod(t, 1.0)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001383 us = int(frac * 1e6)
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001384
1385 # If timestamp is less than one microsecond smaller than a
1386 # full second, us can be rounded up to 1000000. In this case,
1387 # roll over to seconds, otherwise, ValueError is raised
1388 # by the constructor.
1389 if us == 1000000:
1390 t += 1
1391 us = 0
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001392 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001393 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1394 return cls(y, m, d, hh, mm, ss, us)
1395
1396 # XXX This is supposed to do better than we *can* do by using time.time(),
1397 # XXX if the platform supports a more accurate way. The C implementation
1398 # XXX uses gettimeofday on platforms that have it, but that isn't
1399 # XXX available from Python. So now() may return different results
1400 # XXX across the implementations.
1401 @classmethod
1402 def now(cls, tz=None):
1403 "Construct a datetime from time.time() and optional time zone info."
1404 t = _time.time()
1405 return cls.fromtimestamp(t, tz)
1406
1407 @classmethod
1408 def utcnow(cls):
1409 "Construct a UTC datetime from time.time()."
1410 t = _time.time()
1411 return cls.utcfromtimestamp(t)
1412
1413 @classmethod
1414 def combine(cls, date, time):
1415 "Construct a datetime from a given date and a given time."
1416 if not isinstance(date, _date_class):
1417 raise TypeError("date argument must be a date instance")
1418 if not isinstance(time, _time_class):
1419 raise TypeError("time argument must be a time instance")
1420 return cls(date.year, date.month, date.day,
1421 time.hour, time.minute, time.second, time.microsecond,
1422 time.tzinfo)
1423
1424 def timetuple(self):
1425 "Return local time tuple compatible with time.localtime()."
1426 dst = self.dst()
1427 if dst is None:
1428 dst = -1
1429 elif dst:
1430 dst = 1
1431 else:
1432 dst = 0
1433 return _build_struct_time(self.year, self.month, self.day,
1434 self.hour, self.minute, self.second,
1435 dst)
1436
Alexander Belopolskya4415142012-06-08 12:33:09 -04001437 def timestamp(self):
1438 "Return POSIX timestamp as float"
1439 if self._tzinfo is None:
1440 return _time.mktime((self.year, self.month, self.day,
1441 self.hour, self.minute, self.second,
1442 -1, -1, -1)) + self.microsecond / 1e6
1443 else:
1444 return (self - _EPOCH).total_seconds()
1445
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001446 def utctimetuple(self):
1447 "Return UTC time tuple compatible with time.gmtime()."
1448 offset = self.utcoffset()
1449 if offset:
1450 self -= offset
1451 y, m, d = self.year, self.month, self.day
1452 hh, mm, ss = self.hour, self.minute, self.second
1453 return _build_struct_time(y, m, d, hh, mm, ss, 0)
1454
1455 def date(self):
1456 "Return the date part."
1457 return date(self._year, self._month, self._day)
1458
1459 def time(self):
1460 "Return the time part, with tzinfo None."
1461 return time(self.hour, self.minute, self.second, self.microsecond)
1462
1463 def timetz(self):
1464 "Return the time part, with same tzinfo."
1465 return time(self.hour, self.minute, self.second, self.microsecond,
1466 self._tzinfo)
1467
1468 def replace(self, year=None, month=None, day=None, hour=None,
1469 minute=None, second=None, microsecond=None, tzinfo=True):
1470 """Return a new datetime with new values for the specified fields."""
1471 if year is None:
1472 year = self.year
1473 if month is None:
1474 month = self.month
1475 if day is None:
1476 day = self.day
1477 if hour is None:
1478 hour = self.hour
1479 if minute is None:
1480 minute = self.minute
1481 if second is None:
1482 second = self.second
1483 if microsecond is None:
1484 microsecond = self.microsecond
1485 if tzinfo is True:
1486 tzinfo = self.tzinfo
1487 _check_date_fields(year, month, day)
1488 _check_time_fields(hour, minute, second, microsecond)
1489 _check_tzinfo_arg(tzinfo)
1490 return datetime(year, month, day, hour, minute, second,
1491 microsecond, tzinfo)
1492
1493 def astimezone(self, tz):
1494 if not isinstance(tz, tzinfo):
1495 raise TypeError("tz argument must be an instance of tzinfo")
1496
1497 mytz = self.tzinfo
1498 if mytz is None:
1499 raise ValueError("astimezone() requires an aware datetime")
1500
1501 if tz is mytz:
1502 return self
1503
1504 # Convert self to UTC, and attach the new time zone object.
1505 myoffset = self.utcoffset()
1506 if myoffset is None:
1507 raise ValueError("astimezone() requires an aware datetime")
1508 utc = (self - myoffset).replace(tzinfo=tz)
1509
1510 # Convert from UTC to tz's local time.
1511 return tz.fromutc(utc)
1512
1513 # Ways to produce a string.
1514
1515 def ctime(self):
1516 "Return ctime() style string."
1517 weekday = self.toordinal() % 7 or 7
1518 return "%s %s %2d %02d:%02d:%02d %04d" % (
1519 _DAYNAMES[weekday],
1520 _MONTHNAMES[self._month],
1521 self._day,
1522 self._hour, self._minute, self._second,
1523 self._year)
1524
1525 def isoformat(self, sep='T'):
1526 """Return the time formatted according to ISO.
1527
1528 This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if
1529 self.microsecond == 0.
1530
1531 If self.tzinfo is not None, the UTC offset is also attached, giving
1532 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.
1533
1534 Optional argument sep specifies the separator between date and
1535 time, default 'T'.
1536 """
1537 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day,
1538 sep) +
1539 _format_time(self._hour, self._minute, self._second,
1540 self._microsecond))
1541 off = self.utcoffset()
1542 if off is not None:
1543 if off.days < 0:
1544 sign = "-"
1545 off = -off
1546 else:
1547 sign = "+"
1548 hh, mm = divmod(off, timedelta(hours=1))
1549 assert not mm % timedelta(minutes=1), "whole minute"
1550 mm //= timedelta(minutes=1)
1551 s += "%s%02d:%02d" % (sign, hh, mm)
1552 return s
1553
1554 def __repr__(self):
1555 """Convert to formal string, for repr()."""
1556 L = [self._year, self._month, self._day, # These are never zero
1557 self._hour, self._minute, self._second, self._microsecond]
1558 if L[-1] == 0:
1559 del L[-1]
1560 if L[-1] == 0:
1561 del L[-1]
1562 s = ", ".join(map(str, L))
1563 s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
1564 if self._tzinfo is not None:
1565 assert s[-1:] == ")"
1566 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1567 return s
1568
1569 def __str__(self):
1570 "Convert to string, for str()."
1571 return self.isoformat(sep=' ')
1572
1573 @classmethod
1574 def strptime(cls, date_string, format):
1575 'string, format -> new datetime parsed from a string (like time.strptime()).'
1576 import _strptime
1577 return _strptime._strptime_datetime(cls, date_string, format)
1578
1579 def utcoffset(self):
1580 """Return the timezone offset in minutes east of UTC (negative west of
1581 UTC)."""
1582 if self._tzinfo is None:
1583 return None
1584 offset = self._tzinfo.utcoffset(self)
1585 _check_utc_offset("utcoffset", offset)
1586 return offset
1587
1588 def tzname(self):
1589 """Return the timezone name.
1590
1591 Note that the name is 100% informational -- there's no requirement that
1592 it mean anything in particular. For example, "GMT", "UTC", "-500",
1593 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1594 """
1595 name = _call_tzinfo_method(self._tzinfo, "tzname", self)
1596 _check_tzname(name)
1597 return name
1598
1599 def dst(self):
1600 """Return 0 if DST is not in effect, or the DST offset (in minutes
1601 eastward) if DST is in effect.
1602
1603 This is purely informational; the DST offset has already been added to
1604 the UTC offset returned by utcoffset() if applicable, so there's no
1605 need to consult dst() unless you're interested in displaying the DST
1606 info.
1607 """
1608 if self._tzinfo is None:
1609 return None
1610 offset = self._tzinfo.dst(self)
1611 _check_utc_offset("dst", offset)
1612 return offset
1613
1614 # Comparisons of datetime objects with other.
1615
1616 def __eq__(self, other):
1617 if isinstance(other, datetime):
1618 return self._cmp(other) == 0
1619 elif not isinstance(other, date):
1620 return NotImplemented
1621 else:
1622 return False
1623
1624 def __ne__(self, other):
1625 if isinstance(other, datetime):
1626 return self._cmp(other) != 0
1627 elif not isinstance(other, date):
1628 return NotImplemented
1629 else:
1630 return True
1631
1632 def __le__(self, other):
1633 if isinstance(other, datetime):
1634 return self._cmp(other) <= 0
1635 elif not isinstance(other, date):
1636 return NotImplemented
1637 else:
1638 _cmperror(self, other)
1639
1640 def __lt__(self, other):
1641 if isinstance(other, datetime):
1642 return self._cmp(other) < 0
1643 elif not isinstance(other, date):
1644 return NotImplemented
1645 else:
1646 _cmperror(self, other)
1647
1648 def __ge__(self, other):
1649 if isinstance(other, datetime):
1650 return self._cmp(other) >= 0
1651 elif not isinstance(other, date):
1652 return NotImplemented
1653 else:
1654 _cmperror(self, other)
1655
1656 def __gt__(self, other):
1657 if isinstance(other, datetime):
1658 return self._cmp(other) > 0
1659 elif not isinstance(other, date):
1660 return NotImplemented
1661 else:
1662 _cmperror(self, other)
1663
1664 def _cmp(self, other):
1665 assert isinstance(other, datetime)
1666 mytz = self._tzinfo
1667 ottz = other._tzinfo
1668 myoff = otoff = None
1669
1670 if mytz is ottz:
1671 base_compare = True
1672 else:
1673 if mytz is not None:
1674 myoff = self.utcoffset()
1675 if ottz is not None:
1676 otoff = other.utcoffset()
1677 base_compare = myoff == otoff
1678
1679 if base_compare:
1680 return _cmp((self._year, self._month, self._day,
1681 self._hour, self._minute, self._second,
1682 self._microsecond),
1683 (other._year, other._month, other._day,
1684 other._hour, other._minute, other._second,
1685 other._microsecond))
1686 if myoff is None or otoff is None:
1687 raise TypeError("cannot compare naive and aware datetimes")
1688 # XXX What follows could be done more efficiently...
1689 diff = self - other # this will take offsets into account
1690 if diff.days < 0:
1691 return -1
1692 return diff and 1 or 0
1693
1694 def __add__(self, other):
1695 "Add a datetime and a timedelta."
1696 if not isinstance(other, timedelta):
1697 return NotImplemented
1698 delta = timedelta(self.toordinal(),
1699 hours=self._hour,
1700 minutes=self._minute,
1701 seconds=self._second,
1702 microseconds=self._microsecond)
1703 delta += other
1704 hour, rem = divmod(delta.seconds, 3600)
1705 minute, second = divmod(rem, 60)
1706 if 0 < delta.days <= _MAXORDINAL:
1707 return datetime.combine(date.fromordinal(delta.days),
1708 time(hour, minute, second,
1709 delta.microseconds,
1710 tzinfo=self._tzinfo))
1711 raise OverflowError("result out of range")
1712
1713 __radd__ = __add__
1714
1715 def __sub__(self, other):
1716 "Subtract two datetimes, or a datetime and a timedelta."
1717 if not isinstance(other, datetime):
1718 if isinstance(other, timedelta):
1719 return self + -other
1720 return NotImplemented
1721
1722 days1 = self.toordinal()
1723 days2 = other.toordinal()
1724 secs1 = self._second + self._minute * 60 + self._hour * 3600
1725 secs2 = other._second + other._minute * 60 + other._hour * 3600
1726 base = timedelta(days1 - days2,
1727 secs1 - secs2,
1728 self._microsecond - other._microsecond)
1729 if self._tzinfo is other._tzinfo:
1730 return base
1731 myoff = self.utcoffset()
1732 otoff = other.utcoffset()
1733 if myoff == otoff:
1734 return base
1735 if myoff is None or otoff is None:
1736 raise TypeError("cannot mix naive and timezone-aware time")
1737 return base + otoff - myoff
1738
1739 def __hash__(self):
1740 tzoff = self.utcoffset()
1741 if tzoff is None:
1742 return hash(self._getstate()[0])
1743 days = _ymd2ord(self.year, self.month, self.day)
1744 seconds = self.hour * 3600 + self.minute * 60 + self.second
1745 return hash(timedelta(days, seconds, self.microsecond) - tzoff)
1746
1747 # Pickle support.
1748
1749 def _getstate(self):
1750 yhi, ylo = divmod(self._year, 256)
1751 us2, us3 = divmod(self._microsecond, 256)
1752 us1, us2 = divmod(us2, 256)
1753 basestate = bytes([yhi, ylo, self._month, self._day,
1754 self._hour, self._minute, self._second,
1755 us1, us2, us3])
1756 if self._tzinfo is None:
1757 return (basestate,)
1758 else:
1759 return (basestate, self._tzinfo)
1760
1761 def __setstate(self, string, tzinfo):
1762 (yhi, ylo, self._month, self._day, self._hour,
1763 self._minute, self._second, us1, us2, us3) = string
1764 self._year = yhi * 256 + ylo
1765 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1766 if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
1767 self._tzinfo = tzinfo
1768 else:
1769 raise TypeError("bad tzinfo state arg %r" % tzinfo)
1770
1771 def __reduce__(self):
1772 return (self.__class__, self._getstate())
1773
1774
1775datetime.min = datetime(1, 1, 1)
1776datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
1777datetime.resolution = timedelta(microseconds=1)
1778
1779
1780def _isoweek1monday(year):
1781 # Helper to calculate the day number of the Monday starting week 1
1782 # XXX This could be done more efficiently
1783 THURSDAY = 3
1784 firstday = _ymd2ord(year, 1, 1)
1785 firstweekday = (firstday + 6) % 7 # See weekday() above
1786 week1monday = firstday - firstweekday
1787 if firstweekday > THURSDAY:
1788 week1monday += 7
1789 return week1monday
1790
1791class timezone(tzinfo):
1792 __slots__ = '_offset', '_name'
1793
1794 # Sentinel value to disallow None
1795 _Omitted = object()
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001796 def __new__(cls, offset, name=_Omitted):
1797 if not isinstance(offset, timedelta):
1798 raise TypeError("offset must be a timedelta")
1799 if name is cls._Omitted:
1800 if not offset:
1801 return cls.utc
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001802 name = None
1803 elif not isinstance(name, str):
1804 raise TypeError("name must be a string")
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001805 if not cls._minoffset <= offset <= cls._maxoffset:
1806 raise ValueError("offset must be a timedelta"
1807 " strictly between -timedelta(hours=24) and"
1808 " timedelta(hours=24).")
1809 if (offset.microseconds != 0 or
1810 offset.seconds % 60 != 0):
1811 raise ValueError("offset must be a timedelta"
1812 " representing a whole number of minutes")
1813 return cls._create(offset, name)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001814
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001815 @classmethod
1816 def _create(cls, offset, name=None):
1817 self = tzinfo.__new__(cls)
1818 self._offset = offset
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001819 self._name = name
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001820 return self
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001821
1822 def __getinitargs__(self):
1823 """pickle support"""
1824 if self._name is None:
1825 return (self._offset,)
1826 return (self._offset, self._name)
1827
1828 def __eq__(self, other):
1829 return self._offset == other._offset
1830
1831 def __hash__(self):
1832 return hash(self._offset)
1833
1834 def __repr__(self):
1835 """Convert to formal string, for repr().
1836
1837 >>> tz = timezone.utc
1838 >>> repr(tz)
1839 'datetime.timezone.utc'
1840 >>> tz = timezone(timedelta(hours=-5), 'EST')
1841 >>> repr(tz)
1842 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
1843 """
1844 if self is self.utc:
1845 return 'datetime.timezone.utc'
1846 if self._name is None:
1847 return "%s(%r)" % ('datetime.' + self.__class__.__name__,
1848 self._offset)
1849 return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__,
1850 self._offset, self._name)
1851
1852 def __str__(self):
1853 return self.tzname(None)
1854
1855 def utcoffset(self, dt):
1856 if isinstance(dt, datetime) or dt is None:
1857 return self._offset
1858 raise TypeError("utcoffset() argument must be a datetime instance"
1859 " or None")
1860
1861 def tzname(self, dt):
1862 if isinstance(dt, datetime) or dt is None:
1863 if self._name is None:
1864 return self._name_from_offset(self._offset)
1865 return self._name
1866 raise TypeError("tzname() argument must be a datetime instance"
1867 " or None")
1868
1869 def dst(self, dt):
1870 if isinstance(dt, datetime) or dt is None:
1871 return None
1872 raise TypeError("dst() argument must be a datetime instance"
1873 " or None")
1874
1875 def fromutc(self, dt):
1876 if isinstance(dt, datetime):
1877 if dt.tzinfo is not self:
1878 raise ValueError("fromutc: dt.tzinfo "
1879 "is not self")
1880 return dt + self._offset
1881 raise TypeError("fromutc() argument must be a datetime instance"
1882 " or None")
1883
1884 _maxoffset = timedelta(hours=23, minutes=59)
1885 _minoffset = -_maxoffset
1886
1887 @staticmethod
1888 def _name_from_offset(delta):
1889 if delta < timedelta(0):
1890 sign = '-'
1891 delta = -delta
1892 else:
1893 sign = '+'
1894 hours, rest = divmod(delta, timedelta(hours=1))
1895 minutes = rest // timedelta(minutes=1)
1896 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
1897
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +00001898timezone.utc = timezone._create(timedelta(0))
1899timezone.min = timezone._create(timezone._minoffset)
1900timezone.max = timezone._create(timezone._maxoffset)
Alexander Belopolskya4415142012-06-08 12:33:09 -04001901_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001902"""
1903Some time zone algebra. For a datetime x, let
1904 x.n = x stripped of its timezone -- its naive time.
1905 x.o = x.utcoffset(), and assuming that doesn't raise an exception or
1906 return None
1907 x.d = x.dst(), and assuming that doesn't raise an exception or
1908 return None
1909 x.s = x's standard offset, x.o - x.d
1910
1911Now some derived rules, where k is a duration (timedelta).
1912
19131. x.o = x.s + x.d
1914 This follows from the definition of x.s.
1915
19162. If x and y have the same tzinfo member, x.s = y.s.
1917 This is actually a requirement, an assumption we need to make about
1918 sane tzinfo classes.
1919
19203. The naive UTC time corresponding to x is x.n - x.o.
1921 This is again a requirement for a sane tzinfo class.
1922
19234. (x+k).s = x.s
1924 This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
1925
19265. (x+k).n = x.n + k
1927 Again follows from how arithmetic is defined.
1928
1929Now we can explain tz.fromutc(x). Let's assume it's an interesting case
1930(meaning that the various tzinfo methods exist, and don't blow up or return
1931None when called).
1932
1933The function wants to return a datetime y with timezone tz, equivalent to x.
1934x is already in UTC.
1935
1936By #3, we want
1937
1938 y.n - y.o = x.n [1]
1939
1940The algorithm starts by attaching tz to x.n, and calling that y. So
1941x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
1942becomes true; in effect, we want to solve [2] for k:
1943
1944 (y+k).n - (y+k).o = x.n [2]
1945
1946By #1, this is the same as
1947
1948 (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
1949
1950By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
1951Substituting that into [3],
1952
1953 x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
1954 k - (y+k).s - (y+k).d = 0; rearranging,
1955 k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
1956 k = y.s - (y+k).d
1957
1958On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
1959approximate k by ignoring the (y+k).d term at first. Note that k can't be
1960very large, since all offset-returning methods return a duration of magnitude
1961less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
1962be 0, so ignoring it has no consequence then.
1963
1964In any case, the new value is
1965
1966 z = y + y.s [4]
1967
1968It's helpful to step back at look at [4] from a higher level: it's simply
1969mapping from UTC to tz's standard time.
1970
1971At this point, if
1972
1973 z.n - z.o = x.n [5]
1974
1975we have an equivalent time, and are almost done. The insecurity here is
1976at the start of daylight time. Picture US Eastern for concreteness. The wall
1977time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
1978sense then. The docs ask that an Eastern tzinfo class consider such a time to
1979be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
1980on the day DST starts. We want to return the 1:MM EST spelling because that's
1981the only spelling that makes sense on the local wall clock.
1982
1983In fact, if [5] holds at this point, we do have the standard-time spelling,
1984but that takes a bit of proof. We first prove a stronger result. What's the
1985difference between the LHS and RHS of [5]? Let
1986
1987 diff = x.n - (z.n - z.o) [6]
1988
1989Now
1990 z.n = by [4]
1991 (y + y.s).n = by #5
1992 y.n + y.s = since y.n = x.n
1993 x.n + y.s = since z and y are have the same tzinfo member,
1994 y.s = z.s by #2
1995 x.n + z.s
1996
1997Plugging that back into [6] gives
1998
1999 diff =
2000 x.n - ((x.n + z.s) - z.o) = expanding
2001 x.n - x.n - z.s + z.o = cancelling
2002 - z.s + z.o = by #2
2003 z.d
2004
2005So diff = z.d.
2006
2007If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
2008spelling we wanted in the endcase described above. We're done. Contrarily,
2009if z.d = 0, then we have a UTC equivalent, and are also done.
2010
2011If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
2012add to z (in effect, z is in tz's standard time, and we need to shift the
2013local clock into tz's daylight time).
2014
2015Let
2016
2017 z' = z + z.d = z + diff [7]
2018
2019and we can again ask whether
2020
2021 z'.n - z'.o = x.n [8]
2022
2023If so, we're done. If not, the tzinfo class is insane, according to the
2024assumptions we've made. This also requires a bit of proof. As before, let's
2025compute the difference between the LHS and RHS of [8] (and skipping some of
2026the justifications for the kinds of substitutions we've done several times
2027already):
2028
2029 diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
2030 x.n - (z.n + diff - z'.o) = replacing diff via [6]
2031 x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
2032 x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
2033 - z.n + z.n - z.o + z'.o = cancel z.n
2034 - z.o + z'.o = #1 twice
2035 -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
2036 z'.d - z.d
2037
2038So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
2039we've found the UTC-equivalent so are done. In fact, we stop with [7] and
2040return z', not bothering to compute z'.d.
2041
2042How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
2043a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
2044would have to change the result dst() returns: we start in DST, and moving
2045a little further into it takes us out of DST.
2046
2047There isn't a sane case where this can happen. The closest it gets is at
2048the end of DST, where there's an hour in UTC with no spelling in a hybrid
2049tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
2050that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
2051UTC) because the docs insist on that, but 0:MM is taken as being in daylight
2052time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
2053clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
2054standard time. Since that's what the local clock *does*, we want to map both
2055UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
2056in local time, but so it goes -- it's the way the local clock works.
2057
2058When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
2059so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
2060z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
2061(correctly) concludes that z' is not UTC-equivalent to x.
2062
2063Because we know z.d said z was in daylight time (else [5] would have held and
2064we would have stopped then), and we know z.d != z'.d (else [8] would have held
Ezio Melottie130a522011-10-19 10:58:56 +03002065and we have stopped then), and there are only 2 possible values dst() can
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002066return in Eastern, it follows that z'.d must be 0 (which it is in the example,
2067but the reasoning doesn't depend on the example -- it depends on there being
2068two possible dst() outcomes, one zero and the other non-zero). Therefore
2069z' must be in standard time, and is the spelling we want in this case.
2070
2071Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
2072concerned (because it takes z' as being in standard time rather than the
2073daylight time we intend here), but returning it gives the real-life "local
2074clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
2075tz.
2076
2077When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
2078the 1:MM standard time spelling we want.
2079
2080So how can this break? One of the assumptions must be violated. Two
2081possibilities:
2082
20831) [2] effectively says that y.s is invariant across all y belong to a given
2084 time zone. This isn't true if, for political reasons or continental drift,
2085 a region decides to change its base offset from UTC.
2086
20872) There may be versions of "double daylight" time where the tail end of
2088 the analysis gives up a step too early. I haven't thought about that
2089 enough to say.
2090
2091In any case, it's clear that the default fromutc() is strong enough to handle
2092"almost all" time zones: so long as the standard offset is invariant, it
2093doesn't matter if daylight time transition points change from year to year, or
2094if daylight time is skipped in some years; it doesn't matter how large or
2095small dst() may get within its bounds; and it doesn't even matter if some
2096perverse time zone returns a negative dst()). So a breaking case must be
2097pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
2098"""
2099try:
2100 from _datetime import *
2101except ImportError:
2102 pass
2103else:
2104 # Clean up unused names
2105 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH,
2106 _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES,
2107 _build_struct_time, _call_tzinfo_method, _check_date_fields,
2108 _check_time_fields, _check_tzinfo_arg, _check_tzname,
2109 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
2110 _days_before_year, _days_in_month, _format_time, _is_leap,
2111 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
2112 _wrap_strftime, _ymd2ord)
Alexander Belopolskya5658742010-07-23 20:03:53 +00002113 # XXX Since import * above excludes names that start with _,
2114 # docstring does not get overwritten. In the future, it may be
2115 # appropriate to maintain a single module level docstring and
2116 # remove the following line.
2117 from _datetime import __doc__