-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrecurrence.py
107 lines (88 loc) · 3.66 KB
/
recurrence.py
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
from enum import Enum
from datetime import date, timedelta
from typing import Optional
# Enum defining the recurrence types
class RecurrenceType(Enum):
DAILY = 'daily'
WEEKLY = 'weekly'
MONTHLY = 'monthly'
YEARLY = 'yearly'
# Class for handling recurrences
class Recurrence:
_DEFAULT_INTERVAL = 1 # Default interval for recurrences
def __init__(self, recurrence_type: RecurrenceType, interval: int = _DEFAULT_INTERVAL):
"""
Initializes a Recurrence object.
Args:
recurrence_type (RecurrenceType): The type of recurrence (e.g., daily, weekly).
interval (int): The interval between recurrences (default is 1).
"""
if not isinstance(recurrence_type, RecurrenceType):
raise ValueError("recurrence_type must be a valid RecurrenceType")
self._recurrence_type = recurrence_type
self._interval = interval # Number of units for the recurrence (default is 1)
@property
def recurrence_type(self) -> RecurrenceType:
"""Getter for recurrence_type property."""
return self._recurrence_type
@recurrence_type.setter
def recurrence_type(self, value: RecurrenceType) -> None:
"""
Setter for recurrence_type property.
Args:
value (RecurrenceType): The new recurrence type to set.
"""
if not isinstance(value, RecurrenceType):
raise ValueError("recurrence_type must be a valid RecurrenceType")
self._recurrence_type = value
@property
def interval(self) -> int:
"""Getter for interval property."""
return self._interval
@interval.setter
def interval(self, value: int) -> None:
"""
Setter for interval property.
Args:
value (int): The new interval to set.
"""
if not isinstance(value, int):
raise ValueError("interval must be an integer")
self._interval = value
def __str__(self):
"""
Returns a string representation of the Recurrence object.
Returns:
str: String representation of the recurrence.
"""
if self._interval == 1:
return f"Recurrence: {self._recurrence_type.value}"
else:
return f"Recurrence: {self._interval} {self._recurrence_type.value}(s)"
def next_occurrence(self, last_occurrence: date) -> Optional[date]:
"""
Calculates the next occurrence date based on the last occurrence date.
Args:
last_occurrence (date): The last occurrence date.
Returns:
Optional[date]: The next occurrence date, or None if the recurrence type is not recognized.
"""
if self._recurrence_type == RecurrenceType.DAILY:
new_date = last_occurrence + timedelta(days=self._interval)
elif self._recurrence_type == RecurrenceType.WEEKLY:
new_date = last_occurrence + timedelta(weeks=self._interval)
elif self._recurrence_type == RecurrenceType.MONTHLY:
new_date = self.add_months(last_occurrence, self._interval)
elif self._recurrence_type == RecurrenceType.YEARLY:
new_date = last_occurrence.replace(year=last_occurrence.year + self._interval)
else:
return None
return new_date
@staticmethod
def add_months(source_date, months):
# Helper function to add months to a date
month = source_date.month - 1 + months
year = source_date.year + month // 12
month = month % 12 + 1
day = min(source_date.day, [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month])
return source_date.replace(year=year, month=month, day=day)