This commit is contained in:
+8
-3
@@ -64,8 +64,8 @@ def parse_rrule(value: str) -> dict[str, str]:
|
||||
raise HTTPException(422, "无效的 RRULE")
|
||||
key, val = part.split("=", 1)
|
||||
parts[key] = val
|
||||
if parts.get("FREQ") not in {"DAILY", "WEEKLY", "MONTHLY"}:
|
||||
raise HTTPException(422, "仅支持 DAILY、WEEKLY、MONTHLY")
|
||||
if parts.get("FREQ") not in {"DAILY", "WEEKLY", "MONTHLY", "YEARLY"}:
|
||||
raise HTTPException(422, "仅支持 DAILY、WEEKLY、MONTHLY、YEARLY")
|
||||
try:
|
||||
if "INTERVAL" in parts and int(parts["INTERVAL"]) < 1:
|
||||
raise ValueError
|
||||
@@ -103,10 +103,15 @@ def occurrences(rule: str, starts: datetime, start: datetime, end: datetime, cut
|
||||
elif parts["FREQ"] == "WEEKLY":
|
||||
days = {_WEEKDAYS[x] for x in parts.get("BYDAY", list(_WEEKDAYS)[starts.weekday()]).split(",")}
|
||||
include = cursor.weekday() in days and ((cursor.date() - starts.date()).days // 7) % interval == 0
|
||||
else:
|
||||
elif parts["FREQ"] == "MONTHLY":
|
||||
month_delta = (cursor.year - starts.year) * 12 + cursor.month - starts.month
|
||||
month_days = {int(x) for x in parts.get("BYMONTHDAY", str(starts.day)).split(",")}
|
||||
include = month_delta % interval == 0 and cursor.day in month_days
|
||||
else:
|
||||
years = cursor.year - starts.year
|
||||
months = {int(x) for x in parts.get("BYMONTH", str(starts.month)).split(",")}
|
||||
month_days = {int(x) for x in parts.get("BYMONTHDAY", str(starts.day)).split(",")}
|
||||
include = years % interval == 0 and cursor.month in months and cursor.day in month_days
|
||||
if include and cursor >= starts:
|
||||
emitted += 1
|
||||
if start <= cursor <= end:
|
||||
|
||||
@@ -31,10 +31,18 @@ def test_recurring_calendar_exceptions_and_scopes(client):
|
||||
)
|
||||
assert recurrence.status_code == 201
|
||||
recurrence_id = recurrence.json()["id"]
|
||||
yearly = client.post(
|
||||
"/api/v1/tasks",
|
||||
json={"title": "年度任务", "list_id": inbox["id"], "due_at": "2026-09-06T09:00:00Z"},
|
||||
).json()
|
||||
assert client.post(
|
||||
"/api/v1/recurrences", json={"task_id": yearly["id"], "rrule": "FREQ=YEARLY;INTERVAL=1;BYMONTH=9;BYMONTHDAY=6"}
|
||||
).status_code == 201
|
||||
calendar = client.get(
|
||||
"/api/v1/calendar", params={"start": "2026-09-01", "end": "2026-09-30"}
|
||||
).json()
|
||||
assert any(row.get("id") == normal_task["id"] for row in calendar)
|
||||
assert any(row["title"] == "年度任务" for row in calendar)
|
||||
assert not any(row["title"] == "月外普通任务" for row in calendar)
|
||||
occurrences = [row for row in calendar if row["recurrence_id"] == recurrence_id]
|
||||
assert len(occurrences) == 5
|
||||
|
||||
Reference in New Issue
Block a user