Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
relativedelta.py600 linesDownload Raw Back to dateutil
1# -*- coding: utf-8 -*-2import datetime3import calendar4 5import operator6from math import copysign7 8from six import integer_types9from warnings import warn10 11from ._common import weekday12 13MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7))14 15__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]16 17 18class relativedelta(object):19    """20    The relativedelta type is designed to be applied to an existing datetime and21    can replace specific components of that datetime, or represents an interval22    of time.23 24    It is based on the specification of the excellent work done by M.-A. Lemburg25    in his26    `mx.DateTime <https://www.egenix.com/products/python/mxBase/mxDateTime/>`_ extension.27    However, notice that this type does *NOT* implement the same algorithm as28    his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.29 30    There are two different ways to build a relativedelta instance. The31    first one is passing it two date/datetime classes::32 33        relativedelta(datetime1, datetime2)34 35    The second one is passing it any number of the following keyword arguments::36 37        relativedelta(arg1=x,arg2=y,arg3=z...)38 39        year, month, day, hour, minute, second, microsecond:40            Absolute information (argument is singular); adding or subtracting a41            relativedelta with absolute information does not perform an arithmetic42            operation, but rather REPLACES the corresponding value in the43            original datetime with the value(s) in relativedelta.44 45        years, months, weeks, days, hours, minutes, seconds, microseconds:46            Relative information, may be negative (argument is plural); adding47            or subtracting a relativedelta with relative information performs48            the corresponding arithmetic operation on the original datetime value49            with the information in the relativedelta.50 51        weekday:52            One of the weekday instances (MO, TU, etc) available in the53            relativedelta module. These instances may receive a parameter N,54            specifying the Nth weekday, which could be positive or negative55            (like MO(+1) or MO(-2)). Not specifying it is the same as specifying56            +1. You can also use an integer, where 0=MO. This argument is always57            relative e.g. if the calculated date is already Monday, using MO(1)58            or MO(-1) won't change the day. To effectively make it absolute, use59            it in combination with the day argument (e.g. day=1, MO(1) for first60            Monday of the month).61 62        leapdays:63            Will add given days to the date found, if year is a leap64            year, and the date found is post 28 of february.65 66        yearday, nlyearday:67            Set the yearday or the non-leap year day (jump leap days).68            These are converted to day/month/leapdays information.69 70    There are relative and absolute forms of the keyword71    arguments. The plural is relative, and the singular is72    absolute. For each argument in the order below, the absolute form73    is applied first (by setting each attribute to that value) and74    then the relative form (by adding the value to the attribute).75 76    The order of attributes considered when this relativedelta is77    added to a datetime is:78 79    1. Year80    2. Month81    3. Day82    4. Hours83    5. Minutes84    6. Seconds85    7. Microseconds86 87    Finally, weekday is applied, using the rule described above.88 89    For example90 91    >>> from datetime import datetime92    >>> from dateutil.relativedelta import relativedelta, MO93    >>> dt = datetime(2018, 4, 9, 13, 37, 0)94    >>> delta = relativedelta(hours=25, day=1, weekday=MO(1))95    >>> dt + delta96    datetime.datetime(2018, 4, 2, 14, 37)97 98    First, the day is set to 1 (the first of the month), then 25 hours99    are added, to get to the 2nd day and 14th hour, finally the100    weekday is applied, but since the 2nd is already a Monday there is101    no effect.102 103    """104 105    def __init__(self, dt1=None, dt2=None,106                 years=0, months=0, days=0, leapdays=0, weeks=0,107                 hours=0, minutes=0, seconds=0, microseconds=0,108                 year=None, month=None, day=None, weekday=None,109                 yearday=None, nlyearday=None,110                 hour=None, minute=None, second=None, microsecond=None):111 112        if dt1 and dt2:113            # datetime is a subclass of date. So both must be date114            if not (isinstance(dt1, datetime.date) and115                    isinstance(dt2, datetime.date)):116                raise TypeError("relativedelta only diffs datetime/date")117 118            # We allow two dates, or two datetimes, so we coerce them to be119            # of the same type120            if (isinstance(dt1, datetime.datetime) !=121                    isinstance(dt2, datetime.datetime)):122                if not isinstance(dt1, datetime.datetime):123                    dt1 = datetime.datetime.fromordinal(dt1.toordinal())124                elif not isinstance(dt2, datetime.datetime):125                    dt2 = datetime.datetime.fromordinal(dt2.toordinal())126 127            self.years = 0128            self.months = 0129            self.days = 0130            self.leapdays = 0131            self.hours = 0132            self.minutes = 0133            self.seconds = 0134            self.microseconds = 0135            self.year = None136            self.month = None137            self.day = None138            self.weekday = None139            self.hour = None140            self.minute = None141            self.second = None142            self.microsecond = None143            self._has_time = 0144 145            # Get year / month delta between the two146            months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month)147            self._set_months(months)148 149            # Remove the year/month delta so the timedelta is just well-defined150            # time units (seconds, days and microseconds)151            dtm = self.__radd__(dt2)152 153            # If we've overshot our target, make an adjustment154            if dt1 < dt2:155                compare = operator.gt156                increment = 1157            else:158                compare = operator.lt159                increment = -1160 161            while compare(dt1, dtm):162                months += increment163                self._set_months(months)164                dtm = self.__radd__(dt2)165 166            # Get the timedelta between the "months-adjusted" date and dt1167            delta = dt1 - dtm168            self.seconds = delta.seconds + delta.days * 86400169            self.microseconds = delta.microseconds170        else:171            # Check for non-integer values in integer-only quantities172            if any(x is not None and x != int(x) for x in (years, months)):173                raise ValueError("Non-integer years and months are "174                                 "ambiguous and not currently supported.")175 176            # Relative information177            self.years = int(years)178            self.months = int(months)179            self.days = days + weeks * 7180            self.leapdays = leapdays181            self.hours = hours182            self.minutes = minutes183            self.seconds = seconds184            self.microseconds = microseconds185 186            # Absolute information187            self.year = year188            self.month = month189            self.day = day190            self.hour = hour191            self.minute = minute192            self.second = second193            self.microsecond = microsecond194 195            if any(x is not None and int(x) != x196                   for x in (year, month, day, hour,197                             minute, second, microsecond)):198                # For now we'll deprecate floats - later it'll be an error.199                warn("Non-integer value passed as absolute information. " +200                     "This is not a well-defined condition and will raise " +201                     "errors in future versions.", DeprecationWarning)202 203            if isinstance(weekday, integer_types):204                self.weekday = weekdays[weekday]205            else:206                self.weekday = weekday207 208            yday = 0209            if nlyearday:210                yday = nlyearday211            elif yearday:212                yday = yearday213                if yearday > 59:214                    self.leapdays = -1215            if yday:216                ydayidx = [31, 59, 90, 120, 151, 181, 212,217                           243, 273, 304, 334, 366]218                for idx, ydays in enumerate(ydayidx):219                    if yday <= ydays:220                        self.month = idx+1221                        if idx == 0:222                            self.day = yday223                        else:224                            self.day = yday-ydayidx[idx-1]225                        break226                else:227                    raise ValueError("invalid year day (%d)" % yday)228 229        self._fix()230 231    def _fix(self):232        if abs(self.microseconds) > 999999:233            s = _sign(self.microseconds)234            div, mod = divmod(self.microseconds * s, 1000000)235            self.microseconds = mod * s236            self.seconds += div * s237        if abs(self.seconds) > 59:238            s = _sign(self.seconds)239            div, mod = divmod(self.seconds * s, 60)240            self.seconds = mod * s241            self.minutes += div * s242        if abs(self.minutes) > 59:243            s = _sign(self.minutes)244            div, mod = divmod(self.minutes * s, 60)245            self.minutes = mod * s246            self.hours += div * s247        if abs(self.hours) > 23:248            s = _sign(self.hours)249            div, mod = divmod(self.hours * s, 24)250            self.hours = mod * s251            self.days += div * s252        if abs(self.months) > 11:253            s = _sign(self.months)254            div, mod = divmod(self.months * s, 12)255            self.months = mod * s256            self.years += div * s257        if (self.hours or self.minutes or self.seconds or self.microseconds258                or self.hour is not None or self.minute is not None or259                self.second is not None or self.microsecond is not None):260            self._has_time = 1261        else:262            self._has_time = 0263 264    @property265    def weeks(self):266        return int(self.days / 7.0)267 268    @weeks.setter269    def weeks(self, value):270        self.days = self.days - (self.weeks * 7) + value * 7271 272    def _set_months(self, months):273        self.months = months274        if abs(self.months) > 11:275            s = _sign(self.months)276            div, mod = divmod(self.months * s, 12)277            self.months = mod * s278            self.years = div * s279        else:280            self.years = 0281 282    def normalized(self):283        """284        Return a version of this object represented entirely using integer285        values for the relative attributes.286 287        >>> relativedelta(days=1.5, hours=2).normalized()288        relativedelta(days=+1, hours=+14)289 290        :return:291            Returns a :class:`dateutil.relativedelta.relativedelta` object.292        """293        # Cascade remainders down (rounding each to roughly nearest microsecond)294        days = int(self.days)295 296        hours_f = round(self.hours + 24 * (self.days - days), 11)297        hours = int(hours_f)298 299        minutes_f = round(self.minutes + 60 * (hours_f - hours), 10)300        minutes = int(minutes_f)301 302        seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8)303        seconds = int(seconds_f)304 305        microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds))306 307        # Constructor carries overflow back up with call to _fix()308        return self.__class__(years=self.years, months=self.months,309                              days=days, hours=hours, minutes=minutes,310                              seconds=seconds, microseconds=microseconds,311                              leapdays=self.leapdays, year=self.year,312                              month=self.month, day=self.day,313                              weekday=self.weekday, hour=self.hour,314                              minute=self.minute, second=self.second,315                              microsecond=self.microsecond)316 317    def __add__(self, other):318        if isinstance(other, relativedelta):319            return self.__class__(years=other.years + self.years,320                                 months=other.months + self.months,321                                 days=other.days + self.days,322                                 hours=other.hours + self.hours,323                                 minutes=other.minutes + self.minutes,324                                 seconds=other.seconds + self.seconds,325                                 microseconds=(other.microseconds +326                                               self.microseconds),327                                 leapdays=other.leapdays or self.leapdays,328                                 year=(other.year if other.year is not None329                                       else self.year),330                                 month=(other.month if other.month is not None331                                        else self.month),332                                 day=(other.day if other.day is not None333                                      else self.day),334                                 weekday=(other.weekday if other.weekday is not None335                                          else self.weekday),336                                 hour=(other.hour if other.hour is not None337                                       else self.hour),338                                 minute=(other.minute if other.minute is not None339                                         else self.minute),340                                 second=(other.second if other.second is not None341                                         else self.second),342                                 microsecond=(other.microsecond if other.microsecond343                                              is not None else344                                              self.microsecond))345        if isinstance(other, datetime.timedelta):346            return self.__class__(years=self.years,347                                  months=self.months,348                                  days=self.days + other.days,349                                  hours=self.hours,350                                  minutes=self.minutes,351                                  seconds=self.seconds + other.seconds,352                                  microseconds=self.microseconds + other.microseconds,353                                  leapdays=self.leapdays,354                                  year=self.year,355                                  month=self.month,356                                  day=self.day,357                                  weekday=self.weekday,358                                  hour=self.hour,359                                  minute=self.minute,360                                  second=self.second,361                                  microsecond=self.microsecond)362        if not isinstance(other, datetime.date):363            return NotImplemented364        elif self._has_time and not isinstance(other, datetime.datetime):365            other = datetime.datetime.fromordinal(other.toordinal())366        year = (self.year or other.year)+self.years367        month = self.month or other.month368        if self.months:369            assert 1 <= abs(self.months) <= 12370            month += self.months371            if month > 12:372                year += 1373                month -= 12374            elif month < 1:375                year -= 1376                month += 12377        day = min(calendar.monthrange(year, month)[1],378                  self.day or other.day)379        repl = {"year": year, "month": month, "day": day}380        for attr in ["hour", "minute", "second", "microsecond"]:381            value = getattr(self, attr)382            if value is not None:383                repl[attr] = value384        days = self.days385        if self.leapdays and month > 2 and calendar.isleap(year):386            days += self.leapdays387        ret = (other.replace(**repl)388               + datetime.timedelta(days=days,389                                    hours=self.hours,390                                    minutes=self.minutes,391                                    seconds=self.seconds,392                                    microseconds=self.microseconds))393        if self.weekday:394            weekday, nth = self.weekday.weekday, self.weekday.n or 1395            jumpdays = (abs(nth) - 1) * 7396            if nth > 0:397                jumpdays += (7 - ret.weekday() + weekday) % 7398            else:399                jumpdays += (ret.weekday() - weekday) % 7400                jumpdays *= -1401            ret += datetime.timedelta(days=jumpdays)402        return ret403 404    def __radd__(self, other):405        return self.__add__(other)406 407    def __rsub__(self, other):408        return self.__neg__().__radd__(other)409 410    def __sub__(self, other):411        if not isinstance(other, relativedelta):412            return NotImplemented   # In case the other object defines __rsub__413        return self.__class__(years=self.years - other.years,414                             months=self.months - other.months,415                             days=self.days - other.days,416                             hours=self.hours - other.hours,417                             minutes=self.minutes - other.minutes,418                             seconds=self.seconds - other.seconds,419                             microseconds=self.microseconds - other.microseconds,420                             leapdays=self.leapdays or other.leapdays,421                             year=(self.year if self.year is not None422                                   else other.year),423                             month=(self.month if self.month is not None else424                                    other.month),425                             day=(self.day if self.day is not None else426                                  other.day),427                             weekday=(self.weekday if self.weekday is not None else428                                      other.weekday),429                             hour=(self.hour if self.hour is not None else430                                   other.hour),431                             minute=(self.minute if self.minute is not None else432                                     other.minute),433                             second=(self.second if self.second is not None else434                                     other.second),435                             microsecond=(self.microsecond if self.microsecond436                                          is not None else437                                          other.microsecond))438 439    def __abs__(self):440        return self.__class__(years=abs(self.years),441                              months=abs(self.months),442                              days=abs(self.days),443                              hours=abs(self.hours),444                              minutes=abs(self.minutes),445                              seconds=abs(self.seconds),446                              microseconds=abs(self.microseconds),447                              leapdays=self.leapdays,448                              year=self.year,449                              month=self.month,450                              day=self.day,451                              weekday=self.weekday,452                              hour=self.hour,453                              minute=self.minute,454                              second=self.second,455                              microsecond=self.microsecond)456 457    def __neg__(self):458        return self.__class__(years=-self.years,459                             months=-self.months,460                             days=-self.days,461                             hours=-self.hours,462                             minutes=-self.minutes,463                             seconds=-self.seconds,464                             microseconds=-self.microseconds,465                             leapdays=self.leapdays,466                             year=self.year,467                             month=self.month,468                             day=self.day,469                             weekday=self.weekday,470                             hour=self.hour,471                             minute=self.minute,472                             second=self.second,473                             microsecond=self.microsecond)474 475    def __bool__(self):476        return not (not self.years and477                    not self.months and478                    not self.days and479                    not self.hours and480                    not self.minutes and481                    not self.seconds and482                    not self.microseconds and483                    not self.leapdays and484                    self.year is None and485                    self.month is None and486                    self.day is None and487                    self.weekday is None and488                    self.hour is None and489                    self.minute is None and490                    self.second is None and491                    self.microsecond is None)492    # Compatibility with Python 2.x493    __nonzero__ = __bool__494 495    def __mul__(self, other):496        try:497            f = float(other)498        except TypeError:499            return NotImplemented500 501        return self.__class__(years=int(self.years * f),502                             months=int(self.months * f),503                             days=int(self.days * f),504                             hours=int(self.hours * f),505                             minutes=int(self.minutes * f),506                             seconds=int(self.seconds * f),507                             microseconds=int(self.microseconds * f),508                             leapdays=self.leapdays,509                             year=self.year,510                             month=self.month,511                             day=self.day,512                             weekday=self.weekday,513                             hour=self.hour,514                             minute=self.minute,515                             second=self.second,516                             microsecond=self.microsecond)517 518    __rmul__ = __mul__519 520    def __eq__(self, other):521        if not isinstance(other, relativedelta):522            return NotImplemented523        if self.weekday or other.weekday:524            if not self.weekday or not other.weekday:525                return False526            if self.weekday.weekday != other.weekday.weekday:527                return False528            n1, n2 = self.weekday.n, other.weekday.n529            if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)):530                return False531        return (self.years == other.years and532                self.months == other.months and533                self.days == other.days and534                self.hours == other.hours and535                self.minutes == other.minutes and536                self.seconds == other.seconds and537                self.microseconds == other.microseconds and538                self.leapdays == other.leapdays and539                self.year == other.year and540                self.month == other.month and541                self.day == other.day and542                self.hour == other.hour and543                self.minute == other.minute and544                self.second == other.second and545                self.microsecond == other.microsecond)546 547    def __hash__(self):548        return hash((549            self.weekday,550            self.years,551            self.months,552            self.days,553            self.hours,554            self.minutes,555            self.seconds,556            self.microseconds,557            self.leapdays,558            self.year,559            self.month,560            self.day,561            self.hour,562            self.minute,563            self.second,564            self.microsecond,565        ))566 567    def __ne__(self, other):568        return not self.__eq__(other)569 570    def __div__(self, other):571        try:572            reciprocal = 1 / float(other)573        except TypeError:574            return NotImplemented575 576        return self.__mul__(reciprocal)577 578    __truediv__ = __div__579 580    def __repr__(self):581        l = []582        for attr in ["years", "months", "days", "leapdays",583                     "hours", "minutes", "seconds", "microseconds"]:584            value = getattr(self, attr)585            if value:586                l.append("{attr}={value:+g}".format(attr=attr, value=value))587        for attr in ["year", "month", "day", "weekday",588                     "hour", "minute", "second", "microsecond"]:589            value = getattr(self, attr)590            if value is not None:591                l.append("{attr}={value}".format(attr=attr, value=repr(value)))592        return "{classname}({attrs})".format(classname=self.__class__.__name__,593                                             attrs=", ".join(l))594 595 596def _sign(x):597    return int(copysign(1, x))598 599# vim:ts=4:sw=4:et600 
codekingpro/portable-devtools · Team Ai