1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152 |
- """Concrete date/time and related types.
- See http://www.iana.org/time-zones/repository/tz-link.html for
- time zone and DST data sources.
- """
- from __future__ import division
- from __future__ import unicode_literals
- from __future__ import print_function
- from __future__ import absolute_import
- from future.builtins import str
- from future.builtins import bytes
- from future.builtins import map
- from future.builtins import round
- from future.builtins import int
- from future.builtins import object
- from future.utils import native_str, PY2
- import time as _time
- import math as _math
- def _cmp(x, y):
- return 0 if x == y else 1 if x > y else -1
- MINYEAR = 1
- MAXYEAR = 9999
- _MAXORDINAL = 3652059 # date.max.toordinal()
- # Utility functions, adapted from Python's Demo/classes/Dates.py, which
- # also assumes the current Gregorian calendar indefinitely extended in
- # both directions. Difference: Dates.py calls January 1 of year 0 day
- # number 1. The code here calls January 1 of year 1 day number 1. This is
- # to match the definition of the "proleptic Gregorian" calendar in Dershowitz
- # and Reingold's "Calendrical Calculations", where it's the base calendar
- # for all computations. See the book for algorithms for converting between
- # proleptic Gregorian ordinals and many other calendar systems.
- _DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
- _DAYS_BEFORE_MONTH = [None]
- dbm = 0
- for dim in _DAYS_IN_MONTH[1:]:
- _DAYS_BEFORE_MONTH.append(dbm)
- dbm += dim
- del dbm, dim
- def _is_leap(year):
- "year -> 1 if leap year, else 0."
- return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
- def _days_before_year(year):
- "year -> number of days before January 1st of year."
- y = year - 1
- return y*365 + y//4 - y//100 + y//400
- def _days_in_month(year, month):
- "year, month -> number of days in that month in that year."
- assert 1 <= month <= 12, month
- if month == 2 and _is_leap(year):
- return 29
- return _DAYS_IN_MONTH[month]
- def _days_before_month(year, month):
- "year, month -> number of days in year preceding first day of month."
- assert 1 <= month <= 12, 'month must be in 1..12'
- return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
- def _ymd2ord(year, month, day):
- "year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
- assert 1 <= month <= 12, 'month must be in 1..12'
- dim = _days_in_month(year, month)
- assert 1 <= day <= dim, ('day must be in 1..%d' % dim)
- return (_days_before_year(year) +
- _days_before_month(year, month) +
- day)
- _DI400Y = _days_before_year(401) # number of days in 400 years
- _DI100Y = _days_before_year(101) # " " " " 100 "
- _DI4Y = _days_before_year(5) # " " " " 4 "
- # A 4-year cycle has an extra leap day over what we'd get from pasting
- # together 4 single years.
- assert _DI4Y == 4 * 365 + 1
- # Similarly, a 400-year cycle has an extra leap day over what we'd get from
- # pasting together 4 100-year cycles.
- assert _DI400Y == 4 * _DI100Y + 1
- # OTOH, a 100-year cycle has one fewer leap day than we'd get from
- # pasting together 25 4-year cycles.
- assert _DI100Y == 25 * _DI4Y - 1
- def _ord2ymd(n):
- "ordinal -> (year, month, day), considering 01-Jan-0001 as day 1."
- # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years
- # repeats exactly every 400 years. The basic strategy is to find the
- # closest 400-year boundary at or before n, then work with the offset
- # from that boundary to n. Life is much clearer if we subtract 1 from
- # n first -- then the values of n at 400-year boundaries are exactly
- # those divisible by _DI400Y:
- #
- # D M Y n n-1
- # -- --- ---- ---------- ----------------
- # 31 Dec -400 -_DI400Y -_DI400Y -1
- # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary
- # ...
- # 30 Dec 000 -1 -2
- # 31 Dec 000 0 -1
- # 1 Jan 001 1 0 400-year boundary
- # 2 Jan 001 2 1
- # 3 Jan 001 3 2
- # ...
- # 31 Dec 400 _DI400Y _DI400Y -1
- # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary
- n -= 1
- n400, n = divmod(n, _DI400Y)
- year = n400 * 400 + 1 # ..., -399, 1, 401, ...
- # Now n is the (non-negative) offset, in days, from January 1 of year, to
- # the desired date. Now compute how many 100-year cycles precede n.
- # Note that it's possible for n100 to equal 4! In that case 4 full
- # 100-year cycles precede the desired day, which implies the desired
- # day is December 31 at the end of a 400-year cycle.
- n100, n = divmod(n, _DI100Y)
- # Now compute how many 4-year cycles precede it.
- n4, n = divmod(n, _DI4Y)
- # And now how many single years. Again n1 can be 4, and again meaning
- # that the desired day is December 31 at the end of the 4-year cycle.
- n1, n = divmod(n, 365)
- year += n100 * 100 + n4 * 4 + n1
- if n1 == 4 or n100 == 4:
- assert n == 0
- return year-1, 12, 31
- # Now the year is correct, and n is the offset from January 1. We find
- # the month via an estimate that's either exact or one too large.
- leapyear = n1 == 3 and (n4 != 24 or n100 == 3)
- assert leapyear == _is_leap(year)
- month = (n + 50) >> 5
- preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)
- if preceding > n: # estimate is too large
- month -= 1
- preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)
- n -= preceding
- assert 0 <= n < _days_in_month(year, month)
- # Now the year and month are correct, and n is the offset from the
- # start of that month: we're done!
- return year, month, n+1
- # Month and day names. For localized versions, see the calendar module.
- _MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
- _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
- def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
- wday = (_ymd2ord(y, m, d) + 6) % 7
- dnum = _days_before_month(y, m) + d
- return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
- def _format_time(hh, mm, ss, us):
- # Skip trailing microseconds when us==0.
- result = "%02d:%02d:%02d" % (hh, mm, ss)
- if us:
- result += ".%06d" % us
- return result
- # Correctly substitute for %z and %Z escapes in strftime formats.
- def _wrap_strftime(object, format, timetuple):
- # Don't call utcoffset() or tzname() unless actually needed.
- freplace = None # the string to use for %f
- zreplace = None # the string to use for %z
- Zreplace = None # the string to use for %Z
- # Scan format for %z and %Z escapes, replacing as needed.
- newformat = []
- push = newformat.append
- i, n = 0, len(format)
- while i < n:
- ch = format[i]
- i += 1
- if ch == '%':
- if i < n:
- ch = format[i]
- i += 1
- if ch == 'f':
- if freplace is None:
- freplace = '%06d' % getattr(object,
- 'microsecond', 0)
- newformat.append(freplace)
- elif ch == 'z':
- if zreplace is None:
- zreplace = ""
- if hasattr(object, "utcoffset"):
- offset = object.utcoffset()
- if offset is not None:
- sign = '+'
- if offset.days < 0:
- offset = -offset
- sign = '-'
- h, m = divmod(offset, timedelta(hours=1))
- assert not m % timedelta(minutes=1), "whole minute"
- m //= timedelta(minutes=1)
- zreplace = '%c%02d%02d' % (sign, h, m)
- assert '%' not in zreplace
- newformat.append(zreplace)
- elif ch == 'Z':
- if Zreplace is None:
- Zreplace = ""
- if hasattr(object, "tzname"):
- s = object.tzname()
- if s is not None:
- # strftime is going to have at this: escape %
- Zreplace = s.replace('%', '%%')
- newformat.append(Zreplace)
- else:
- push('%')
- push(ch)
- else:
- push('%')
- else:
- push(ch)
- newformat = "".join(newformat)
- return _time.strftime(newformat, timetuple)
- def _call_tzinfo_method(tzinfo, methname, tzinfoarg):
- if tzinfo is None:
- return None
- return getattr(tzinfo, methname)(tzinfoarg)
- # Just raise TypeError if the arg isn't None or a string.
- def _check_tzname(name):
- if name is not None and not isinstance(name, str):
- raise TypeError("tzinfo.tzname() must return None or string, "
- "not '%s'" % type(name))
- # name is the offset-producing method, "utcoffset" or "dst".
- # offset is what it returned.
- # If offset isn't None or timedelta, raises TypeError.
- # If offset is None, returns None.
- # Else offset is checked for being in range, and a whole # of minutes.
- # If it is, its integer value is returned. Else ValueError is raised.
- def _check_utc_offset(name, offset):
- assert name in ("utcoffset", "dst")
- if offset is None:
- return
- if not isinstance(offset, timedelta):
- raise TypeError("tzinfo.%s() must return None "
- "or timedelta, not '%s'" % (name, type(offset)))
- if offset % timedelta(minutes=1) or offset.microseconds:
- raise ValueError("tzinfo.%s() must return a whole number "
- "of minutes, got %s" % (name, offset))
- if not -timedelta(1) < offset < timedelta(1):
- raise ValueError("%s()=%s, must be must be strictly between"
- " -timedelta(hours=24) and timedelta(hours=24)"
- % (name, offset))
- def _check_date_fields(year, month, day):
- if not isinstance(year, int):
- raise TypeError('int expected')
- if not MINYEAR <= year <= MAXYEAR:
- raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
- if not 1 <= month <= 12:
- raise ValueError('month must be in 1..12', month)
- dim = _days_in_month(year, month)
- if not 1 <= day <= dim:
- raise ValueError('day must be in 1..%d' % dim, day)
- def _check_time_fields(hour, minute, second, microsecond):
- if not isinstance(hour, int):
- raise TypeError('int expected')
- if not 0 <= hour <= 23:
- raise ValueError('hour must be in 0..23', hour)
- if not 0 <= minute <= 59:
- raise ValueError('minute must be in 0..59', minute)
- if not 0 <= second <= 59:
- raise ValueError('second must be in 0..59', second)
- if not 0 <= microsecond <= 999999:
- raise ValueError('microsecond must be in 0..999999', microsecond)
- def _check_tzinfo_arg(tz):
- if tz is not None and not isinstance(tz, tzinfo):
- raise TypeError("tzinfo argument must be None or of a tzinfo subclass")
- def _cmperror(x, y):
- raise TypeError("can't compare '%s' to '%s'" % (
- type(x).__name__, type(y).__name__))
- class timedelta(object):
- """Represent the difference between two datetime objects.
- Supported operators:
- - add, subtract timedelta
- - unary plus, minus, abs
- - compare to timedelta
- - multiply, divide by int
- In addition, datetime supports subtraction of two datetime objects
- returning a timedelta, and addition or subtraction of a datetime
- and a timedelta giving a datetime.
- Representation: (days, seconds, microseconds). Why? Because I
- felt like it.
- """
- __slots__ = '_days', '_seconds', '_microseconds'
- def __new__(cls, days=0, seconds=0, microseconds=0,
- milliseconds=0, minutes=0, hours=0, weeks=0):
- # Doing this efficiently and accurately in C is going to be difficult
- # and error-prone, due to ubiquitous overflow possibilities, and that
- # C double doesn't have enough bits of precision to represent
- # microseconds over 10K years faithfully. The code here tries to make
- # explicit where go-fast assumptions can be relied on, in order to
- # guide the C implementation; it's way more convoluted than speed-
- # ignoring auto-overflow-to-long idiomatic Python could be.
- # XXX Check that all inputs are ints or floats.
- # Final values, all integer.
- # s and us fit in 32-bit signed ints; d isn't bounded.
- d = s = us = 0
- # Normalize everything to days, seconds, microseconds.
- days += weeks*7
- seconds += minutes*60 + hours*3600
- microseconds += milliseconds*1000
- # Get rid of all fractions, and normalize s and us.
- # Take a deep breath <wink>.
- if isinstance(days, float):
- dayfrac, days = _math.modf(days)
- daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
- assert daysecondswhole == int(daysecondswhole) # can't overflow
- s = int(daysecondswhole)
- assert days == int(days)
- d = int(days)
- else:
- daysecondsfrac = 0.0
- d = days
- assert isinstance(daysecondsfrac, float)
- assert abs(daysecondsfrac) <= 1.0
- assert isinstance(d, int)
- assert abs(s) <= 24 * 3600
- # days isn't referenced again before redefinition
- if isinstance(seconds, float):
- secondsfrac, seconds = _math.modf(seconds)
- assert seconds == int(seconds)
- seconds = int(seconds)
- secondsfrac += daysecondsfrac
- assert abs(secondsfrac) <= 2.0
- else:
- secondsfrac = daysecondsfrac
- # daysecondsfrac isn't referenced again
- assert isinstance(secondsfrac, float)
- assert abs(secondsfrac) <= 2.0
- assert isinstance(seconds, int)
- days, seconds = divmod(seconds, 24*3600)
- d += days
- s += int(seconds) # can't overflow
- assert isinstance(s, int)
- assert abs(s) <= 2 * 24 * 3600
- # seconds isn't referenced again before redefinition
- usdouble = secondsfrac * 1e6
- assert abs(usdouble) < 2.1e6 # exact value not critical
- # secondsfrac isn't referenced again
- if isinstance(microseconds, float):
- microseconds += usdouble
- microseconds = round(microseconds, 0)
- seconds, microseconds = divmod(microseconds, 1e6)
- assert microseconds == int(microseconds)
- assert seconds == int(seconds)
- days, seconds = divmod(seconds, 24.*3600.)
- assert days == int(days)
- assert seconds == int(seconds)
- d += int(days)
- s += int(seconds) # can't overflow
- assert isinstance(s, int)
- assert abs(s) <= 3 * 24 * 3600
- else:
- seconds, microseconds = divmod(microseconds, 1000000)
- days, seconds = divmod(seconds, 24*3600)
- d += days
- s += int(seconds) # can't overflow
- assert isinstance(s, int)
- assert abs(s) <= 3 * 24 * 3600
- microseconds = float(microseconds)
- microseconds += usdouble
- microseconds = round(microseconds, 0)
- assert abs(s) <= 3 * 24 * 3600
- assert abs(microseconds) < 3.1e6
- # Just a little bit of carrying possible for microseconds and seconds.
- assert isinstance(microseconds, float)
- assert int(microseconds) == microseconds
- us = int(microseconds)
- seconds, us = divmod(us, 1000000)
- s += seconds # cant't overflow
- assert isinstance(s, int)
- days, s = divmod(s, 24*3600)
- d += days
- assert isinstance(d, int)
- assert isinstance(s, int) and 0 <= s < 24*3600
- assert isinstance(us, int) and 0 <= us < 1000000
- self = object.__new__(cls)
- self._days = d
- self._seconds = s
- self._microseconds = us
- if abs(d) > 999999999:
- raise OverflowError("timedelta # of days is too large: %d" % d)
- return self
- def __repr__(self):
- if self._microseconds:
- return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
- self._days,
- self._seconds,
- self._microseconds)
- if self._seconds:
- return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__,
- self._days,
- self._seconds)
- return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days)
- def __str__(self):
- mm, ss = divmod(self._seconds, 60)
- hh, mm = divmod(mm, 60)
- s = "%d:%02d:%02d" % (hh, mm, ss)
- if self._days:
- def plural(n):
- return n, abs(n) != 1 and "s" or ""
- s = ("%d day%s, " % plural(self._days)) + s
- if self._microseconds:
- s = s + ".%06d" % self._microseconds
- return s
- def total_seconds(self):
- """Total seconds in the duration."""
- return ((self.days * 86400 + self.seconds)*10**6 +
- self.microseconds) / 10**6
- # Read-only field accessors
- @property
- def days(self):
- """days"""
- return self._days
- @property
- def seconds(self):
- """seconds"""
- return self._seconds
- @property
- def microseconds(self):
- """microseconds"""
- return self._microseconds
- def __add__(self, other):
- if isinstance(other, timedelta):
- # for CPython compatibility, we cannot use
- # our __class__ here, but need a real timedelta
- return timedelta(self._days + other._days,
- self._seconds + other._seconds,
- self._microseconds + other._microseconds)
- return NotImplemented
- __radd__ = __add__
- def __sub__(self, other):
- if isinstance(other, timedelta):
- # for CPython compatibility, we cannot use
- # our __class__ here, but need a real timedelta
- return timedelta(self._days - other._days,
- self._seconds - other._seconds,
- self._microseconds - other._microseconds)
- return NotImplemented
- def __rsub__(self, other):
- if isinstance(other, timedelta):
- return -self + other
- return NotImplemented
- def __neg__(self):
- # for CPython compatibility, we cannot use
- # our __class__ here, but need a real timedelta
- return timedelta(-self._days,
- -self._seconds,
- -self._microseconds)
- def __pos__(self):
- return self
- def __abs__(self):
- if self._days < 0:
- return -self
- else:
- return self
- def __mul__(self, other):
- if isinstance(other, int):
- # for CPython compatibility, we cannot use
- # our __class__ here, but need a real timedelta
- return timedelta(self._days * other,
- self._seconds * other,
- self._microseconds * other)
- if isinstance(other, float):
- a, b = other.as_integer_ratio()
- return self * a / b
- return NotImplemented
- __rmul__ = __mul__
- def _to_microseconds(self):
- return ((self._days * (24*3600) + self._seconds) * 1000000 +
- self._microseconds)
- def __floordiv__(self, other):
- if not isinstance(other, (int, timedelta)):
- return NotImplemented
- usec = self._to_microseconds()
- if isinstance(other, timedelta):
- return usec // other._to_microseconds()
- if isinstance(other, int):
- return timedelta(0, 0, usec // other)
- def __truediv__(self, other):
- if not isinstance(other, (int, float, timedelta)):
- return NotImplemented
- usec = self._to_microseconds()
- if isinstance(other, timedelta):
- return usec / other._to_microseconds()
- if isinstance(other, int):
- return timedelta(0, 0, usec / other)
- if isinstance(other, float):
- a, b = other.as_integer_ratio()
- return timedelta(0, 0, b * usec / a)
- def __mod__(self, other):
- if isinstance(other, timedelta):
- r = self._to_microseconds() % other._to_microseconds()
- return timedelta(0, 0, r)
- return NotImplemented
- def __divmod__(self, other):
- if isinstance(other, timedelta):
- q, r = divmod(self._to_microseconds(),
- other._to_microseconds())
- return q, timedelta(0, 0, r)
- return NotImplemented
- # Comparisons of timedelta objects with other.
- def __eq__(self, other):
- if isinstance(other, timedelta):
- return self._cmp(other) == 0
- else:
- return False
- def __ne__(self, other):
- if isinstance(other, timedelta):
- return self._cmp(other) != 0
- else:
- return True
- def __le__(self, other):
- if isinstance(other, timedelta):
- return self._cmp(other) <= 0
- else:
- _cmperror(self, other)
- def __lt__(self, other):
- if isinstance(other, timedelta):
- return self._cmp(other) < 0
- else:
- _cmperror(self, other)
- def __ge__(self, other):
- if isinstance(other, timedelta):
- return self._cmp(other) >= 0
- else:
- _cmperror(self, other)
- def __gt__(self, other):
- if isinstance(other, timedelta):
- return self._cmp(other) > 0
- else:
- _cmperror(self, other)
- def _cmp(self, other):
- assert isinstance(other, timedelta)
- return _cmp(self._getstate(), other._getstate())
- def __hash__(self):
- return hash(self._getstate())
- def __bool__(self):
- return (self._days != 0 or
- self._seconds != 0 or
- self._microseconds != 0)
- # Pickle support.
- def _getstate(self):
- return (self._days, self._seconds, self._microseconds)
- def __reduce__(self):
- return (self.__class__, self._getstate())
- timedelta.min = timedelta(-999999999)
- timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
- microseconds=999999)
- timedelta.resolution = timedelta(microseconds=1)
- class date(object):
- """Concrete date type.
- Constructors:
- __new__()
- fromtimestamp()
- today()
- fromordinal()
- Operators:
- __repr__, __str__
- __cmp__, __hash__
- __add__, __radd__, __sub__ (add/radd only with timedelta arg)
- Methods:
- timetuple()
- toordinal()
- weekday()
- isoweekday(), isocalendar(), isoformat()
- ctime()
- strftime()
- Properties (readonly):
- year, month, day
- """
- __slots__ = '_year', '_month', '_day'
- def __new__(cls, year, month=None, day=None):
- """Constructor.
- Arguments:
- year, month, day (required, base 1)
- """
- if (isinstance(year, bytes) and len(year) == 4 and
- 1 <= year[2] <= 12 and month is None): # Month is sane
- # Pickle support
- self = object.__new__(cls)
- self.__setstate(year)
- return self
- _check_date_fields(year, month, day)
- self = object.__new__(cls)
- self._year = year
- self._month = month
- self._day = day
- return self
- # Additional constructors
- @classmethod
- def fromtimestamp(cls, t):
- "Construct a date from a POSIX timestamp (like time.time())."
- y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
- return cls(y, m, d)
- @classmethod
- def today(cls):
- "Construct a date from time.time()."
- t = _time.time()
- return cls.fromtimestamp(t)
- @classmethod
- def fromordinal(cls, n):
- """Contruct a date from a proleptic Gregorian ordinal.
- January 1 of year 1 is day 1. Only the year, month and day are
- non-zero in the result.
- """
- y, m, d = _ord2ymd(n)
- return cls(y, m, d)
- # Conversions to string
- def __repr__(self):
- """Convert to formal string, for repr().
- >>> dt = datetime(2010, 1, 1)
- >>> repr(dt)
- 'datetime.datetime(2010, 1, 1, 0, 0)'
- >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
- >>> repr(dt)
- 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'
- """
- return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
- self._year,
- self._month,
- self._day)
- # XXX These shouldn't depend on time.localtime(), because that
- # clips the usable dates to [1970 .. 2038). At least ctime() is
- # easily done without using strftime() -- that's better too because
- # strftime("%c", ...) is locale specific.
- def ctime(self):
- "Return ctime() style string."
- weekday = self.toordinal() % 7 or 7
- return "%s %s %2d 00:00:00 %04d" % (
- _DAYNAMES[weekday],
- _MONTHNAMES[self._month],
- self._day, self._year)
- def strftime(self, fmt):
- "Format using strftime()."
- return _wrap_strftime(self, fmt, self.timetuple())
- def __format__(self, fmt):
- if len(fmt) != 0:
- return self.strftime(fmt)
- return str(self)
- def isoformat(self):
- """Return the date formatted according to ISO.
- This is 'YYYY-MM-DD'.
- References:
- - http://www.w3.org/TR/NOTE-datetime
- - http://www.cl.cam.ac.uk/~mgk25/iso-time.html
- """
- return "%04d-%02d-%02d" % (self._year, self._month, self._day)
- __str__ = isoformat
- # Read-only field accessors
- @property
- def year(self):
- """year (1-9999)"""
- return self._year
- @property
- def month(self):
- """month (1-12)"""
- return self._month
- @property
- def day(self):
- """day (1-31)"""
- return self._day
- # Standard conversions, __cmp__, __hash__ (and helpers)
- def timetuple(self):
- "Return local time tuple compatible with time.localtime()."
- return _build_struct_time(self._year, self._month, self._day,
- 0, 0, 0, -1)
- def toordinal(self):
- """Return proleptic Gregorian ordinal for the year, month and day.
- January 1 of year 1 is day 1. Only the year, month and day values
- contribute to the result.
- """
- return _ymd2ord(self._year, self._month, self._day)
- def replace(self, year=None, month=None, day=None):
- """Return a new date with new values for the specified fields."""
- if year is None:
- year = self._year
- if month is None:
- month = self._month
- if day is None:
- day = self._day
- _check_date_fields(year, month, day)
- return date(year, month, day)
- # Comparisons of date objects with other.
- def __eq__(self, other):
- if isinstance(other, date):
- return self._cmp(other) == 0
- return NotImplemented
- def __ne__(self, other):
- if isinstance(other, date):
- return self._cmp(other) != 0
- return NotImplemented
- def __le__(self, other):
- if isinstance(other, date):
- return self._cmp(other) <= 0
- return NotImplemented
- def __lt__(self, other):
- if isinstance(other, date):
- return self._cmp(other) < 0
- return NotImplemented
- def __ge__(self, other):
- if isinstance(other, date):
- return self._cmp(other) >= 0
- return NotImplemented
- def __gt__(self, other):
- if isinstance(other, date):
- return self._cmp(other) > 0
- return NotImplemented
- def _cmp(self, other):
- assert isinstance(other, date)
- y, m, d = self._year, self._month, self._day
- y2, m2, d2 = other._year, other._month, other._day
- return _cmp((y, m, d), (y2, m2, d2))
- def __hash__(self):
- "Hash."
- return hash(self._getstate())
- # Computations
- def __add__(self, other):
- "Add a date to a timedelta."
- if isinstance(other, timedelta):
- o = self.toordinal() + other.days
- if 0 < o <= _MAXORDINAL:
- return date.fromordinal(o)
- raise OverflowError("result out of range")
- return NotImplemented
- __radd__ = __add__
- def __sub__(self, other):
- """Subtract two dates, or a date and a timedelta."""
- if isinstance(other, timedelta):
- return self + timedelta(-other.days)
- if isinstance(other, date):
- days1 = self.toordinal()
- days2 = other.toordinal()
- return timedelta(days1 - days2)
- return NotImplemented
- def weekday(self):
- "Return day of the week, where Monday == 0 ... Sunday == 6."
- return (self.toordinal() + 6) % 7
- # Day-of-the-week and week-of-the-year, according to ISO
- def isoweekday(self):
- "Return day of the week, where Monday == 1 ... Sunday == 7."
- # 1-Jan-0001 is a Monday
- return self.toordinal() % 7 or 7
- def isocalendar(self):
- """Return a 3-tuple containing ISO year, week number, and weekday.
- The first ISO week of the year is the (Mon-Sun) week
- containing the year's first Thursday; everything else derives
- from that.
- The first week is 1; Monday is 1 ... Sunday is 7.
- ISO calendar algorithm taken from
- http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
- """
- year = self._year
- week1monday = _isoweek1monday(year)
- today = _ymd2ord(self._year, self._month, self._day)
- # Internally, week and day have origin 0
- week, day = divmod(today - week1monday, 7)
- if week < 0:
- year -= 1
- week1monday = _isoweek1monday(year)
- week, day = divmod(today - week1monday, 7)
- elif week >= 52:
- if today >= _isoweek1monday(year+1):
- year += 1
- week = 0
- return year, week+1, day+1
- # Pickle support.
- def _getstate(self):
- yhi, ylo = divmod(self._year, 256)
- return bytes([yhi, ylo, self._month, self._day]),
- def __setstate(self, string):
- if len(string) != 4 or not (1 <= string[2] <= 12):
- raise TypeError("not enough arguments")
- yhi, ylo, self._month, self._day = string
- self._year = yhi * 256 + ylo
- def __reduce__(self):
- return (self.__class__, self._getstate())
- _date_class = date # so functions w/ args named "date" can get at the class
- date.min = date(1, 1, 1)
- date.max = date(9999, 12, 31)
- date.resolution = timedelta(days=1)
- class tzinfo(object):
- """Abstract base class for time zone info classes.
- Subclasses must override the name(), utcoffset() and dst() methods.
- """
- __slots__ = ()
- def tzname(self, dt):
- "datetime -> string name of time zone."
- raise NotImplementedError("tzinfo subclass must override tzname()")
- def utcoffset(self, dt):
- "datetime -> minutes east of UTC (negative for west of UTC)"
- raise NotImplementedError("tzinfo subclass must override utcoffset()")
- def dst(self, dt):
- """datetime -> DST offset in minutes east of UTC.
- Return 0 if DST not in effect. utcoffset() must include the DST
- offset.
- """
- raise NotImplementedError("tzinfo subclass must override dst()")
- def fromutc(self, dt):
- "datetime in UTC -> datetime in local time."
- if not isinstance(dt, datetime):
- raise TypeError("fromutc() requires a datetime argument")
- if dt.tzinfo is not self:
- raise ValueError("dt.tzinfo is not self")
- dtoff = dt.utcoffset()
- if dtoff is None:
- raise ValueError("fromutc() requires a non-None utcoffset() "
- "result")
- # See the long comment block at the end of this file for an
- # explanation of this algorithm.
- dtdst = dt.dst()
- if dtdst is None:
- raise ValueError("fromutc() requires a non-None dst() result")
- delta = dtoff - dtdst
- if delta:
- dt += delta
- dtdst = dt.dst()
- if dtdst is None:
- raise ValueError("fromutc(): dt.dst gave inconsistent "
- "results; cannot convert")
- return dt + dtdst
- # Pickle support.
- def __reduce__(self):
- getinitargs = getattr(self, "__getinitargs__", None)
- if getinitargs:
- args = getinitargs()
- else:
- args = ()
- getstate = getattr(self, "__getstate__", None)
- if getstate:
- state = getstate()
- else:
- state = getattr(self, "__dict__", None) or None
- if state is None:
- return (self.__class__, args)
- else:
- return (self.__class__, args, state)
- _tzinfo_class = tzinfo
- class time(object):
- """Time with time zone.
- Constructors:
- __new__()
- Operators:
- __repr__, __str__
- __cmp__, __hash__
- Methods:
- strftime()
- isoformat()
- utcoffset()
- tzname()
- dst()
- Properties (readonly):
- hour, minute, second, microsecond, tzinfo
- """
- def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
- """Constructor.
- Arguments:
- hour, minute (required)
- second, microsecond (default to zero)
- tzinfo (default to None)
- """
- self = object.__new__(cls)
- if isinstance(hour, bytes) and len(hour) == 6:
- # Pickle support
- self.__setstate(hour, minute or None)
- return self
- _check_tzinfo_arg(tzinfo)
- _check_time_fields(hour, minute, second, microsecond)
- self._hour = hour
- self._minute = minute
- self._second = second
- self._microsecond = microsecond
- self._tzinfo = tzinfo
- return self
- # Read-only field accessors
- @property
- def hour(self):
- """hour (0-23)"""
- return self._hour
- @property
- def minute(self):
- """minute (0-59)"""
- return self._minute
- @property
- def second(self):
- """second (0-59)"""
- return self._second
- @property
- def microsecond(self):
- """microsecond (0-999999)"""
- return self._microsecond
- @property
- def tzinfo(self):
- """timezone info object"""
- return self._tzinfo
- # Standard conversions, __hash__ (and helpers)
- # Comparisons of time objects with other.
- def __eq__(self, other):
- if isinstance(other, time):
- return self._cmp(other, allow_mixed=True) == 0
- else:
- return False
- def __ne__(self, other):
- if isinstance(other, time):
- return self._cmp(other, allow_mixed=True) != 0
- else:
- return True
- def __le__(self, other):
- if isinstance(other, time):
- return self._cmp(other) <= 0
- else:
- _cmperror(self, other)
- def __lt__(self, other):
- if isinstance(other, time):
- return self._cmp(other) < 0
- else:
- _cmperror(self, other)
- def __ge__(self, other):
- if isinstance(other, time):
- return self._cmp(other) >= 0
- else:
- _cmperror(self, other)
- def __gt__(self, other):
- if isinstance(other, time):
- return self._cmp(other) > 0
- else:
- _cmperror(self, other)
- def _cmp(self, other, allow_mixed=False):
- assert isinstance(other, time)
- mytz = self._tzinfo
- ottz = other._tzinfo
- myoff = otoff = None
- if mytz is ottz:
- base_compare = True
- else:
- myoff = self.utcoffset()
- otoff = other.utcoffset()
- base_compare = myoff == otoff
- if base_compare:
- return _cmp((self._hour, self._minute, self._second,
- self._microsecond),
- (other._hour, other._minute, other._second,
- other._microsecond))
- if myoff is None or otoff is None:
- if allow_mixed:
- return 2 # arbitrary non-zero value
- else:
- raise TypeError("cannot compare naive and aware times")
- myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
- othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
- return _cmp((myhhmm, self._second, self._microsecond),
- (othhmm, other._second, other._microsecond))
- def __hash__(self):
- """Hash."""
- tzoff = self.utcoffset()
- if not tzoff: # zero or None
- return hash(self._getstate()[0])
- h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
- timedelta(hours=1))
- assert not m % timedelta(minutes=1), "whole minute"
- m //= timedelta(minutes=1)
- if 0 <= h < 24:
- return hash(time(h, m, self.second, self.microsecond))
- return hash((h, m, self.second, self.microsecond))
- # Conversion to string
- def _tzstr(self, sep=":"):
- """Return formatted timezone offset (+xx:xx) or None."""
- off = self.utcoffset()
- if off is not None:
- if off.days < 0:
- sign = "-"
- off = -off
- else:
- sign = "+"
- hh, mm = divmod(off, timedelta(hours=1))
- assert not mm % timedelta(minutes=1), "whole minute"
- mm //= timedelta(minutes=1)
- assert 0 <= hh < 24
- off = "%s%02d%s%02d" % (sign, hh, sep, mm)
- return off
- def __repr__(self):
- """Convert to formal string, for repr()."""
- if self._microsecond != 0:
- s = ", %d, %d" % (self._second, self._microsecond)
- elif self._second != 0:
- s = ", %d" % self._second
- else:
- s = ""
- s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__,
- self._hour, self._minute, s)
- if self._tzinfo is not None:
- assert s[-1:] == ")"
- s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
- return s
- def isoformat(self):
- """Return the time formatted according to ISO.
- This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if
- self.microsecond == 0.
- """
- s = _format_time(self._hour, self._minute, self._second,
- self._microsecond)
- tz = self._tzstr()
- if tz:
- s += tz
- return s
- __str__ = isoformat
- def strftime(self, fmt):
- """Format using strftime(). The date part of the timestamp passed
- to underlying strftime should not be used.
- """
- # The year must be >= 1000 else Python's strftime implementation
- # can raise a bogus exception.
- timetuple = (1900, 1, 1,
- self._hour, self._minute, self._second,
- 0, 1, -1)
- return _wrap_strftime(self, fmt, timetuple)
- def __format__(self, fmt):
- if len(fmt) != 0:
- return self.strftime(fmt)
- return str(self)
- # Timezone functions
- def utcoffset(self):
- """Return the timezone offset in minutes east of UTC (negative west of
- UTC)."""
- if self._tzinfo is None:
- return None
- offset = self._tzinfo.utcoffset(None)
- _check_utc_offset("utcoffset", offset)
- return offset
- def tzname(self):
- """Return the timezone name.
- Note that the name is 100% informational -- there's no requirement that
- it mean anything in particular. For example, "GMT", "UTC", "-500",
- "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
- """
- if self._tzinfo is None:
- return None
- name = self._tzinfo.tzname(None)
- _check_tzname(name)
- return name
- def dst(self):
- """Return 0 if DST is not in effect, or the DST offset (in minutes
- eastward) if DST is in effect.
- This is purely informational; the DST offset has already been added to
- the UTC offset returned by utcoffset() if applicable, so there's no
- need to consult dst() unless you're interested in displaying the DST
- info.
- """
- if self._tzinfo is None:
- return None
- offset = self._tzinfo.dst(None)
- _check_utc_offset("dst", offset)
- return offset
- def replace(self, hour=None, minute=None, second=None, microsecond=None,
- tzinfo=True):
- """Return a new time with new values for the specified fields."""
- if hour is None:
- hour = self.hour
- if minute is None:
- minute = self.minute
- if second is None:
- second = self.second
- if microsecond is None:
- microsecond = self.microsecond
- if tzinfo is True:
- tzinfo = self.tzinfo
- _check_time_fields(hour, minute, second, microsecond)
- _check_tzinfo_arg(tzinfo)
- return time(hour, minute, second, microsecond, tzinfo)
- def __bool__(self):
- if self.second or self.microsecond:
- return True
- offset = self.utcoffset() or timedelta(0)
- return timedelta(hours=self.hour, minutes=self.minute) != offset
- # Pickle support.
- def _getstate(self):
- us2, us3 = divmod(self._microsecond, 256)
- us1, us2 = divmod(us2, 256)
- basestate = bytes([self._hour, self._minute, self._second,
- us1, us2, us3])
- if self._tzinfo is None:
- return (basestate,)
- else:
- return (basestate, self._tzinfo)
- def __setstate(self, string, tzinfo):
- if len(string) != 6 or string[0] >= 24:
- raise TypeError("an integer is required")
- (self._hour, self._minute, self._second,
- us1, us2, us3) = string
- self._microsecond = (((us1 << 8) | us2) << 8) | us3
- if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
- self._tzinfo = tzinfo
- else:
- raise TypeError("bad tzinfo state arg %r" % tzinfo)
- def __reduce__(self):
- return (time, self._getstate())
- _time_class = time # so functions w/ args named "time" can get at the class
- time.min = time(0, 0, 0)
- time.max = time(23, 59, 59, 999999)
- time.resolution = timedelta(microseconds=1)
- class datetime(date):
- """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
- The year, month and day arguments are required. tzinfo may be None, or an
- instance of a tzinfo subclass. The remaining arguments may be ints.
- """
- __slots__ = date.__slots__ + (
- '_hour', '_minute', '_second',
- '_microsecond', '_tzinfo')
- def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
- microsecond=0, tzinfo=None):
- if isinstance(year, bytes) and len(year) == 10:
- # Pickle support
- self = date.__new__(cls, year[:4])
- self.__setstate(year, month)
- return self
- _check_tzinfo_arg(tzinfo)
- _check_time_fields(hour, minute, second, microsecond)
- self = date.__new__(cls, year, month, day)
- self._hour = hour
- self._minute = minute
- self._second = second
- self._microsecond = microsecond
- self._tzinfo = tzinfo
- return self
- # Read-only field accessors
- @property
- def hour(self):
- """hour (0-23)"""
- return self._hour
- @property
- def minute(self):
- """minute (0-59)"""
- return self._minute
- @property
- def second(self):
- """second (0-59)"""
- return self._second
- @property
- def microsecond(self):
- """microsecond (0-999999)"""
- return self._microsecond
- @property
- def tzinfo(self):
- """timezone info object"""
- return self._tzinfo
- @classmethod
- def fromtimestamp(cls, t, tz=None):
- """Construct a datetime from a POSIX timestamp (like time.time()).
- A timezone info object may be passed in as well.
- """
- _check_tzinfo_arg(tz)
- converter = _time.localtime if tz is None else _time.gmtime
- t, frac = divmod(t, 1.0)
- us = int(frac * 1e6)
- # If timestamp is less than one microsecond smaller than a
- # full second, us can be rounded up to 1000000. In this case,
- # roll over to seconds, otherwise, ValueError is raised
- # by the constructor.
- if us == 1000000:
- t += 1
- us = 0
- y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
- ss = min(ss, 59) # clamp out leap seconds if the platform has them
- result = cls(y, m, d, hh, mm, ss, us, tz)
- if tz is not None:
- result = tz.fromutc(result)
- return result
- @classmethod
- def utcfromtimestamp(cls, t):
- "Construct a UTC datetime from a POSIX timestamp (like time.time())."
- t, frac = divmod(t, 1.0)
- us = int(frac * 1e6)
- # If timestamp is less than one microsecond smaller than a
- # full second, us can be rounded up to 1000000. In this case,
- # roll over to seconds, otherwise, ValueError is raised
- # by the constructor.
- if us == 1000000:
- t += 1
- us = 0
- y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
- ss = min(ss, 59) # clamp out leap seconds if the platform has them
- return cls(y, m, d, hh, mm, ss, us)
- # XXX This is supposed to do better than we *can* do by using time.time(),
- # XXX if the platform supports a more accurate way. The C implementation
- # XXX uses gettimeofday on platforms that have it, but that isn't
- # XXX available from Python. So now() may return different results
- # XXX across the implementations.
- @classmethod
- def now(cls, tz=None):
- "Construct a datetime from time.time() and optional time zone info."
- t = _time.time()
- return cls.fromtimestamp(t, tz)
- @classmethod
- def utcnow(cls):
- "Construct a UTC datetime from time.time()."
- t = _time.time()
- return cls.utcfromtimestamp(t)
- @classmethod
- def combine(cls, date, time):
- "Construct a datetime from a given date and a given time."
- if not isinstance(date, _date_class):
- raise TypeError("date argument must be a date instance")
- if not isinstance(time, _time_class):
- raise TypeError("time argument must be a time instance")
- return cls(date.year, date.month, date.day,
- time.hour, time.minute, time.second, time.microsecond,
- time.tzinfo)
- def timetuple(self):
- "Return local time tuple compatible with time.localtime()."
- dst = self.dst()
- if dst is None:
- dst = -1
- elif dst:
- dst = 1
- else:
- dst = 0
- return _build_struct_time(self.year, self.month, self.day,
- self.hour, self.minute, self.second,
- dst)
- def timestamp(self):
- "Return POSIX timestamp as float"
- if self._tzinfo is None:
- return _time.mktime((self.year, self.month, self.day,
- self.hour, self.minute, self.second,
- -1, -1, -1)) + self.microsecond / 1e6
- else:
- return (self - _EPOCH).total_seconds()
- def utctimetuple(self):
- "Return UTC time tuple compatible with time.gmtime()."
- offset = self.utcoffset()
- if offset:
- self -= offset
- y, m, d = self.year, self.month, self.day
- hh, mm, ss = self.hour, self.minute, self.second
- return _build_struct_time(y, m, d, hh, mm, ss, 0)
- def date(self):
- "Return the date part."
- return date(self._year, self._month, self._day)
- def time(self):
- "Return the time part, with tzinfo None."
- return time(self.hour, self.minute, self.second, self.microsecond)
- def timetz(self):
- "Return the time part, with same tzinfo."
- return time(self.hour, self.minute, self.second, self.microsecond,
- self._tzinfo)
- def replace(self, year=None, month=None, day=None, hour=None,
- minute=None, second=None, microsecond=None, tzinfo=True):
- """Return a new datetime with new values for the specified fields."""
- if year is None:
- year = self.year
- if month is None:
- month = self.month
- if day is None:
- day = self.day
- if hour is None:
- hour = self.hour
- if minute is None:
- minute = self.minute
- if second is None:
- second = self.second
- if microsecond is None:
- microsecond = self.microsecond
- if tzinfo is True:
- tzinfo = self.tzinfo
- _check_date_fields(year, month, day)
- _check_time_fields(hour, minute, second, microsecond)
- _check_tzinfo_arg(tzinfo)
- return datetime(year, month, day, hour, minute, second,
- microsecond, tzinfo)
- def astimezone(self, tz=None):
- if tz is None:
- if self.tzinfo is None:
- raise ValueError("astimezone() requires an aware datetime")
- ts = (self - _EPOCH) // timedelta(seconds=1)
- localtm = _time.localtime(ts)
- local = datetime(*localtm[:6])
- try:
- # Extract TZ data if available
- gmtoff = localtm.tm_gmtoff
- zone = localtm.tm_zone
- except AttributeError:
- # Compute UTC offset and compare with the value implied
- # by tm_isdst. If the values match, use the zone name
- # implied by tm_isdst.
- delta = local - datetime(*_time.gmtime(ts)[:6])
- dst = _time.daylight and localtm.tm_isdst > 0
- gmtoff = -(_time.altzone if dst else _time.timezone)
- if delta == timedelta(seconds=gmtoff):
- tz = timezone(delta, _time.tzname[dst])
- else:
- tz = timezone(delta)
- else:
- tz = timezone(timedelta(seconds=gmtoff), zone)
- elif not isinstance(tz, tzinfo):
- raise TypeError("tz argument must be an instance of tzinfo")
- mytz = self.tzinfo
- if mytz is None:
- raise ValueError("astimezone() requires an aware datetime")
- if tz is mytz:
- return self
- # Convert self to UTC, and attach the new time zone object.
- myoffset = self.utcoffset()
- if myoffset is None:
- raise ValueError("astimezone() requires an aware datetime")
- utc = (self - myoffset).replace(tzinfo=tz)
- # Convert from UTC to tz's local time.
- return tz.fromutc(utc)
- # Ways to produce a string.
- def ctime(self):
- "Return ctime() style string."
- weekday = self.toordinal() % 7 or 7
- return "%s %s %2d %02d:%02d:%02d %04d" % (
- _DAYNAMES[weekday],
- _MONTHNAMES[self._month],
- self._day,
- self._hour, self._minute, self._second,
- self._year)
- def isoformat(self, sep='T'):
- """Return the time formatted according to ISO.
- This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if
- self.microsecond == 0.
- If self.tzinfo is not None, the UTC offset is also attached, giving
- 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'.
- Optional argument sep specifies the separator between date and
- time, default 'T'.
- """
- s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day,
- sep) +
- _format_time(self._hour, self._minute, self._second,
- self._microsecond))
- off = self.utcoffset()
- if off is not None:
- if off.days < 0:
- sign = "-"
- off = -off
- else:
- sign = "+"
- hh, mm = divmod(off, timedelta(hours=1))
- assert not mm % timedelta(minutes=1), "whole minute"
- mm //= timedelta(minutes=1)
- s += "%s%02d:%02d" % (sign, hh, mm)
- return s
- def __repr__(self):
- """Convert to formal string, for repr()."""
- L = [self._year, self._month, self._day, # These are never zero
- self._hour, self._minute, self._second, self._microsecond]
- if L[-1] == 0:
- del L[-1]
- if L[-1] == 0:
- del L[-1]
- s = ", ".join(map(str, L))
- s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
- if self._tzinfo is not None:
- assert s[-1:] == ")"
- s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
- return s
- def __str__(self):
- "Convert to string, for str()."
- return self.isoformat(sep=' ')
- @classmethod
- def strptime(cls, date_string, format):
- 'string, format -> new datetime parsed from a string (like time.strptime()).'
- import _strptime
- return _strptime._strptime_datetime(cls, date_string, format)
- def utcoffset(self):
- """Return the timezone offset in minutes east of UTC (negative west of
- UTC)."""
- if self._tzinfo is None:
- return None
- offset = self._tzinfo.utcoffset(self)
- _check_utc_offset("utcoffset", offset)
- return offset
- def tzname(self):
- """Return the timezone name.
- Note that the name is 100% informational -- there's no requirement that
- it mean anything in particular. For example, "GMT", "UTC", "-500",
- "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
- """
- name = _call_tzinfo_method(self._tzinfo, "tzname", self)
- _check_tzname(name)
- return name
- def dst(self):
- """Return 0 if DST is not in effect, or the DST offset (in minutes
- eastward) if DST is in effect.
- This is purely informational; the DST offset has already been added to
- the UTC offset returned by utcoffset() if applicable, so there's no
- need to consult dst() unless you're interested in displaying the DST
- info.
- """
- if self._tzinfo is None:
- return None
- offset = self._tzinfo.dst(self)
- _check_utc_offset("dst", offset)
- return offset
- # Comparisons of datetime objects with other.
- def __eq__(self, other):
- if isinstance(other, datetime):
- return self._cmp(other, allow_mixed=True) == 0
- elif not isinstance(other, date):
- return NotImplemented
- else:
- return False
- def __ne__(self, other):
- if isinstance(other, datetime):
- return self._cmp(other, allow_mixed=True) != 0
- elif not isinstance(other, date):
- return NotImplemented
- else:
- return True
- def __le__(self, other):
- if isinstance(other, datetime):
- return self._cmp(other) <= 0
- elif not isinstance(other, date):
- return NotImplemented
- else:
- _cmperror(self, other)
- def __lt__(self, other):
- if isinstance(other, datetime):
- return self._cmp(other) < 0
- elif not isinstance(other, date):
- return NotImplemented
- else:
- _cmperror(self, other)
- def __ge__(self, other):
- if isinstance(other, datetime):
- return self._cmp(other) >= 0
- elif not isinstance(other, date):
- return NotImplemented
- else:
- _cmperror(self, other)
- def __gt__(self, other):
- if isinstance(other, datetime):
- return self._cmp(other) > 0
- elif not isinstance(other, date):
- return NotImplemented
- else:
- _cmperror(self, other)
- def _cmp(self, other, allow_mixed=False):
- assert isinstance(other, datetime)
- mytz = self._tzinfo
- ottz = other._tzinfo
- myoff = otoff = None
- if mytz is ottz:
- base_compare = True
- else:
- myoff = self.utcoffset()
- otoff = other.utcoffset()
- base_compare = myoff == otoff
- if base_compare:
- return _cmp((self._year, self._month, self._day,
- self._hour, self._minute, self._second,
- self._microsecond),
- (other._year, other._month, other._day,
- other._hour, other._minute, other._second,
- other._microsecond))
- if myoff is None or otoff is None:
- if allow_mixed:
- return 2 # arbitrary non-zero value
- else:
- raise TypeError("cannot compare naive and aware datetimes")
- # XXX What follows could be done more efficiently...
- diff = self - other # this will take offsets into account
- if diff.days < 0:
- return -1
- return diff and 1 or 0
- def __add__(self, other):
- "Add a datetime and a timedelta."
- if not isinstance(other, timedelta):
- return NotImplemented
- delta = timedelta(self.toordinal(),
- hours=self._hour,
- minutes=self._minute,
- seconds=self._second,
- microseconds=self._microsecond)
- delta += other
- hour, rem = divmod(delta.seconds, 3600)
- minute, second = divmod(rem, 60)
- if 0 < delta.days <= _MAXORDINAL:
- return datetime.combine(date.fromordinal(delta.days),
- time(hour, minute, second,
- delta.microseconds,
- tzinfo=self._tzinfo))
- raise OverflowError("result out of range")
- __radd__ = __add__
- def __sub__(self, other):
- "Subtract two datetimes, or a datetime and a timedelta."
- if not isinstance(other, datetime):
- if isinstance(other, timedelta):
- return self + -other
- return NotImplemented
- days1 = self.toordinal()
- days2 = other.toordinal()
- secs1 = self._second + self._minute * 60 + self._hour * 3600
- secs2 = other._second + other._minute * 60 + other._hour * 3600
- base = timedelta(days1 - days2,
- secs1 - secs2,
- self._microsecond - other._microsecond)
- if self._tzinfo is other._tzinfo:
- return base
- myoff = self.utcoffset()
- otoff = other.utcoffset()
- if myoff == otoff:
- return base
- if myoff is None or otoff is None:
- raise TypeError("cannot mix naive and timezone-aware time")
- return base + otoff - myoff
- def __hash__(self):
- tzoff = self.utcoffset()
- if tzoff is None:
- return hash(self._getstate()[0])
- days = _ymd2ord(self.year, self.month, self.day)
- seconds = self.hour * 3600 + self.minute * 60 + self.second
- return hash(timedelta(days, seconds, self.microsecond) - tzoff)
- # Pickle support.
- def _getstate(self):
- yhi, ylo = divmod(self._year, 256)
- us2, us3 = divmod(self._microsecond, 256)
- us1, us2 = divmod(us2, 256)
- basestate = bytes([yhi, ylo, self._month, self._day,
- self._hour, self._minute, self._second,
- us1, us2, us3])
- if self._tzinfo is None:
- return (basestate,)
- else:
- return (basestate, self._tzinfo)
- def __setstate(self, string, tzinfo):
- (yhi, ylo, self._month, self._day, self._hour,
- self._minute, self._second, us1, us2, us3) = string
- self._year = yhi * 256 + ylo
- self._microsecond = (((us1 << 8) | us2) << 8) | us3
- if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
- self._tzinfo = tzinfo
- else:
- raise TypeError("bad tzinfo state arg %r" % tzinfo)
- def __reduce__(self):
- return (self.__class__, self._getstate())
- datetime.min = datetime(1, 1, 1)
- datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
- datetime.resolution = timedelta(microseconds=1)
- def _isoweek1monday(year):
- # Helper to calculate the day number of the Monday starting week 1
- # XXX This could be done more efficiently
- THURSDAY = 3
- firstday = _ymd2ord(year, 1, 1)
- firstweekday = (firstday + 6) % 7 # See weekday() above
- week1monday = firstday - firstweekday
- if firstweekday > THURSDAY:
- week1monday += 7
- return week1monday
- class timezone(tzinfo):
- __slots__ = '_offset', '_name'
- # Sentinel value to disallow None
- _Omitted = object()
- def __new__(cls, offset, name=_Omitted):
- if not isinstance(offset, timedelta):
- raise TypeError("offset must be a timedelta")
- if name is cls._Omitted:
- if not offset:
- return cls.utc
- name = None
- elif not isinstance(name, str):
- ###
- # For Python-Future:
- if PY2 and isinstance(name, native_str):
- name = name.decode()
- else:
- raise TypeError("name must be a string")
- ###
- if not cls._minoffset <= offset <= cls._maxoffset:
- raise ValueError("offset must be a timedelta"
- " strictly between -timedelta(hours=24) and"
- " timedelta(hours=24).")
- if (offset.microseconds != 0 or
- offset.seconds % 60 != 0):
- raise ValueError("offset must be a timedelta"
- " representing a whole number of minutes")
- return cls._create(offset, name)
- @classmethod
- def _create(cls, offset, name=None):
- self = tzinfo.__new__(cls)
- self._offset = offset
- self._name = name
- return self
- def __getinitargs__(self):
- """pickle support"""
- if self._name is None:
- return (self._offset,)
- return (self._offset, self._name)
- def __eq__(self, other):
- if type(other) != timezone:
- return False
- return self._offset == other._offset
- def __hash__(self):
- return hash(self._offset)
- def __repr__(self):
- """Convert to formal string, for repr().
- >>> tz = timezone.utc
- >>> repr(tz)
- 'datetime.timezone.utc'
- >>> tz = timezone(timedelta(hours=-5), 'EST')
- >>> repr(tz)
- "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
- """
- if self is self.utc:
- return 'datetime.timezone.utc'
- if self._name is None:
- return "%s(%r)" % ('datetime.' + self.__class__.__name__,
- self._offset)
- return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__,
- self._offset, self._name)
- def __str__(self):
- return self.tzname(None)
- def utcoffset(self, dt):
- if isinstance(dt, datetime) or dt is None:
- return self._offset
- raise TypeError("utcoffset() argument must be a datetime instance"
- " or None")
- def tzname(self, dt):
- if isinstance(dt, datetime) or dt is None:
- if self._name is None:
- return self._name_from_offset(self._offset)
- return self._name
- raise TypeError("tzname() argument must be a datetime instance"
- " or None")
- def dst(self, dt):
- if isinstance(dt, datetime) or dt is None:
- return None
- raise TypeError("dst() argument must be a datetime instance"
- " or None")
- def fromutc(self, dt):
- if isinstance(dt, datetime):
- if dt.tzinfo is not self:
- raise ValueError("fromutc: dt.tzinfo "
- "is not self")
- return dt + self._offset
- raise TypeError("fromutc() argument must be a datetime instance"
- " or None")
- _maxoffset = timedelta(hours=23, minutes=59)
- _minoffset = -_maxoffset
- @staticmethod
- def _name_from_offset(delta):
- if delta < timedelta(0):
- sign = '-'
- delta = -delta
- else:
- sign = '+'
- hours, rest = divmod(delta, timedelta(hours=1))
- minutes = rest // timedelta(minutes=1)
- return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
- timezone.utc = timezone._create(timedelta(0))
- timezone.min = timezone._create(timezone._minoffset)
- timezone.max = timezone._create(timezone._maxoffset)
- _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
- """
- Some time zone algebra. For a datetime x, let
- x.n = x stripped of its timezone -- its naive time.
- x.o = x.utcoffset(), and assuming that doesn't raise an exception or
- return None
- x.d = x.dst(), and assuming that doesn't raise an exception or
- return None
- x.s = x's standard offset, x.o - x.d
- Now some derived rules, where k is a duration (timedelta).
- 1. x.o = x.s + x.d
- This follows from the definition of x.s.
- 2. If x and y have the same tzinfo member, x.s = y.s.
- This is actually a requirement, an assumption we need to make about
- sane tzinfo classes.
- 3. The naive UTC time corresponding to x is x.n - x.o.
- This is again a requirement for a sane tzinfo class.
- 4. (x+k).s = x.s
- This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
- 5. (x+k).n = x.n + k
- Again follows from how arithmetic is defined.
- Now we can explain tz.fromutc(x). Let's assume it's an interesting case
- (meaning that the various tzinfo methods exist, and don't blow up or return
- None when called).
- The function wants to return a datetime y with timezone tz, equivalent to x.
- x is already in UTC.
- By #3, we want
- y.n - y.o = x.n [1]
- The algorithm starts by attaching tz to x.n, and calling that y. So
- x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
- becomes true; in effect, we want to solve [2] for k:
- (y+k).n - (y+k).o = x.n [2]
- By #1, this is the same as
- (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
- By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
- Substituting that into [3],
- x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
- k - (y+k).s - (y+k).d = 0; rearranging,
- k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
- k = y.s - (y+k).d
- On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
- approximate k by ignoring the (y+k).d term at first. Note that k can't be
- very large, since all offset-returning methods return a duration of magnitude
- less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
- be 0, so ignoring it has no consequence then.
- In any case, the new value is
- z = y + y.s [4]
- It's helpful to step back at look at [4] from a higher level: it's simply
- mapping from UTC to tz's standard time.
- At this point, if
- z.n - z.o = x.n [5]
- we have an equivalent time, and are almost done. The insecurity here is
- at the start of daylight time. Picture US Eastern for concreteness. The wall
- time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
- sense then. The docs ask that an Eastern tzinfo class consider such a time to
- be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
- on the day DST starts. We want to return the 1:MM EST spelling because that's
- the only spelling that makes sense on the local wall clock.
- In fact, if [5] holds at this point, we do have the standard-time spelling,
- but that takes a bit of proof. We first prove a stronger result. What's the
- difference between the LHS and RHS of [5]? Let
- diff = x.n - (z.n - z.o) [6]
- Now
- z.n = by [4]
- (y + y.s).n = by #5
- y.n + y.s = since y.n = x.n
- x.n + y.s = since z and y are have the same tzinfo member,
- y.s = z.s by #2
- x.n + z.s
- Plugging that back into [6] gives
- diff =
- x.n - ((x.n + z.s) - z.o) = expanding
- x.n - x.n - z.s + z.o = cancelling
- - z.s + z.o = by #2
- z.d
- So diff = z.d.
- If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
- spelling we wanted in the endcase described above. We're done. Contrarily,
- if z.d = 0, then we have a UTC equivalent, and are also done.
- If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
- add to z (in effect, z is in tz's standard time, and we need to shift the
- local clock into tz's daylight time).
- Let
- z' = z + z.d = z + diff [7]
- and we can again ask whether
- z'.n - z'.o = x.n [8]
- If so, we're done. If not, the tzinfo class is insane, according to the
- assumptions we've made. This also requires a bit of proof. As before, let's
- compute the difference between the LHS and RHS of [8] (and skipping some of
- the justifications for the kinds of substitutions we've done several times
- already):
- diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
- x.n - (z.n + diff - z'.o) = replacing diff via [6]
- x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
- x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
- - z.n + z.n - z.o + z'.o = cancel z.n
- - z.o + z'.o = #1 twice
- -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
- z'.d - z.d
- So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
- we've found the UTC-equivalent so are done. In fact, we stop with [7] and
- return z', not bothering to compute z'.d.
- How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
- a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
- would have to change the result dst() returns: we start in DST, and moving
- a little further into it takes us out of DST.
- There isn't a sane case where this can happen. The closest it gets is at
- the end of DST, where there's an hour in UTC with no spelling in a hybrid
- tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
- that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
- UTC) because the docs insist on that, but 0:MM is taken as being in daylight
- time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
- clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
- standard time. Since that's what the local clock *does*, we want to map both
- UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
- in local time, but so it goes -- it's the way the local clock works.
- When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
- so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
- z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
- (correctly) concludes that z' is not UTC-equivalent to x.
- Because we know z.d said z was in daylight time (else [5] would have held and
- we would have stopped then), and we know z.d != z'.d (else [8] would have held
- and we have stopped then), and there are only 2 possible values dst() can
- return in Eastern, it follows that z'.d must be 0 (which it is in the example,
- but the reasoning doesn't depend on the example -- it depends on there being
- two possible dst() outcomes, one zero and the other non-zero). Therefore
- z' must be in standard time, and is the spelling we want in this case.
- Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
- concerned (because it takes z' as being in standard time rather than the
- daylight time we intend here), but returning it gives the real-life "local
- clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
- tz.
- When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
- the 1:MM standard time spelling we want.
- So how can this break? One of the assumptions must be violated. Two
- possibilities:
- 1) [2] effectively says that y.s is invariant across all y belong to a given
- time zone. This isn't true if, for political reasons or continental drift,
- a region decides to change its base offset from UTC.
- 2) There may be versions of "double daylight" time where the tail end of
- the analysis gives up a step too early. I haven't thought about that
- enough to say.
- In any case, it's clear that the default fromutc() is strong enough to handle
- "almost all" time zones: so long as the standard offset is invariant, it
- doesn't matter if daylight time transition points change from year to year, or
- if daylight time is skipped in some years; it doesn't matter how large or
- small dst() may get within its bounds; and it doesn't even matter if some
- perverse time zone returns a negative dst()). So a breaking case must be
- pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
- """
- try:
- from _datetime import *
- except ImportError:
- pass
- else:
- # Clean up unused names
- del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH,
- _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES,
- _build_struct_time, _call_tzinfo_method, _check_date_fields,
- _check_time_fields, _check_tzinfo_arg, _check_tzname,
- _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month,
- _days_before_year, _days_in_month, _format_time, _is_leap,
- _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class,
- _wrap_strftime, _ymd2ord)
- # XXX Since import * above excludes names that start with _,
- # docstring does not get overwritten. In the future, it may be
- # appropriate to maintain a single module level docstring and
- # remove the following line.
- from _datetime import __doc__
|