You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.9 KiB
Python
138 lines
3.9 KiB
Python
from collections import OrderedDict
|
|
from decimal import Decimal, InvalidOperation
|
|
|
|
import arrow # type: ignore
|
|
|
|
from isoduration.parser.exceptions import (
|
|
IncorrectDesignator,
|
|
NoTime,
|
|
OutOfDesignators,
|
|
UnknownToken,
|
|
UnparseableValue,
|
|
)
|
|
from isoduration.parser.util import (
|
|
is_letter,
|
|
is_number,
|
|
is_time,
|
|
is_week,
|
|
parse_designator,
|
|
)
|
|
from isoduration.types import DateDuration, Duration, TimeDuration
|
|
|
|
|
|
def parse_datetime_duration(duration_str: str, sign: int) -> Duration:
|
|
try:
|
|
duration: arrow.Arrow = arrow.get(duration_str)
|
|
except (arrow.ParserError, ValueError):
|
|
raise UnparseableValue(f"Value could not be parsed as datetime: {duration_str}")
|
|
|
|
return Duration(
|
|
DateDuration(
|
|
years=sign * duration.year,
|
|
months=sign * duration.month,
|
|
days=sign * duration.day,
|
|
),
|
|
TimeDuration(
|
|
hours=sign * duration.hour,
|
|
minutes=sign * duration.minute,
|
|
seconds=sign * duration.second,
|
|
),
|
|
)
|
|
|
|
|
|
def parse_date_duration(date_str: str, sign: int) -> Duration:
|
|
date_designators = OrderedDict(
|
|
(("Y", "years"), ("M", "months"), ("D", "days"), ("W", "weeks"))
|
|
)
|
|
|
|
duration = DateDuration()
|
|
tmp_value = ""
|
|
|
|
for idx, ch in enumerate(date_str):
|
|
if is_time(ch):
|
|
if tmp_value != "" and tmp_value == date_str[:idx]:
|
|
# PYYYY-MM-DDThh:mm:ss
|
|
# PYYYYMMDDThhmmss
|
|
return parse_datetime_duration(date_str, sign)
|
|
|
|
time_idx = idx + 1
|
|
time_str = date_str[time_idx:]
|
|
|
|
if time_str == "":
|
|
raise NoTime("Wanted time, no time provided")
|
|
|
|
return Duration(duration, parse_time_duration(time_str, sign))
|
|
|
|
if is_letter(ch):
|
|
try:
|
|
key = parse_designator(date_designators, ch)
|
|
value = sign * Decimal(tmp_value)
|
|
except OutOfDesignators as exc:
|
|
raise IncorrectDesignator(
|
|
f"Wrong date designator, or designator in the wrong order: {ch}"
|
|
) from exc
|
|
except InvalidOperation as exc:
|
|
raise UnparseableValue(
|
|
f"Value could not be parsed as decimal: {tmp_value}"
|
|
) from exc
|
|
|
|
if is_week(ch) and duration != DateDuration():
|
|
raise IncorrectDesignator(
|
|
"Week is incompatible with any other date designator"
|
|
)
|
|
|
|
setattr(duration, key, value)
|
|
tmp_value = ""
|
|
|
|
continue
|
|
|
|
if is_number(ch):
|
|
if ch == ",":
|
|
tmp_value += "."
|
|
else:
|
|
tmp_value += ch
|
|
|
|
continue
|
|
|
|
raise UnknownToken(f"Token not recognizable: {ch}")
|
|
|
|
return Duration(duration, TimeDuration())
|
|
|
|
|
|
def parse_time_duration(time_str: str, sign: int) -> TimeDuration:
|
|
time_designators = OrderedDict((("H", "hours"), ("M", "minutes"), ("S", "seconds")))
|
|
|
|
duration = TimeDuration()
|
|
tmp_value = ""
|
|
|
|
for ch in time_str:
|
|
if is_letter(ch):
|
|
try:
|
|
key = parse_designator(time_designators, ch)
|
|
value = sign * Decimal(tmp_value)
|
|
except OutOfDesignators as exc:
|
|
raise IncorrectDesignator(
|
|
f"Wrong time designator, or designator in the wrong order: {ch}"
|
|
) from exc
|
|
except InvalidOperation as exc:
|
|
raise UnparseableValue(
|
|
f"Value could not be parsed as decimal: {tmp_value}"
|
|
) from exc
|
|
|
|
setattr(duration, key, value)
|
|
tmp_value = ""
|
|
|
|
continue
|
|
|
|
if is_number(ch):
|
|
if ch == ",":
|
|
tmp_value += "."
|
|
else:
|
|
tmp_value += ch
|
|
|
|
continue
|
|
|
|
raise UnknownToken(f"Token not recognizable: {ch}")
|
|
|
|
return duration
|