1 ```# -*- coding: utf-8 -*- ``` 2 20 ```import datetime ``` 3 20 ```import calendar ``` 4 5 20 ```import operator ``` 6 20 ```from math import copysign ``` 7 8 20 ```from six import integer_types ``` 9 20 ```from warnings import warn ``` 10 11 20 ```from ._common import weekday ``` 12 13 20 ```MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) ``` 14 15 20 ```__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] ``` 16 17 18 20 ```class relativedelta(object): ``` 19 ``` """ ``` 20 ``` The relativedelta type is designed to be applied to an existing datetime and ``` 21 ``` can replace specific components of that datetime, or represents an interval ``` 22 ``` of time. ``` 23 24 ``` It is based on the specification of the excellent work done by M.-A. Lemburg ``` 25 ``` in his ``` 26 ``` `mx.DateTime `_ extension. ``` 27 ``` However, notice that this type does *NOT* implement the same algorithm as ``` 28 ``` 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. The ``` 31 ``` 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 a ``` 41 ``` relativedelta with absolute information does not perform an arithmetic ``` 42 ``` operation, but rather REPLACES the corresponding value in the ``` 43 ``` 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); adding ``` 47 ``` or subtracting a relativedelta with relative information performs ``` 48 ``` the corresponding arithmetic operation on the original datetime value ``` 49 ``` with the information in the relativedelta. ``` 50 51 ``` weekday: ``` 52 ``` One of the weekday instances (MO, TU, etc) available in the ``` 53 ``` relativedelta module. These instances may receive a parameter N, ``` 54 ``` specifying the Nth weekday, which could be positive or negative ``` 55 ``` (like MO(+1) or MO(-2)). Not specifying it is the same as specifying ``` 56 ``` +1. You can also use an integer, where 0=MO. This argument is always ``` 57 ``` 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, use ``` 59 ``` it in combination with the day argument (e.g. day=1, MO(1) for first ``` 60 ``` Monday of the month). ``` 61 62 ``` leapdays: ``` 63 ``` Will add given days to the date found, if year is a leap ``` 64 ``` 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 keyword ``` 71 ``` arguments. The plural is relative, and the singular is ``` 72 ``` absolute. For each argument in the order below, the absolute form ``` 73 ``` is applied first (by setting each attribute to that value) and ``` 74 ``` then the relative form (by adding the value to the attribute). ``` 75 76 ``` The order of attributes considered when this relativedelta is ``` 77 ``` added to a datetime is: ``` 78 79 ``` 1. Year ``` 80 ``` 2. Month ``` 81 ``` 3. Day ``` 82 ``` 4. Hours ``` 83 ``` 5. Minutes ``` 84 ``` 6. Seconds ``` 85 ``` 7. Microseconds ``` 86 87 ``` Finally, weekday is applied, using the rule described above. ``` 88 89 ``` For example ``` 90 91 ``` >>> from datetime import datetime ``` 92 ``` >>> from dateutil.relativedelta import relativedelta, MO ``` 93 ``` >>> dt = datetime(2018, 4, 9, 13, 37, 0) ``` 94 ``` >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) ``` 95 ``` >>> dt + delta ``` 96 ``` datetime.datetime(2018, 4, 2, 14, 37) ``` 97 98 ``` First, the day is set to 1 (the first of the month), then 25 hours ``` 99 ``` are added, to get to the 2nd day and 14th hour, finally the ``` 100 ``` weekday is applied, but since the 2nd is already a Monday there is ``` 101 ``` no effect. ``` 102 103 ``` """ ``` 104 105 20 ``` 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 20 ``` if dt1 and dt2: ``` 113 ``` # datetime is a subclass of date. So both must be date ``` 114 20 ``` if not (isinstance(dt1, datetime.date) and ``` 115 ``` isinstance(dt2, datetime.date)): ``` 116 20 ``` raise TypeError("relativedelta only diffs datetime/date") ``` 117 118 ``` # We allow two dates, or two datetimes, so we coerce them to be ``` 119 ``` # of the same type ``` 120 20 ``` if (isinstance(dt1, datetime.datetime) != ``` 121 ``` isinstance(dt2, datetime.datetime)): ``` 122 20 ``` if not isinstance(dt1, datetime.datetime): ``` 123 20 ``` dt1 = datetime.datetime.fromordinal(dt1.toordinal()) ``` 124 20 ``` elif not isinstance(dt2, datetime.datetime): ``` 125 20 ``` dt2 = datetime.datetime.fromordinal(dt2.toordinal()) ``` 126 127 20 ``` self.years = 0 ``` 128 20 ``` self.months = 0 ``` 129 20 ``` self.days = 0 ``` 130 20 ``` self.leapdays = 0 ``` 131 20 ``` self.hours = 0 ``` 132 20 ``` self.minutes = 0 ``` 133 20 ``` self.seconds = 0 ``` 134 20 ``` self.microseconds = 0 ``` 135 20 ``` self.year = None ``` 136 20 ``` self.month = None ``` 137 20 ``` self.day = None ``` 138 20 ``` self.weekday = None ``` 139 20 ``` self.hour = None ``` 140 20 ``` self.minute = None ``` 141 20 ``` self.second = None ``` 142 20 ``` self.microsecond = None ``` 143 20 ``` self._has_time = 0 ``` 144 145 ``` # Get year / month delta between the two ``` 146 20 ``` months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) ``` 147 20 ``` self._set_months(months) ``` 148 149 ``` # Remove the year/month delta so the timedelta is just well-defined ``` 150 ``` # time units (seconds, days and microseconds) ``` 151 20 ``` dtm = self.__radd__(dt2) ``` 152 153 ``` # If we've overshot our target, make an adjustment ``` 154 20 ``` if dt1 < dt2: ``` 155 20 ``` compare = operator.gt ``` 156 20 ``` increment = 1 ``` 157 ``` else: ``` 158 20 ``` compare = operator.lt ``` 159 20 ``` increment = -1 ``` 160 161 20 ``` while compare(dt1, dtm): ``` 162 20 ``` months += increment ``` 163 20 ``` self._set_months(months) ``` 164 20 ``` dtm = self.__radd__(dt2) ``` 165 166 ``` # Get the timedelta between the "months-adjusted" date and dt1 ``` 167 20 ``` delta = dt1 - dtm ``` 168 20 ``` self.seconds = delta.seconds + delta.days * 86400 ``` 169 20 ``` self.microseconds = delta.microseconds ``` 170 ``` else: ``` 171 ``` # Check for non-integer values in integer-only quantities ``` 172 20 ``` if any(x is not None and x != int(x) for x in (years, months)): ``` 173 20 ``` raise ValueError("Non-integer years and months are " ``` 174 ``` "ambiguous and not currently supported.") ``` 175 176 ``` # Relative information ``` 177 20 ``` self.years = int(years) ``` 178 20 ``` self.months = int(months) ``` 179 20 ``` self.days = days + weeks * 7 ``` 180 20 ``` self.leapdays = leapdays ``` 181 20 ``` self.hours = hours ``` 182 20 ``` self.minutes = minutes ``` 183 20 ``` self.seconds = seconds ``` 184 20 ``` self.microseconds = microseconds ``` 185 186 ``` # Absolute information ``` 187 20 ``` self.year = year ``` 188 20 ``` self.month = month ``` 189 20 ``` self.day = day ``` 190 20 ``` self.hour = hour ``` 191 20 ``` self.minute = minute ``` 192 20 ``` self.second = second ``` 193 20 ``` self.microsecond = microsecond ``` 194 195 20 ``` if any(x is not None and int(x) != x ``` 196 ``` 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 20 ``` 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 20 ``` if isinstance(weekday, integer_types): ``` 204 20 ``` self.weekday = weekdays[weekday] ``` 205 ``` else: ``` 206 20 ``` self.weekday = weekday ``` 207 208 20 ``` yday = 0 ``` 209 20 ``` if nlyearday: ``` 210 20 ``` yday = nlyearday ``` 211 20 ``` elif yearday: ``` 212 20 ``` yday = yearday ``` 213 20 ``` if yearday > 59: ``` 214 20 ``` self.leapdays = -1 ``` 215 20 ``` if yday: ``` 216 20 ``` ydayidx = [31, 59, 90, 120, 151, 181, 212, ``` 217 ``` 243, 273, 304, 334, 366] ``` 218 20 ``` for idx, ydays in enumerate(ydayidx): ``` 219 20 ``` if yday <= ydays: ``` 220 20 ``` self.month = idx+1 ``` 221 20 ``` if idx == 0: ``` 222 20 ``` self.day = yday ``` 223 ``` else: ``` 224 20 ``` self.day = yday-ydayidx[idx-1] ``` 225 20 ``` break ``` 226 ``` else: ``` 227 20 ``` raise ValueError("invalid year day (%d)" % yday) ``` 228 229 20 ``` self._fix() ``` 230 231 20 ``` def _fix(self): ``` 232 20 ``` if abs(self.microseconds) > 999999: ``` 233 20 ``` s = _sign(self.microseconds) ``` 234 20 ``` div, mod = divmod(self.microseconds * s, 1000000) ``` 235 20 ``` self.microseconds = mod * s ``` 236 20 ``` self.seconds += div * s ``` 237 20 ``` if abs(self.seconds) > 59: ``` 238 20 ``` s = _sign(self.seconds) ``` 239 20 ``` div, mod = divmod(self.seconds * s, 60) ``` 240 20 ``` self.seconds = mod * s ``` 241 20 ``` self.minutes += div * s ``` 242 20 ``` if abs(self.minutes) > 59: ``` 243 20 ``` s = _sign(self.minutes) ``` 244 20 ``` div, mod = divmod(self.minutes * s, 60) ``` 245 20 ``` self.minutes = mod * s ``` 246 20 ``` self.hours += div * s ``` 247 20 ``` if abs(self.hours) > 23: ``` 248 20 ``` s = _sign(self.hours) ``` 249 20 ``` div, mod = divmod(self.hours * s, 24) ``` 250 20 ``` self.hours = mod * s ``` 251 20 ``` self.days += div * s ``` 252 20 ``` if abs(self.months) > 11: ``` 253 20 ``` s = _sign(self.months) ``` 254 20 ``` div, mod = divmod(self.months * s, 12) ``` 255 20 ``` self.months = mod * s ``` 256 20 ``` self.years += div * s ``` 257 20 ``` if (self.hours or self.minutes or self.seconds or self.microseconds ``` 258 ``` or self.hour is not None or self.minute is not None or ``` 259 ``` self.second is not None or self.microsecond is not None): ``` 260 20 ``` self._has_time = 1 ``` 261 ``` else: ``` 262 20 ``` self._has_time = 0 ``` 263 264 20 ``` @property ``` 265 2 ``` def weeks(self): ``` 266 20 ``` return int(self.days / 7.0) ``` 267 268 20 ``` @weeks.setter ``` 269 2 ``` def weeks(self, value): ``` 270 20 ``` self.days = self.days - (self.weeks * 7) + value * 7 ``` 271 272 20 ``` def _set_months(self, months): ``` 273 20 ``` self.months = months ``` 274 20 ``` if abs(self.months) > 11: ``` 275 20 ``` s = _sign(self.months) ``` 276 20 ``` div, mod = divmod(self.months * s, 12) ``` 277 20 ``` self.months = mod * s ``` 278 20 ``` self.years = div * s ``` 279 ``` else: ``` 280 20 ``` self.years = 0 ``` 281 282 20 ``` def normalized(self): ``` 283 ``` """ ``` 284 ``` Return a version of this object represented entirely using integer ``` 285 ``` 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 20 ``` days = int(self.days) ``` 295 296 20 ``` hours_f = round(self.hours + 24 * (self.days - days), 11) ``` 297 20 ``` hours = int(hours_f) ``` 298 299 20 ``` minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) ``` 300 20 ``` minutes = int(minutes_f) ``` 301 302 20 ``` seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) ``` 303 20 ``` seconds = int(seconds_f) ``` 304 305 20 ``` microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) ``` 306 307 ``` # Constructor carries overflow back up with call to _fix() ``` 308 20 ``` 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 20 ``` def __add__(self, other): ``` 318 20 ``` if isinstance(other, relativedelta): ``` 319 20 ``` 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 None ``` 329 ``` else self.year), ``` 330 ``` month=(other.month if other.month is not None ``` 331 ``` else self.month), ``` 332 ``` day=(other.day if other.day is not None ``` 333 ``` else self.day), ``` 334 ``` weekday=(other.weekday if other.weekday is not None ``` 335 ``` else self.weekday), ``` 336 ``` hour=(other.hour if other.hour is not None ``` 337 ``` else self.hour), ``` 338 ``` minute=(other.minute if other.minute is not None ``` 339 ``` else self.minute), ``` 340 ``` second=(other.second if other.second is not None ``` 341 ``` else self.second), ``` 342 ``` microsecond=(other.microsecond if other.microsecond ``` 343 ``` is not None else ``` 344 ``` self.microsecond)) ``` 345 20 ``` if isinstance(other, datetime.timedelta): ``` 346 20 ``` 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 20 ``` if not isinstance(other, datetime.date): ``` 363 20 ``` return NotImplemented ``` 364 20 ``` elif self._has_time and not isinstance(other, datetime.datetime): ``` 365 20 ``` other = datetime.datetime.fromordinal(other.toordinal()) ``` 366 20 ``` year = (self.year or other.year)+self.years ``` 367 20 ``` month = self.month or other.month ``` 368 20 ``` if self.months: ``` 369 20 ``` assert 1 <= abs(self.months) <= 12 ``` 370 20 ``` month += self.months ``` 371 20 ``` if month > 12: ``` 372 20 ``` year += 1 ``` 373 20 ``` month -= 12 ``` 374 20 ``` elif month < 1: ``` 375 20 ``` year -= 1 ``` 376 20 ``` month += 12 ``` 377 20 ``` day = min(calendar.monthrange(year, month)[1], ``` 378 ``` self.day or other.day) ``` 379 20 ``` repl = {"year": year, "month": month, "day": day} ``` 380 20 ``` for attr in ["hour", "minute", "second", "microsecond"]: ``` 381 20 ``` value = getattr(self, attr) ``` 382 20 ``` if value is not None: ``` 383 20 ``` repl[attr] = value ``` 384 20 ``` days = self.days ``` 385 20 ``` if self.leapdays and month > 2 and calendar.isleap(year): ``` 386 20 ``` days += self.leapdays ``` 387 20 ``` 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 20 ``` if self.weekday: ``` 394 20 ``` weekday, nth = self.weekday.weekday, self.weekday.n or 1 ``` 395 20 ``` jumpdays = (abs(nth) - 1) * 7 ``` 396 20 ``` if nth > 0: ``` 397 20 ``` jumpdays += (7 - ret.weekday() + weekday) % 7 ``` 398 ``` else: ``` 399 20 ``` jumpdays += (ret.weekday() - weekday) % 7 ``` 400 20 ``` jumpdays *= -1 ``` 401 20 ``` ret += datetime.timedelta(days=jumpdays) ``` 402 20 ``` return ret ``` 403 404 20 ``` def __radd__(self, other): ``` 405 20 ``` return self.__add__(other) ``` 406 407 20 ``` def __rsub__(self, other): ``` 408 20 ``` return self.__neg__().__radd__(other) ``` 409 410 20 ``` def __sub__(self, other): ``` 411 20 ``` if not isinstance(other, relativedelta): ``` 412 20 ``` return NotImplemented # In case the other object defines __rsub__ ``` 413 20 ``` 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 None ``` 422 ``` else other.year), ``` 423 ``` month=(self.month if self.month is not None else ``` 424 ``` other.month), ``` 425 ``` day=(self.day if self.day is not None else ``` 426 ``` other.day), ``` 427 ``` weekday=(self.weekday if self.weekday is not None else ``` 428 ``` other.weekday), ``` 429 ``` hour=(self.hour if self.hour is not None else ``` 430 ``` other.hour), ``` 431 ``` minute=(self.minute if self.minute is not None else ``` 432 ``` other.minute), ``` 433 ``` second=(self.second if self.second is not None else ``` 434 ``` other.second), ``` 435 ``` microsecond=(self.microsecond if self.microsecond ``` 436 ``` is not None else ``` 437 ``` other.microsecond)) ``` 438 439 20 ``` def __abs__(self): ``` 440 20 ``` 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 20 ``` def __neg__(self): ``` 458 20 ``` 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 20 ``` def __bool__(self): ``` 476 20 ``` return not (not self.years and ``` 477 ``` not self.months and ``` 478 ``` not self.days and ``` 479 ``` not self.hours and ``` 480 ``` not self.minutes and ``` 481 ``` not self.seconds and ``` 482 ``` not self.microseconds and ``` 483 ``` not self.leapdays and ``` 484 ``` self.year is None and ``` 485 ``` self.month is None and ``` 486 ``` self.day is None and ``` 487 ``` self.weekday is None and ``` 488 ``` self.hour is None and ``` 489 ``` self.minute is None and ``` 490 ``` self.second is None and ``` 491 ``` self.microsecond is None) ``` 492 ``` # Compatibility with Python 2.x ``` 493 20 ``` __nonzero__ = __bool__ ``` 494 495 20 ``` def __mul__(self, other): ``` 496 20 ``` try: ``` 497 20 ``` f = float(other) ``` 498 20 ``` except TypeError: ``` 499 20 ``` return NotImplemented ``` 500 501 20 ``` 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 20 ``` __rmul__ = __mul__ ``` 519 520 20 ``` def __eq__(self, other): ``` 521 20 ``` if not isinstance(other, relativedelta): ``` 522 20 ``` return NotImplemented ``` 523 20 ``` if self.weekday or other.weekday: ``` 524 20 ``` if not self.weekday or not other.weekday: ``` 525 20 ``` return False ``` 526 20 ``` if self.weekday.weekday != other.weekday.weekday: ``` 527 20 ``` return False ``` 528 20 ``` n1, n2 = self.weekday.n, other.weekday.n ``` 529 20 ``` if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): ``` 530 20 ``` return False ``` 531 20 ``` return (self.years == other.years and ``` 532 ``` self.months == other.months and ``` 533 ``` self.days == other.days and ``` 534 ``` self.hours == other.hours and ``` 535 ``` self.minutes == other.minutes and ``` 536 ``` self.seconds == other.seconds and ``` 537 ``` self.microseconds == other.microseconds and ``` 538 ``` self.leapdays == other.leapdays and ``` 539 ``` self.year == other.year and ``` 540 ``` self.month == other.month and ``` 541 ``` self.day == other.day and ``` 542 ``` self.hour == other.hour and ``` 543 ``` self.minute == other.minute and ``` 544 ``` self.second == other.second and ``` 545 ``` self.microsecond == other.microsecond) ``` 546 547 20 ``` def __hash__(self): ``` 548 20 ``` 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 20 ``` def __ne__(self, other): ``` 568 20 ``` return not self.__eq__(other) ``` 569 570 20 ``` def __div__(self, other): ``` 571 20 ``` try: ``` 572 20 ``` reciprocal = 1 / float(other) ``` 573 20 ``` except TypeError: ``` 574 20 ``` return NotImplemented ``` 575 576 20 ``` return self.__mul__(reciprocal) ``` 577 578 20 ``` __truediv__ = __div__ ``` 579 580 20 ``` def __repr__(self): ``` 581 20 ``` l = [] ``` 582 20 ``` for attr in ["years", "months", "days", "leapdays", ``` 583 ``` "hours", "minutes", "seconds", "microseconds"]: ``` 584 20 ``` value = getattr(self, attr) ``` 585 20 ``` if value: ``` 586 20 ``` l.append("{attr}={value:+g}".format(attr=attr, value=value)) ``` 587 20 ``` for attr in ["year", "month", "day", "weekday", ``` 588 ``` "hour", "minute", "second", "microsecond"]: ``` 589 20 ``` value = getattr(self, attr) ``` 590 20 ``` if value is not None: ``` 591 20 ``` l.append("{attr}={value}".format(attr=attr, value=repr(value))) ``` 592 20 ``` return "{classname}({attrs})".format(classname=self.__class__.__name__, ``` 593 ``` attrs=", ".join(l)) ``` 594 595 596 20 ```def _sign(x): ``` 597 20 ``` return int(copysign(1, x)) ``` 598 599 ```# vim:ts=4:sw=4:et ```

Read our documentation on viewing source code .