parsing.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from collections import OrderedDict
  2. from decimal import Decimal, InvalidOperation
  3. import arrow # type: ignore
  4. from isoduration.parser.exceptions import (
  5. IncorrectDesignator,
  6. NoTime,
  7. OutOfDesignators,
  8. UnknownToken,
  9. UnparseableValue,
  10. )
  11. from isoduration.parser.util import (
  12. is_letter,
  13. is_number,
  14. is_time,
  15. is_week,
  16. parse_designator,
  17. )
  18. from isoduration.types import DateDuration, Duration, TimeDuration
  19. def parse_datetime_duration(duration_str: str, sign: int) -> Duration:
  20. try:
  21. duration: arrow.Arrow = arrow.get(duration_str)
  22. except (arrow.ParserError, ValueError):
  23. raise UnparseableValue(f"Value could not be parsed as datetime: {duration_str}")
  24. return Duration(
  25. DateDuration(
  26. years=sign * duration.year,
  27. months=sign * duration.month,
  28. days=sign * duration.day,
  29. ),
  30. TimeDuration(
  31. hours=sign * duration.hour,
  32. minutes=sign * duration.minute,
  33. seconds=sign * duration.second,
  34. ),
  35. )
  36. def parse_date_duration(date_str: str, sign: int) -> Duration:
  37. date_designators = OrderedDict(
  38. (("Y", "years"), ("M", "months"), ("D", "days"), ("W", "weeks"))
  39. )
  40. duration = DateDuration()
  41. tmp_value = ""
  42. for idx, ch in enumerate(date_str):
  43. if is_time(ch):
  44. if tmp_value != "" and tmp_value == date_str[:idx]:
  45. # PYYYY-MM-DDThh:mm:ss
  46. # PYYYYMMDDThhmmss
  47. return parse_datetime_duration(date_str, sign)
  48. time_idx = idx + 1
  49. time_str = date_str[time_idx:]
  50. if time_str == "":
  51. raise NoTime("Wanted time, no time provided")
  52. return Duration(duration, parse_time_duration(time_str, sign))
  53. if is_letter(ch):
  54. try:
  55. key = parse_designator(date_designators, ch)
  56. value = sign * Decimal(tmp_value)
  57. except OutOfDesignators as exc:
  58. raise IncorrectDesignator(
  59. f"Wrong date designator, or designator in the wrong order: {ch}"
  60. ) from exc
  61. except InvalidOperation as exc:
  62. raise UnparseableValue(
  63. f"Value could not be parsed as decimal: {tmp_value}"
  64. ) from exc
  65. if is_week(ch) and duration != DateDuration():
  66. raise IncorrectDesignator(
  67. "Week is incompatible with any other date designator"
  68. )
  69. setattr(duration, key, value)
  70. tmp_value = ""
  71. continue
  72. if is_number(ch):
  73. if ch == ",":
  74. tmp_value += "."
  75. else:
  76. tmp_value += ch
  77. continue
  78. raise UnknownToken(f"Token not recognizable: {ch}")
  79. return Duration(duration, TimeDuration())
  80. def parse_time_duration(time_str: str, sign: int) -> TimeDuration:
  81. time_designators = OrderedDict((("H", "hours"), ("M", "minutes"), ("S", "seconds")))
  82. duration = TimeDuration()
  83. tmp_value = ""
  84. for ch in time_str:
  85. if is_letter(ch):
  86. try:
  87. key = parse_designator(time_designators, ch)
  88. value = sign * Decimal(tmp_value)
  89. except OutOfDesignators as exc:
  90. raise IncorrectDesignator(
  91. f"Wrong time designator, or designator in the wrong order: {ch}"
  92. ) from exc
  93. except InvalidOperation as exc:
  94. raise UnparseableValue(
  95. f"Value could not be parsed as decimal: {tmp_value}"
  96. ) from exc
  97. setattr(duration, key, value)
  98. tmp_value = ""
  99. continue
  100. if is_number(ch):
  101. if ch == ",":
  102. tmp_value += "."
  103. else:
  104. tmp_value += ch
  105. continue
  106. raise UnknownToken(f"Token not recognizable: {ch}")
  107. return duration