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