File: duration.py

package info (click to toggle)
python-ical 9.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,448 kB
  • sloc: python: 13,877; sh: 9; makefile: 5
file content (75 lines) | stat: -rw-r--r-- 2,490 bytes parent folder | download
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
"""Library for parsing and encoding DURATION values."""

import datetime
import re

from ical.parsing.property import ParsedProperty

from .data_types import DATA_TYPE

DATE_PART = r"(\d+)D"
TIME_PART = r"T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
DATETIME_PART = f"(?:{DATE_PART})?(?:{TIME_PART})?"
WEEKS_PART = r"(\d+)W"
DURATION_REGEX = re.compile(f"([-+]?)P(?:{WEEKS_PART}|{DATETIME_PART})$")


@DATA_TYPE.register("DURATION")
class DurationEncoder:
    """Class that can encode DURATION values."""

    @classmethod
    def __property_type__(cls) -> type:
        return datetime.timedelta

    @classmethod
    def __parse_property_value__(cls, prop: ParsedProperty) -> datetime.timedelta:
        """Parse a rfc5545 into a datetime.date."""
        if not isinstance(prop, ParsedProperty):
            raise ValueError(f"Expected ParsedProperty but was {prop}")
        if not (match := DURATION_REGEX.fullmatch(prop.value)):
            raise ValueError(f"Expected value to match DURATION pattern: {prop.value}")
        sign, weeks, days, hours, minutes, seconds = match.groups()
        result: datetime.timedelta
        if weeks:
            result = datetime.timedelta(weeks=int(weeks))
        else:
            result = datetime.timedelta(
                days=int(days or 0),
                hours=int(hours or 0),
                minutes=int(minutes or 0),
                seconds=int(seconds or 0),
            )
        if sign == "-":
            result = -result
        return result

    @classmethod
    def __encode_property_json__(cls, duration: datetime.timedelta) -> str:
        """Serialize a time delta as a DURATION ICS value."""
        parts = []
        if duration < datetime.timedelta(days=0):
            parts.append("-")
            duration = -duration
        parts.append("P")
        days = duration.days
        weeks = int(days / 7)
        days %= 7
        if weeks > 0:
            parts.append(f"{weeks}W")
        if days > 0:
            parts.append(f"{days}D")
        if duration.seconds != 0:
            parts.append("T")
            seconds = duration.seconds
            hours = int(seconds / 3600)
            seconds %= 3600
            if hours != 0:
                parts.append(f"{hours}H")
            minutes = int(seconds / 60)
            seconds %= 60
            if minutes != 0:
                parts.append(f"{minutes}M")
            if seconds != 0:
                parts.append(f"{seconds}S")
        return "".join(parts)