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