parser.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. from __future__ import annotations
  2. import datetime
  3. import typing as t
  4. import pendulum
  5. from pendulum.duration import Duration
  6. from pendulum.parsing import _Interval
  7. from pendulum.parsing import parse as base_parse
  8. from pendulum.tz.timezone import UTC
  9. if t.TYPE_CHECKING:
  10. from pendulum.date import Date
  11. from pendulum.datetime import DateTime
  12. from pendulum.interval import Interval
  13. from pendulum.time import Time
  14. try:
  15. from pendulum._pendulum import Duration as RustDuration
  16. except ImportError:
  17. RustDuration = None # type: ignore[assignment,misc]
  18. def parse(text: str, **options: t.Any) -> Date | Time | DateTime | Duration:
  19. # Use the mock now value if it exists
  20. options["now"] = options.get("now")
  21. return _parse(text, **options)
  22. def _parse(text: str, **options: t.Any) -> Date | DateTime | Time | Duration | Interval:
  23. """
  24. Parses a string with the given options.
  25. :param text: The string to parse.
  26. """
  27. # Handling special cases
  28. if text == "now":
  29. return pendulum.now()
  30. parsed = base_parse(text, **options)
  31. if isinstance(parsed, datetime.datetime):
  32. return pendulum.datetime(
  33. parsed.year,
  34. parsed.month,
  35. parsed.day,
  36. parsed.hour,
  37. parsed.minute,
  38. parsed.second,
  39. parsed.microsecond,
  40. tz=parsed.tzinfo or options.get("tz", UTC),
  41. )
  42. if isinstance(parsed, datetime.date):
  43. return pendulum.date(parsed.year, parsed.month, parsed.day)
  44. if isinstance(parsed, datetime.time):
  45. return pendulum.time(
  46. parsed.hour, parsed.minute, parsed.second, parsed.microsecond
  47. )
  48. if isinstance(parsed, _Interval):
  49. if parsed.duration is not None:
  50. duration = parsed.duration
  51. if parsed.start is not None:
  52. dt = pendulum.instance(parsed.start, tz=options.get("tz", UTC))
  53. return pendulum.interval(
  54. dt,
  55. dt.add(
  56. years=duration.years,
  57. months=duration.months,
  58. weeks=duration.weeks,
  59. days=duration.remaining_days,
  60. hours=duration.hours,
  61. minutes=duration.minutes,
  62. seconds=duration.remaining_seconds,
  63. microseconds=duration.microseconds,
  64. ),
  65. )
  66. dt = pendulum.instance(
  67. t.cast(datetime.datetime, parsed.end), tz=options.get("tz", UTC)
  68. )
  69. return pendulum.interval(
  70. dt.subtract(
  71. years=duration.years,
  72. months=duration.months,
  73. weeks=duration.weeks,
  74. days=duration.remaining_days,
  75. hours=duration.hours,
  76. minutes=duration.minutes,
  77. seconds=duration.remaining_seconds,
  78. microseconds=duration.microseconds,
  79. ),
  80. dt,
  81. )
  82. return pendulum.interval(
  83. pendulum.instance(
  84. t.cast(datetime.datetime, parsed.start), tz=options.get("tz", UTC)
  85. ),
  86. pendulum.instance(
  87. t.cast(datetime.datetime, parsed.end), tz=options.get("tz", UTC)
  88. ),
  89. )
  90. if isinstance(parsed, Duration):
  91. return parsed
  92. if RustDuration is not None and isinstance(parsed, RustDuration):
  93. return pendulum.duration(
  94. years=parsed.years,
  95. months=parsed.months,
  96. weeks=parsed.weeks,
  97. days=parsed.days,
  98. hours=parsed.hours,
  99. minutes=parsed.minutes,
  100. seconds=parsed.seconds,
  101. microseconds=parsed.microseconds,
  102. )
  103. raise NotImplementedError