relativedelta.py (14252B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\dateutil\relativedelta.py 2 import datetime 3 import calendar 4 import operator 5 from math import copysign 6 from six import integer_types 7 from warnings import warn 8 from ._common import weekday 9 MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([ weekday(x) for x in range(7) ]) 10 __all__ = ['relativedelta', 11 'MO', 12 'TU', 13 'WE', 14 'TH', 15 'FR', 16 'SA', 17 'SU'] 18 19 class relativedelta(object): 20 21 def __init__(self, dt1 = None, dt2 = None, years = 0, months = 0, days = 0, leapdays = 0, weeks = 0, hours = 0, minutes = 0, seconds = 0, microseconds = 0, year = None, month = None, day = None, weekday = None, yearday = None, nlyearday = None, hour = None, minute = None, second = None, microsecond = None): 22 if any((x is not None and x != int(x) for x in (years, months))): 23 raise ValueError('Non-integer years and months are ambiguous and not currently supported.') 24 if dt1 and dt2: 25 if not (isinstance(dt1, datetime.date) and isinstance(dt2, datetime.date)): 26 raise TypeError('relativedelta only diffs datetime/date') 27 if isinstance(dt1, datetime.datetime) != isinstance(dt2, datetime.datetime): 28 if not isinstance(dt1, datetime.datetime): 29 dt1 = datetime.datetime.fromordinal(dt1.toordinal()) 30 elif not isinstance(dt2, datetime.datetime): 31 dt2 = datetime.datetime.fromordinal(dt2.toordinal()) 32 self.years = 0 33 self.months = 0 34 self.days = 0 35 self.leapdays = 0 36 self.hours = 0 37 self.minutes = 0 38 self.seconds = 0 39 self.microseconds = 0 40 self.year = None 41 self.month = None 42 self.day = None 43 self.weekday = None 44 self.hour = None 45 self.minute = None 46 self.second = None 47 self.microsecond = None 48 self._has_time = 0 49 months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) 50 self._set_months(months) 51 dtm = self.__radd__(dt2) 52 if dt1 < dt2: 53 compare = operator.gt 54 increment = 1 55 else: 56 compare = operator.lt 57 increment = -1 58 while compare(dt1, dtm): 59 months += increment 60 self._set_months(months) 61 dtm = self.__radd__(dt2) 62 63 delta = dt1 - dtm 64 self.seconds = delta.seconds + delta.days * 86400 65 self.microseconds = delta.microseconds 66 else: 67 self.years = years 68 self.months = months 69 self.days = days + weeks * 7 70 self.leapdays = leapdays 71 self.hours = hours 72 self.minutes = minutes 73 self.seconds = seconds 74 self.microseconds = microseconds 75 self.year = year 76 self.month = month 77 self.day = day 78 self.hour = hour 79 self.minute = minute 80 self.second = second 81 self.microsecond = microsecond 82 if any((x is not None and int(x) != x for x in (year, 83 month, 84 day, 85 hour, 86 minute, 87 second, 88 microsecond))): 89 warn('Non-integer value passed as absolute information. ' + 'This is not a well-defined condition and will raise ' + 'errors in future versions.', DeprecationWarning) 90 if isinstance(weekday, integer_types): 91 self.weekday = weekdays[weekday] 92 else: 93 self.weekday = weekday 94 yday = 0 95 if nlyearday: 96 yday = nlyearday 97 elif yearday: 98 yday = yearday 99 if yearday > 59: 100 self.leapdays = -1 101 if yday: 102 ydayidx = [31, 103 59, 104 90, 105 120, 106 151, 107 181, 108 212, 109 243, 110 273, 111 304, 112 334, 113 366] 114 for idx, ydays in enumerate(ydayidx): 115 if yday <= ydays: 116 self.month = idx + 1 117 if idx == 0: 118 self.day = yday 119 else: 120 self.day = yday - ydayidx[idx - 1] 121 break 122 else: 123 raise ValueError('invalid year day (%d)' % yday) 124 125 self._fix() 126 127 def _fix(self): 128 if abs(self.microseconds) > 999999: 129 s = _sign(self.microseconds) 130 div, mod = divmod(self.microseconds * s, 1000000) 131 self.microseconds = mod * s 132 self.seconds += div * s 133 if abs(self.seconds) > 59: 134 s = _sign(self.seconds) 135 div, mod = divmod(self.seconds * s, 60) 136 self.seconds = mod * s 137 self.minutes += div * s 138 if abs(self.minutes) > 59: 139 s = _sign(self.minutes) 140 div, mod = divmod(self.minutes * s, 60) 141 self.minutes = mod * s 142 self.hours += div * s 143 if abs(self.hours) > 23: 144 s = _sign(self.hours) 145 div, mod = divmod(self.hours * s, 24) 146 self.hours = mod * s 147 self.days += div * s 148 if abs(self.months) > 11: 149 s = _sign(self.months) 150 div, mod = divmod(self.months * s, 12) 151 self.months = mod * s 152 self.years += div * s 153 if self.hours or self.minutes or self.seconds or self.microseconds or self.hour is not None or self.minute is not None or self.second is not None or self.microsecond is not None: 154 self._has_time = 1 155 else: 156 self._has_time = 0 157 158 @property 159 def weeks(self): 160 return self.days // 7 161 162 @weeks.setter 163 def weeks(self, value): 164 self.days = self.days - self.weeks * 7 + value * 7 165 166 def _set_months(self, months): 167 self.months = months 168 if abs(self.months) > 11: 169 s = _sign(self.months) 170 div, mod = divmod(self.months * s, 12) 171 self.months = mod * s 172 self.years = div * s 173 else: 174 self.years = 0 175 176 def normalized(self): 177 days = int(self.days) 178 hours_f = round(self.hours + 24 * (self.days - days), 11) 179 hours = int(hours_f) 180 minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) 181 minutes = int(minutes_f) 182 seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) 183 seconds = int(seconds_f) 184 microseconds = round(self.microseconds + 1000000.0 * (seconds_f - seconds)) 185 return self.__class__(years=self.years, months=self.months, days=days, hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds, leapdays=self.leapdays, year=self.year, month=self.month, day=self.day, weekday=self.weekday, hour=self.hour, minute=self.minute, second=self.second, microsecond=self.microsecond) 186 187 def __add__(self, other): 188 if isinstance(other, relativedelta): 189 return self.__class__(years=other.years + self.years, months=other.months + self.months, days=other.days + self.days, hours=other.hours + self.hours, minutes=other.minutes + self.minutes, seconds=other.seconds + self.seconds, microseconds=other.microseconds + self.microseconds, leapdays=other.leapdays or self.leapdays, year=other.year or self.year, month=other.month or self.month, day=other.day or self.day, weekday=other.weekday or self.weekday, hour=other.hour or self.hour, minute=other.minute or self.minute, second=other.second or self.second, microsecond=other.microsecond or self.microsecond) 190 if isinstance(other, datetime.timedelta): 191 return self.__class__(years=self.years, months=self.months, days=self.days + other.days, hours=self.hours, minutes=self.minutes, seconds=self.seconds + other.seconds, microseconds=self.microseconds + other.microseconds, leapdays=self.leapdays, year=self.year, month=self.month, day=self.day, weekday=self.weekday, hour=self.hour, minute=self.minute, second=self.second, microsecond=self.microsecond) 192 if not isinstance(other, datetime.date): 193 return NotImplemented 194 if self._has_time and not isinstance(other, datetime.datetime): 195 other = datetime.datetime.fromordinal(other.toordinal()) 196 year = (self.year or other.year) + self.years 197 month = self.month or other.month 198 if self.months: 199 month += self.months 200 if month > 12: 201 year += 1 202 month -= 12 203 elif month < 1: 204 year -= 1 205 month += 12 206 day = min(calendar.monthrange(year, month)[1], self.day or other.day) 207 repl = {'year': year, 208 'month': month, 209 'day': day} 210 for attr in ['hour', 211 'minute', 212 'second', 213 'microsecond']: 214 value = getattr(self, attr) 215 if value is not None: 216 repl[attr] = value 217 218 days = self.days 219 if self.leapdays and month > 2 and calendar.isleap(year): 220 days += self.leapdays 221 ret = other.replace(**repl) + datetime.timedelta(days=days, hours=self.hours, minutes=self.minutes, seconds=self.seconds, microseconds=self.microseconds) 222 if self.weekday: 223 weekday, nth = self.weekday.weekday, self.weekday.n or 1 224 jumpdays = (abs(nth) - 1) * 7 225 if nth > 0: 226 jumpdays += (7 - ret.weekday() + weekday) % 7 227 else: 228 jumpdays += (ret.weekday() - weekday) % 7 229 jumpdays *= -1 230 ret += datetime.timedelta(days=jumpdays) 231 return ret 232 233 def __radd__(self, other): 234 return self.__add__(other) 235 236 def __rsub__(self, other): 237 return self.__neg__().__radd__(other) 238 239 def __sub__(self, other): 240 if not isinstance(other, relativedelta): 241 return NotImplemented 242 return self.__class__(years=self.years - other.years, months=self.months - other.months, days=self.days - other.days, hours=self.hours - other.hours, minutes=self.minutes - other.minutes, seconds=self.seconds - other.seconds, microseconds=self.microseconds - other.microseconds, leapdays=self.leapdays or other.leapdays, year=self.year or other.year, month=self.month or other.month, day=self.day or other.day, weekday=self.weekday or other.weekday, hour=self.hour or other.hour, minute=self.minute or other.minute, second=self.second or other.second, microsecond=self.microsecond or other.microsecond) 243 244 def __neg__(self): 245 return self.__class__(years=-self.years, months=-self.months, days=-self.days, hours=-self.hours, minutes=-self.minutes, seconds=-self.seconds, microseconds=-self.microseconds, leapdays=self.leapdays, year=self.year, month=self.month, day=self.day, weekday=self.weekday, hour=self.hour, minute=self.minute, second=self.second, microsecond=self.microsecond) 246 247 def __bool__(self): 248 return not (not self.years and not self.months and not self.days and not self.hours and not self.minutes and not self.seconds and not self.microseconds and not self.leapdays and self.year is None and self.month is None and self.day is None and self.weekday is None and self.hour is None and self.minute is None and self.second is None and self.microsecond is None) 249 250 __nonzero__ = __bool__ 251 252 def __mul__(self, other): 253 try: 254 f = float(other) 255 except TypeError: 256 return NotImplemented 257 258 return self.__class__(years=int(self.years * f), months=int(self.months * f), days=int(self.days * f), hours=int(self.hours * f), minutes=int(self.minutes * f), seconds=int(self.seconds * f), microseconds=int(self.microseconds * f), leapdays=self.leapdays, year=self.year, month=self.month, day=self.day, weekday=self.weekday, hour=self.hour, minute=self.minute, second=self.second, microsecond=self.microsecond) 259 260 __rmul__ = __mul__ 261 262 def __eq__(self, other): 263 if not isinstance(other, relativedelta): 264 return NotImplemented 265 if self.weekday or other.weekday: 266 if not self.weekday or not other.weekday: 267 return False 268 if self.weekday.weekday != other.weekday.weekday: 269 return False 270 n1, n2 = self.weekday.n, other.weekday.n 271 if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): 272 return False 273 return self.years == other.years and self.months == other.months and self.days == other.days and self.hours == other.hours and self.minutes == other.minutes and self.seconds == other.seconds and self.microseconds == other.microseconds and self.leapdays == other.leapdays and self.year == other.year and self.month == other.month and self.day == other.day and self.hour == other.hour and self.minute == other.minute and self.second == other.second and self.microsecond == other.microsecond 274 275 __hash__ = None 276 277 def __ne__(self, other): 278 return not self.__eq__(other) 279 280 def __div__(self, other): 281 try: 282 reciprocal = 1 / float(other) 283 except TypeError: 284 return NotImplemented 285 286 return self.__mul__(reciprocal) 287 288 __truediv__ = __div__ 289 290 def __repr__(self): 291 l = [] 292 for attr in ['years', 293 'months', 294 'days', 295 'leapdays', 296 'hours', 297 'minutes', 298 'seconds', 299 'microseconds']: 300 value = getattr(self, attr) 301 if value: 302 l.append('{attr}={value:+g}'.format(attr=attr, value=value)) 303 304 for attr in ['year', 305 'month', 306 'day', 307 'weekday', 308 'hour', 309 'minute', 310 'second', 311 'microsecond']: 312 value = getattr(self, attr) 313 if value is not None: 314 l.append('{attr}={value}'.format(attr=attr, value=repr(value))) 315 316 return '{classname}({attrs})'.format(classname=self.__class__.__name__, attrs=', '.join(l)) 317 318 319 def _sign(x): 320 return int(copysign(1, x))