0
# Recurrence Rules
1
2
Complete implementation of iCalendar recurrence rules (RFC 5545) for generating recurring dates. Supports complex patterns, exclusions, caching, and efficient iteration over large date ranges.
3
4
## Capabilities
5
6
### rrule Class
7
8
Core recurrence rule implementation supporting all iCalendar recurrence patterns with optional caching for performance.
9
10
```python { .api }
11
class rrule:
12
def __init__(self, freq, dtstart=None, interval=1, wkst=None, count=None,
13
until=None, bysetpos=None, bymonth=None, bymonthday=None,
14
byyearday=None, byweekno=None, byweekday=None, byhour=None,
15
byminute=None, bysecond=None, cache=False):
16
"""
17
Create a recurrence rule.
18
19
Parameters:
20
- freq (int): Frequency constant (YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY)
21
- dtstart (datetime, optional): Start date/time for recurrence
22
- interval (int): Interval between recurrences (default: 1)
23
- wkst (int, optional): Week start day (0=Monday, 6=Sunday)
24
- count (int, optional): Maximum number of occurrences
25
- until (datetime, optional): End date/time for recurrence
26
- bysetpos (int/list, optional): Position in set of occurrences
27
- bymonth (int/list, optional): Months (1-12)
28
- bymonthday (int/list, optional): Days of month (1-31, -31 to -1)
29
- byyearday (int/list, optional): Days of year (1-366, -366 to -1)
30
- byweekno (int/list, optional): Week numbers (1-53, -53 to -1)
31
- byweekday (int/weekday/list, optional): Weekdays
32
- byhour (int/list, optional): Hours (0-23)
33
- byminute (int/list, optional): Minutes (0-59)
34
- bysecond (int/list, optional): Seconds (0-59)
35
- cache (bool): Enable caching for performance
36
"""
37
38
def between(self, after, before, inc=False):
39
"""
40
Generate occurrences between two dates.
41
42
Parameters:
43
- after (datetime): Start date (exclusive)
44
- before (datetime): End date (exclusive)
45
- inc (bool): Include boundary dates if True
46
47
Returns:
48
list[datetime]: List of occurrences in range
49
"""
50
51
def before(self, dt, inc=False):
52
"""
53
Get occurrence before specified date.
54
55
Parameters:
56
- dt (datetime): Reference date
57
- inc (bool): Include dt if it's an occurrence
58
59
Returns:
60
datetime | None: Previous occurrence or None
61
"""
62
63
def after(self, dt, inc=False):
64
"""
65
Get occurrence after specified date.
66
67
Parameters:
68
- dt (datetime): Reference date
69
- inc (bool): Include dt if it's an occurrence
70
71
Returns:
72
datetime | None: Next occurrence or None
73
"""
74
75
def count(self):
76
"""
77
Get total number of occurrences.
78
79
Returns:
80
int: Total count (may be infinite)
81
"""
82
```
83
84
**Usage Examples:**
85
86
```python
87
from dateutil.rrule import rrule, DAILY, WEEKLY, MONTHLY, YEARLY
88
from datetime import datetime
89
90
# Daily recurrence for 10 days
91
start = datetime(2023, 1, 1, 9, 0)
92
daily = rrule(DAILY, dtstart=start, count=10)
93
dates = list(daily)
94
95
# Weekly on Mondays and Wednesdays for 8 weeks
96
weekly = rrule(WEEKLY, dtstart=start, count=16, byweekday=(0, 2)) # Monday, Wednesday
97
98
# Monthly on 15th of each month for 1 year
99
monthly = rrule(MONTHLY, dtstart=start, count=12, bymonthday=15)
100
101
# Yearly on specific date until end date
102
until_date = datetime(2030, 12, 31)
103
yearly = rrule(YEARLY, dtstart=start, until=until_date, bymonth=12, bymonthday=25)
104
105
# Complex pattern: Every other Tuesday in January and July
106
complex_rule = rrule(WEEKLY, dtstart=start, interval=2,
107
byweekday=1, bymonth=(1, 7)) # Tuesday = 1
108
```
109
110
### Frequency Constants
111
112
Predefined constants for specifying recurrence frequency.
113
114
```python { .api }
115
YEARLY = 0 # Annual recurrence
116
MONTHLY = 1 # Monthly recurrence
117
WEEKLY = 2 # Weekly recurrence
118
DAILY = 3 # Daily recurrence
119
HOURLY = 4 # Hourly recurrence
120
MINUTELY = 5 # Per-minute recurrence
121
SECONDLY = 6 # Per-second recurrence
122
```
123
124
### Weekday Constants and Class
125
126
Weekday specifications for use in recurrence rules, supporting nth occurrence patterns.
127
128
```python { .api }
129
# Weekday constants (Monday=0 to Sunday=6)
130
MO = weekday(0)
131
TU = weekday(1)
132
WE = weekday(2)
133
TH = weekday(3)
134
FR = weekday(4)
135
SA = weekday(5)
136
SU = weekday(6)
137
138
class weekday:
139
def __init__(self, wkday, n=None):
140
"""
141
Weekday specification for rrules.
142
143
Parameters:
144
- wkday (int): Weekday number (0=Monday, 6=Sunday)
145
- n (int, optional): Nth occurrence (1 for first, -1 for last, etc.)
146
147
Note: Unlike relativedelta.weekday, n=0 is not allowed
148
149
Raises:
150
ValueError: If n is 0
151
"""
152
```
153
154
**Usage Examples:**
155
156
```python
157
from dateutil.rrule import rrule, MONTHLY, MO, FR
158
from datetime import datetime
159
160
start = datetime(2023, 1, 1)
161
162
# Every first Monday of each month
163
first_monday = rrule(MONTHLY, dtstart=start, byweekday=MO(1))
164
165
# Every last Friday of each month
166
last_friday = rrule(MONTHLY, dtstart=start, byweekday=FR(-1))
167
168
# Every second and fourth Tuesday
169
second_fourth_tue = rrule(MONTHLY, dtstart=start, byweekday=(TU(2), TU(4)))
170
171
# All Mondays and Fridays (no nth specification)
172
mon_fri = rrule(WEEKLY, dtstart=start, byweekday=(MO, FR))
173
```
174
175
### rruleset Class
176
177
Collection class for combining multiple rrules with inclusion and exclusion rules.
178
179
```python { .api }
180
class rruleset:
181
def __init__(self, cache=False):
182
"""
183
Create a set of recurrence rules.
184
185
Parameters:
186
- cache (bool): Enable caching for performance
187
"""
188
189
def rrule(self, rrule):
190
"""
191
Add an rrule to the set.
192
193
Parameters:
194
- rrule (rrule): Recurrence rule to include
195
"""
196
197
def rdate(self, rdate):
198
"""
199
Add a single date to the set.
200
201
Parameters:
202
- rdate (datetime): Date to include
203
"""
204
205
def exrule(self, exrule):
206
"""
207
Add an exclusion rule to the set.
208
209
Parameters:
210
- exrule (rrule): Recurrence rule for dates to exclude
211
"""
212
213
def exdate(self, exdate):
214
"""
215
Add an exclusion date to the set.
216
217
Parameters:
218
- exdate (datetime): Date to exclude
219
"""
220
221
# Same iteration methods as rrule
222
def between(self, after, before, inc=False): ...
223
def before(self, dt, inc=False): ...
224
def after(self, dt, inc=False): ...
225
def count(self): ...
226
```
227
228
**Usage Examples:**
229
230
```python
231
from dateutil.rrule import rrule, rruleset, DAILY, WEEKLY, MO, FR
232
from datetime import datetime
233
234
# Create a complex schedule
235
schedule = rruleset()
236
237
# Add daily meetings Monday-Friday
238
start = datetime(2023, 1, 2, 9, 0) # Monday
239
weekdays = rrule(DAILY, dtstart=start, byweekday=(MO, TU, WE, TH, FR))
240
schedule.rrule(weekdays)
241
242
# Add extra meeting on first Friday of each month
243
monthly_meeting = rrule(MONTHLY, dtstart=start, byweekday=FR(1))
244
schedule.rrule(monthly_meeting)
245
246
# Exclude holidays
247
holiday1 = datetime(2023, 7, 4, 9, 0) # July 4th
248
holiday2 = datetime(2023, 12, 25, 9, 0) # Christmas
249
schedule.exdate(holiday1)
250
schedule.exdate(holiday2)
251
252
# Exclude entire holiday week
253
holiday_week = rrule(DAILY, dtstart=datetime(2023, 12, 25, 9, 0), count=5)
254
schedule.exrule(holiday_week)
255
256
# Get all meetings in January 2023
257
jan_meetings = schedule.between(
258
datetime(2023, 1, 1),
259
datetime(2023, 2, 1)
260
)
261
```
262
263
### String Parsing
264
265
Parse recurrence rules from iCalendar-style strings.
266
267
```python { .api }
268
def rrulestr(s, **kwargs):
269
"""
270
Parse rrule from RFC 5545 string format.
271
272
Parameters:
273
- s (str): RRULE string (e.g., "FREQ=DAILY;COUNT=10")
274
- **kwargs: Additional parameters passed to rrule constructor
275
276
Returns:
277
rrule: Parsed recurrence rule
278
279
Raises:
280
ValueError: If string format is invalid
281
"""
282
```
283
284
**Usage Examples:**
285
286
```python
287
from dateutil.rrule import rrulestr
288
from datetime import datetime
289
290
# Parse from iCalendar format
291
rule_str = "FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10"
292
rule = rrulestr(rule_str, dtstart=datetime(2023, 1, 2))
293
294
# More complex pattern
295
complex_str = "FREQ=MONTHLY;BYMONTHDAY=15;BYMONTH=3,6,9,12"
296
quarterly = rrulestr(complex_str, dtstart=datetime(2023, 1, 1))
297
298
# With until date
299
until_str = "FREQ=DAILY;INTERVAL=2;UNTIL=20231231T235959Z"
300
every_other_day = rrulestr(until_str, dtstart=datetime(2023, 1, 1))
301
```
302
303
## Advanced Patterns
304
305
### Caching for Performance
306
307
```python
308
from dateutil.rrule import rrule, DAILY
309
from datetime import datetime
310
311
# Large recurrence with caching
312
start = datetime(2020, 1, 1)
313
large_rule = rrule(DAILY, dtstart=start, count=10000, cache=True)
314
315
# First iteration builds cache
316
dates = list(large_rule) # Slower first time
317
318
# Subsequent operations use cache
319
more_dates = list(large_rule) # Much faster
320
count = large_rule.count() # Instant
321
```
322
323
### Complex Business Rules
324
325
```python
326
from dateutil.rrule import rrule, rruleset, MONTHLY, WEEKLY, MO, TU, WE, TH, FR
327
from datetime import datetime
328
329
def create_business_schedule():
330
"""Create complex business meeting schedule."""
331
schedule = rruleset()
332
start = datetime(2023, 1, 1, 14, 0) # 2 PM meetings
333
334
# Weekly team meetings on Tuesdays
335
team_meetings = rrule(WEEKLY, dtstart=start, byweekday=TU)
336
schedule.rrule(team_meetings)
337
338
# Monthly all-hands on first Thursday
339
all_hands = rrule(MONTHLY, dtstart=start, byweekday=TH(1))
340
schedule.rrule(all_hands)
341
342
# Quarterly board meetings on 15th
343
board_meetings = rrule(MONTHLY, dtstart=start, interval=3, bymonthday=15)
344
schedule.rrule(board_meetings)
345
346
# Exclude summer vacation period
347
vacation_start = datetime(2023, 7, 1, 14, 0)
348
vacation_rule = rrule(DAILY, dtstart=vacation_start, count=14)
349
schedule.exrule(vacation_rule)
350
351
return schedule
352
353
schedule = create_business_schedule()
354
meetings_2023 = schedule.between(
355
datetime(2023, 1, 1),
356
datetime(2024, 1, 1)
357
)
358
```
359
360
### Timezone-Aware Recurrences
361
362
```python
363
from dateutil.rrule import rrule, DAILY
364
from dateutil.tz import gettz
365
from datetime import datetime
366
367
# Timezone-aware recurrence
368
eastern = gettz('America/New_York')
369
start = datetime(2023, 1, 1, 9, 0, tzinfo=eastern)
370
371
daily_meetings = rrule(DAILY, dtstart=start, count=30, byweekday=(MO, TU, WE, TH, FR))
372
373
# All generated dates will be timezone-aware
374
for meeting in daily_meetings:
375
print(f"{meeting} ({meeting.tzinfo})")
376
```
377
378
## Types
379
380
```python { .api }
381
from datetime import datetime
382
383
class rrule:
384
# Iteration protocol
385
def __iter__(self): ...
386
def __getitem__(self, item): ...
387
def __contains__(self, item): ...
388
def __len__(self): ...
389
390
class rruleset:
391
# Same iteration protocol as rrule
392
def __iter__(self): ...
393
def __getitem__(self, item): ...
394
def __contains__(self, item): ...
395
def __len__(self): ...
396
397
class weekday:
398
weekday: int # 0-6 (Monday-Sunday)
399
n: int | None # Nth occurrence (cannot be 0)
400
401
# Frequency constants
402
FrequencyType = int # One of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY
403
404
# Common parameter types
405
DateTimeList = list[datetime] | datetime
406
IntList = list[int] | int
407
WeekdayList = list[weekday | int] | weekday | int
408
```