[verified] feat: support monthly completion recurrence
ci / gitleaks (push) Successful in 23s
ci / docker (push) Successful in 7m16s

This commit is contained in:
2026-09-20 21:04:31 +08:00
parent e6d720b85c
commit 3155155f53
13 changed files with 193 additions and 39 deletions
+13 -3
View File
@@ -2,6 +2,7 @@ from datetime import UTC, datetime, time, timedelta
from uuid import UUID
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from dateutil.relativedelta import relativedelta
from fastapi import HTTPException
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
@@ -25,10 +26,15 @@ def _user_zone(user: User) -> ZoneInfo:
raise HTTPException(422, "用户时区无效") from exc
def _after_completion_due(task: Task, completed_at: datetime, days: int, user: User) -> datetime:
def _after_completion_due(
task: Task, completed_at: datetime, value: int, unit: str | None, user: User
) -> datetime:
zone = _user_zone(user)
completed_local = completed_at.astimezone(zone)
target_date = completed_local.date() + timedelta(days=days)
if unit == "months":
target_date = completed_local.date() + relativedelta(months=value)
else:
target_date = completed_local.date() + timedelta(days=value)
if task.due_has_time:
due_local = task.due_at.astimezone(zone)
wall_time = due_local.timetz().replace(tzinfo=None)
@@ -67,7 +73,11 @@ async def apply_task_changes(
if recurrence.trigger_mode == "after_completion":
completed_at = utcnow()
next_due = _after_completion_due(
task, completed_at, recurrence.after_completion_days, user
task,
completed_at,
recurrence.after_completion_days,
recurrence.after_completion_unit,
user,
)
changes["completed"] = False
changes["due_at"] = next_due