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