|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 |
|
| 3 | +from math import copysign |
| 4 | +from datetime import timedelta |
| 5 | + |
3 | 6 | try: |
4 | 7 | from ._extensions._helpers import local_time |
5 | 8 | except ImportError: |
6 | 9 | from ._extensions.helpers import local_time |
| 10 | + |
| 11 | +from .constants import DAYS_PER_MONTHS |
| 12 | + |
| 13 | + |
| 14 | +def is_leap(year): |
| 15 | + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) |
| 16 | + |
| 17 | + |
| 18 | +def add_duration(dt, years=0, months=0, weeks=0, days=0, |
| 19 | + hours=0, minutes=0, seconds=0, microseconds=0): |
| 20 | + """ |
| 21 | + Adds a duration to a datetime instance. |
| 22 | +
|
| 23 | + :param dt: The datetime instance |
| 24 | + :type dt: datetime.datetime |
| 25 | +
|
| 26 | + :param years: The number of years |
| 27 | + :type years: int |
| 28 | +
|
| 29 | + :param months: The number of months |
| 30 | + :type months: int |
| 31 | +
|
| 32 | + :param weeks: The number of weeks |
| 33 | + :type weeks: int |
| 34 | +
|
| 35 | + :param days: The number of days |
| 36 | + :type days: int |
| 37 | +
|
| 38 | + :param hours: The number of hours |
| 39 | + :type hours: int |
| 40 | +
|
| 41 | + :param minutes: The number of minutes |
| 42 | + :type minutes: int |
| 43 | +
|
| 44 | + :param seconds: The number of seconds |
| 45 | + :type seconds: int |
| 46 | +
|
| 47 | + :param microseconds: The number of microseconds |
| 48 | + :type microseconds: int |
| 49 | +
|
| 50 | + :rtype: datetime.datetime |
| 51 | + """ |
| 52 | + days += weeks * 7 |
| 53 | + |
| 54 | + # Normalizing |
| 55 | + if abs(microseconds) > 999999: |
| 56 | + s = _sign(microseconds) |
| 57 | + div, mod = divmod(microseconds * s, 1000000) |
| 58 | + microseconds = mod * s |
| 59 | + seconds += div * s |
| 60 | + |
| 61 | + if abs(seconds) > 59: |
| 62 | + s = _sign(seconds) |
| 63 | + div, mod = divmod(seconds * s, 60) |
| 64 | + seconds = mod * s |
| 65 | + minutes += div * s |
| 66 | + |
| 67 | + if abs(minutes) > 59: |
| 68 | + s = _sign(minutes) |
| 69 | + div, mod = divmod(minutes * s, 60) |
| 70 | + minutes = mod * s |
| 71 | + hours += div * s |
| 72 | + |
| 73 | + if abs(hours) > 23: |
| 74 | + s = _sign(hours) |
| 75 | + div, mod = divmod(hours * s, 24) |
| 76 | + hours = mod * s |
| 77 | + days += div * s |
| 78 | + |
| 79 | + if abs(months) > 11: |
| 80 | + s = _sign(months) |
| 81 | + div, mod = divmod(months * s, 12) |
| 82 | + months = mod * s |
| 83 | + years += div * s |
| 84 | + |
| 85 | + year = dt.year + years |
| 86 | + month = dt.month |
| 87 | + |
| 88 | + if months: |
| 89 | + month += months |
| 90 | + if month > 12: |
| 91 | + year += 1 |
| 92 | + month -= 12 |
| 93 | + elif month < 1: |
| 94 | + year -= 1 |
| 95 | + month += 12 |
| 96 | + |
| 97 | + day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day) |
| 98 | + |
| 99 | + dt = dt.replace(year=year, month=month, day=day) |
| 100 | + |
| 101 | + return dt + timedelta( |
| 102 | + days=days, |
| 103 | + hours=hours, |
| 104 | + minutes=minutes, |
| 105 | + seconds=seconds, |
| 106 | + microseconds=microseconds |
| 107 | + ) |
| 108 | + |
| 109 | + |
| 110 | +def _sign(x): |
| 111 | + return int(copysign(1, x)) |
0 commit comments