File size: 3,990 Bytes
d1ceb73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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