Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/dateutil/relativedelta.py
7794 views
# -*- coding: utf-8 -*-1import datetime2import calendar34import operator5from math import copysign67from six import integer_types8from warnings import warn910from ._common import weekday1112MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))1314__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]151617class relativedelta(object):18"""19The relativedelta type is designed to be applied to an existing datetime and20can replace specific components of that datetime, or represents an interval21of time.2223It is based on the specification of the excellent work done by M.-A. Lemburg24in his25`mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.26However, notice that this type does *NOT* implement the same algorithm as27his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.2829There are two different ways to build a relativedelta instance. The30first one is passing it two date/datetime classes::3132relativedelta(datetime1, datetime2)3334The second one is passing it any number of the following keyword arguments::3536relativedelta(arg1=x,arg2=y,arg3=z...)3738year, month, day, hour, minute, second, microsecond:39Absolute information (argument is singular); adding or subtracting a40relativedelta with absolute information does not perform an arithmetic41operation, but rather REPLACES the corresponding value in the42original datetime with the value(s) in relativedelta.4344years, months, weeks, days, hours, minutes, seconds, microseconds:45Relative information, may be negative (argument is plural); adding46or subtracting a relativedelta with relative information performs47the corresponding arithmetic operation on the original datetime value48with the information in the relativedelta.4950weekday:51One of the weekday instances (MO, TU, etc) available in the52relativedelta module. These instances may receive a parameter N,53specifying the Nth weekday, which could be positive or negative54(like MO(+1) or MO(-2)). Not specifying it is the same as specifying55+1. You can also use an integer, where 0=MO. This argument is always56relative e.g. if the calculated date is already Monday, using MO(1)57or MO(-1) won't change the day. To effectively make it absolute, use58it in combination with the day argument (e.g. day=1, MO(1) for first59Monday of the month).6061leapdays:62Will add given days to the date found, if year is a leap63year, and the date found is post 28 of february.6465yearday, nlyearday:66Set the yearday or the non-leap year day (jump leap days).67These are converted to day/month/leapdays information.6869There are relative and absolute forms of the keyword70arguments. The plural is relative, and the singular is71absolute. For each argument in the order below, the absolute form72is applied first (by setting each attribute to that value) and73then the relative form (by adding the value to the attribute).7475The order of attributes considered when this relativedelta is76added to a datetime is:77781. Year792. Month803. Day814. Hours825. Minutes836. Seconds847. Microseconds8586Finally, weekday is applied, using the rule described above.8788For example8990>>> from datetime import datetime91>>> from dateutil.relativedelta import relativedelta, MO92>>> dt = datetime(2018, 4, 9, 13, 37, 0)93>>> delta = relativedelta(hours=25, day=1, weekday=MO(1))94>>> dt + delta95datetime.datetime(2018, 4, 2, 14, 37)9697First, the day is set to 1 (the first of the month), then 25 hours98are added, to get to the 2nd day and 14th hour, finally the99weekday is applied, but since the 2nd is already a Monday there is100no effect.101102"""103104def __init__(self, dt1=None, dt2=None,105years=0, months=0, days=0, leapdays=0, weeks=0,106hours=0, minutes=0, seconds=0, microseconds=0,107year=None, month=None, day=None, weekday=None,108yearday=None, nlyearday=None,109hour=None, minute=None, second=None, microsecond=None):110111if dt1 and dt2:112# datetime is a subclass of date. So both must be date113if not (isinstance(dt1, datetime.date) and114isinstance(dt2, datetime.date)):115raise TypeError("relativedelta only diffs datetime/date")116117# We allow two dates, or two datetimes, so we coerce them to be118# of the same type119if (isinstance(dt1, datetime.datetime) !=120isinstance(dt2, datetime.datetime)):121if not isinstance(dt1, datetime.datetime):122dt1 = datetime.datetime.fromordinal(dt1.toordinal())123elif not isinstance(dt2, datetime.datetime):124dt2 = datetime.datetime.fromordinal(dt2.toordinal())125126self.years = 0127self.months = 0128self.days = 0129self.leapdays = 0130self.hours = 0131self.minutes = 0132self.seconds = 0133self.microseconds = 0134self.year = None135self.month = None136self.day = None137self.weekday = None138self.hour = None139self.minute = None140self.second = None141self.microsecond = None142self._has_time = 0143144# Get year / month delta between the two145months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month)146self._set_months(months)147148# Remove the year/month delta so the timedelta is just well-defined149# time units (seconds, days and microseconds)150dtm = self.__radd__(dt2)151152# If we've overshot our target, make an adjustment153if dt1 < dt2:154compare = operator.gt155increment = 1156else:157compare = operator.lt158increment = -1159160while compare(dt1, dtm):161months += increment162self._set_months(months)163dtm = self.__radd__(dt2)164165# Get the timedelta between the "months-adjusted" date and dt1166delta = dt1 - dtm167self.seconds = delta.seconds + delta.days * 86400168self.microseconds = delta.microseconds169else:170# Check for non-integer values in integer-only quantities171if any(x is not None and x != int(x) for x in (years, months)):172raise ValueError("Non-integer years and months are "173"ambiguous and not currently supported.")174175# Relative information176self.years = int(years)177self.months = int(months)178self.days = days + weeks * 7179self.leapdays = leapdays180self.hours = hours181self.minutes = minutes182self.seconds = seconds183self.microseconds = microseconds184185# Absolute information186self.year = year187self.month = month188self.day = day189self.hour = hour190self.minute = minute191self.second = second192self.microsecond = microsecond193194if any(x is not None and int(x) != x195for x in (year, month, day, hour,196minute, second, microsecond)):197# For now we'll deprecate floats - later it'll be an error.198warn("Non-integer value passed as absolute information. " +199"This is not a well-defined condition and will raise " +200"errors in future versions.", DeprecationWarning)201202if isinstance(weekday, integer_types):203self.weekday = weekdays[weekday]204else:205self.weekday = weekday206207yday = 0208if nlyearday:209yday = nlyearday210elif yearday:211yday = yearday212if yearday > 59:213self.leapdays = -1214if yday:215ydayidx = [31, 59, 90, 120, 151, 181, 212,216243, 273, 304, 334, 366]217for idx, ydays in enumerate(ydayidx):218if yday <= ydays:219self.month = idx+1220if idx == 0:221self.day = yday222else:223self.day = yday-ydayidx[idx-1]224break225else:226raise ValueError("invalid year day (%d)" % yday)227228self._fix()229230def _fix(self):231if abs(self.microseconds) > 999999:232s = _sign(self.microseconds)233div, mod = divmod(self.microseconds * s, 1000000)234self.microseconds = mod * s235self.seconds += div * s236if abs(self.seconds) > 59:237s = _sign(self.seconds)238div, mod = divmod(self.seconds * s, 60)239self.seconds = mod * s240self.minutes += div * s241if abs(self.minutes) > 59:242s = _sign(self.minutes)243div, mod = divmod(self.minutes * s, 60)244self.minutes = mod * s245self.hours += div * s246if abs(self.hours) > 23:247s = _sign(self.hours)248div, mod = divmod(self.hours * s, 24)249self.hours = mod * s250self.days += div * s251if abs(self.months) > 11:252s = _sign(self.months)253div, mod = divmod(self.months * s, 12)254self.months = mod * s255self.years += div * s256if (self.hours or self.minutes or self.seconds or self.microseconds257or self.hour is not None or self.minute is not None or258self.second is not None or self.microsecond is not None):259self._has_time = 1260else:261self._has_time = 0262263@property264def weeks(self):265return int(self.days / 7.0)266267@weeks.setter268def weeks(self, value):269self.days = self.days - (self.weeks * 7) + value * 7270271def _set_months(self, months):272self.months = months273if abs(self.months) > 11:274s = _sign(self.months)275div, mod = divmod(self.months * s, 12)276self.months = mod * s277self.years = div * s278else:279self.years = 0280281def normalized(self):282"""283Return a version of this object represented entirely using integer284values for the relative attributes.285286>>> relativedelta(days=1.5, hours=2).normalized()287relativedelta(days=+1, hours=+14)288289:return:290Returns a :class:`dateutil.relativedelta.relativedelta` object.291"""292# Cascade remainders down (rounding each to roughly nearest microsecond)293days = int(self.days)294295hours_f = round(self.hours + 24 * (self.days - days), 11)296hours = int(hours_f)297298minutes_f = round(self.minutes + 60 * (hours_f - hours), 10)299minutes = int(minutes_f)300301seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8)302seconds = int(seconds_f)303304microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds))305306# Constructor carries overflow back up with call to _fix()307return self.__class__(years=self.years, months=self.months,308days=days, hours=hours, minutes=minutes,309seconds=seconds, microseconds=microseconds,310leapdays=self.leapdays, year=self.year,311month=self.month, day=self.day,312weekday=self.weekday, hour=self.hour,313minute=self.minute, second=self.second,314microsecond=self.microsecond)315316def __add__(self, other):317if isinstance(other, relativedelta):318return self.__class__(years=other.years + self.years,319months=other.months + self.months,320days=other.days + self.days,321hours=other.hours + self.hours,322minutes=other.minutes + self.minutes,323seconds=other.seconds + self.seconds,324microseconds=(other.microseconds +325self.microseconds),326leapdays=other.leapdays or self.leapdays,327year=(other.year if other.year is not None328else self.year),329month=(other.month if other.month is not None330else self.month),331day=(other.day if other.day is not None332else self.day),333weekday=(other.weekday if other.weekday is not None334else self.weekday),335hour=(other.hour if other.hour is not None336else self.hour),337minute=(other.minute if other.minute is not None338else self.minute),339second=(other.second if other.second is not None340else self.second),341microsecond=(other.microsecond if other.microsecond342is not None else343self.microsecond))344if isinstance(other, datetime.timedelta):345return self.__class__(years=self.years,346months=self.months,347days=self.days + other.days,348hours=self.hours,349minutes=self.minutes,350seconds=self.seconds + other.seconds,351microseconds=self.microseconds + other.microseconds,352leapdays=self.leapdays,353year=self.year,354month=self.month,355day=self.day,356weekday=self.weekday,357hour=self.hour,358minute=self.minute,359second=self.second,360microsecond=self.microsecond)361if not isinstance(other, datetime.date):362return NotImplemented363elif self._has_time and not isinstance(other, datetime.datetime):364other = datetime.datetime.fromordinal(other.toordinal())365year = (self.year or other.year)+self.years366month = self.month or other.month367if self.months:368assert 1 <= abs(self.months) <= 12369month += self.months370if month > 12:371year += 1372month -= 12373elif month < 1:374year -= 1375month += 12376day = min(calendar.monthrange(year, month)[1],377self.day or other.day)378repl = {"year": year, "month": month, "day": day}379for attr in ["hour", "minute", "second", "microsecond"]:380value = getattr(self, attr)381if value is not None:382repl[attr] = value383days = self.days384if self.leapdays and month > 2 and calendar.isleap(year):385days += self.leapdays386ret = (other.replace(**repl)387+ datetime.timedelta(days=days,388hours=self.hours,389minutes=self.minutes,390seconds=self.seconds,391microseconds=self.microseconds))392if self.weekday:393weekday, nth = self.weekday.weekday, self.weekday.n or 1394jumpdays = (abs(nth) - 1) * 7395if nth > 0:396jumpdays += (7 - ret.weekday() + weekday) % 7397else:398jumpdays += (ret.weekday() - weekday) % 7399jumpdays *= -1400ret += datetime.timedelta(days=jumpdays)401return ret402403def __radd__(self, other):404return self.__add__(other)405406def __rsub__(self, other):407return self.__neg__().__radd__(other)408409def __sub__(self, other):410if not isinstance(other, relativedelta):411return NotImplemented # In case the other object defines __rsub__412return self.__class__(years=self.years - other.years,413months=self.months - other.months,414days=self.days - other.days,415hours=self.hours - other.hours,416minutes=self.minutes - other.minutes,417seconds=self.seconds - other.seconds,418microseconds=self.microseconds - other.microseconds,419leapdays=self.leapdays or other.leapdays,420year=(self.year if self.year is not None421else other.year),422month=(self.month if self.month is not None else423other.month),424day=(self.day if self.day is not None else425other.day),426weekday=(self.weekday if self.weekday is not None else427other.weekday),428hour=(self.hour if self.hour is not None else429other.hour),430minute=(self.minute if self.minute is not None else431other.minute),432second=(self.second if self.second is not None else433other.second),434microsecond=(self.microsecond if self.microsecond435is not None else436other.microsecond))437438def __abs__(self):439return self.__class__(years=abs(self.years),440months=abs(self.months),441days=abs(self.days),442hours=abs(self.hours),443minutes=abs(self.minutes),444seconds=abs(self.seconds),445microseconds=abs(self.microseconds),446leapdays=self.leapdays,447year=self.year,448month=self.month,449day=self.day,450weekday=self.weekday,451hour=self.hour,452minute=self.minute,453second=self.second,454microsecond=self.microsecond)455456def __neg__(self):457return self.__class__(years=-self.years,458months=-self.months,459days=-self.days,460hours=-self.hours,461minutes=-self.minutes,462seconds=-self.seconds,463microseconds=-self.microseconds,464leapdays=self.leapdays,465year=self.year,466month=self.month,467day=self.day,468weekday=self.weekday,469hour=self.hour,470minute=self.minute,471second=self.second,472microsecond=self.microsecond)473474def __bool__(self):475return not (not self.years and476not self.months and477not self.days and478not self.hours and479not self.minutes and480not self.seconds and481not self.microseconds and482not self.leapdays and483self.year is None and484self.month is None and485self.day is None and486self.weekday is None and487self.hour is None and488self.minute is None and489self.second is None and490self.microsecond is None)491# Compatibility with Python 2.x492__nonzero__ = __bool__493494def __mul__(self, other):495try:496f = float(other)497except TypeError:498return NotImplemented499500return self.__class__(years=int(self.years * f),501months=int(self.months * f),502days=int(self.days * f),503hours=int(self.hours * f),504minutes=int(self.minutes * f),505seconds=int(self.seconds * f),506microseconds=int(self.microseconds * f),507leapdays=self.leapdays,508year=self.year,509month=self.month,510day=self.day,511weekday=self.weekday,512hour=self.hour,513minute=self.minute,514second=self.second,515microsecond=self.microsecond)516517__rmul__ = __mul__518519def __eq__(self, other):520if not isinstance(other, relativedelta):521return NotImplemented522if self.weekday or other.weekday:523if not self.weekday or not other.weekday:524return False525if self.weekday.weekday != other.weekday.weekday:526return False527n1, n2 = self.weekday.n, other.weekday.n528if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)):529return False530return (self.years == other.years and531self.months == other.months and532self.days == other.days and533self.hours == other.hours and534self.minutes == other.minutes and535self.seconds == other.seconds and536self.microseconds == other.microseconds and537self.leapdays == other.leapdays and538self.year == other.year and539self.month == other.month and540self.day == other.day and541self.hour == other.hour and542self.minute == other.minute and543self.second == other.second and544self.microsecond == other.microsecond)545546def __hash__(self):547return hash((548self.weekday,549self.years,550self.months,551self.days,552self.hours,553self.minutes,554self.seconds,555self.microseconds,556self.leapdays,557self.year,558self.month,559self.day,560self.hour,561self.minute,562self.second,563self.microsecond,564))565566def __ne__(self, other):567return not self.__eq__(other)568569def __div__(self, other):570try:571reciprocal = 1 / float(other)572except TypeError:573return NotImplemented574575return self.__mul__(reciprocal)576577__truediv__ = __div__578579def __repr__(self):580l = []581for attr in ["years", "months", "days", "leapdays",582"hours", "minutes", "seconds", "microseconds"]:583value = getattr(self, attr)584if value:585l.append("{attr}={value:+g}".format(attr=attr, value=value))586for attr in ["year", "month", "day", "weekday",587"hour", "minute", "second", "microsecond"]:588value = getattr(self, attr)589if value is not None:590l.append("{attr}={value}".format(attr=attr, value=repr(value)))591return "{classname}({attrs})".format(classname=self.__class__.__name__,592attrs=", ".join(l))593594595def _sign(x):596return int(copysign(1, x))597598# vim:ts=4:sw=4:et599600601