__init__.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. from __future__ import annotations
  2. from datetime import datetime
  3. from decimal import ROUND_HALF_UP, Decimal
  4. from typing import TYPE_CHECKING
  5. from isoduration.operations.util import max_day_in_month, mod2, mod3, quot2, quot3
  6. if TYPE_CHECKING: # pragma: no cover
  7. from isoduration.types import Duration
  8. def add(start: datetime, duration: Duration) -> datetime:
  9. """
  10. https://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
  11. """
  12. # Months.
  13. temp = Decimal(start.month) + duration.date.months
  14. end_month = mod3(temp, Decimal(1), Decimal(13))
  15. carry = quot3(temp, Decimal(1), Decimal(13))
  16. # Years.
  17. end_year = Decimal(start.year) + duration.date.years + carry
  18. # Zone.
  19. end_tzinfo = start.tzinfo
  20. # Seconds.
  21. temp = Decimal(start.second) + duration.time.seconds
  22. end_second = mod2(temp, Decimal("60"))
  23. carry = quot2(temp, Decimal("60"))
  24. # Minutes.
  25. temp = Decimal(start.minute) + duration.time.minutes + carry
  26. end_minute = mod2(temp, Decimal("60"))
  27. carry = quot2(temp, Decimal("60"))
  28. # Hours.
  29. temp = Decimal(start.hour) + duration.time.hours + carry
  30. end_hour = mod2(temp, Decimal("24"))
  31. carry = quot2(temp, Decimal("24"))
  32. # Days.
  33. end_max_day_in_month = max_day_in_month(end_year, end_month)
  34. if start.day > end_max_day_in_month:
  35. temp = end_max_day_in_month
  36. else:
  37. temp = Decimal(start.day)
  38. end_day = temp + duration.date.days + (7 * duration.date.weeks) + carry
  39. while True:
  40. if end_day < 1:
  41. end_day += max_day_in_month(end_year, end_month - 1)
  42. carry = Decimal(-1)
  43. elif end_day > max_day_in_month(end_year, end_month):
  44. end_day -= max_day_in_month(end_year, end_month)
  45. carry = Decimal(1)
  46. else:
  47. break
  48. temp = end_month + carry
  49. end_month = mod3(temp, Decimal(1), Decimal(13))
  50. end_year = end_year + quot3(temp, Decimal(1), Decimal(13))
  51. return datetime(
  52. year=int(end_year.to_integral_value(ROUND_HALF_UP)),
  53. month=int(end_month.to_integral_value(ROUND_HALF_UP)),
  54. day=int(end_day.to_integral_value(ROUND_HALF_UP)),
  55. hour=int(end_hour.to_integral_value(ROUND_HALF_UP)),
  56. minute=int(end_minute.to_integral_value(ROUND_HALF_UP)),
  57. second=int(end_second.to_integral_value(ROUND_HALF_UP)),
  58. tzinfo=end_tzinfo,
  59. )