From 36e6ab4af88b5db8997719c4f0b91483e6dfdadc Mon Sep 17 00:00:00 2001 From: "Mr. Bubbles" Date: Sun, 22 Sep 2024 12:08:50 +0200 Subject: [PATCH] Fix due date calculation for future dailies in Habitica integration (#126403) Calculate next due date for dailies with startdate in the future --- homeassistant/components/habitica/util.py | 41 +++++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/habitica/util.py b/homeassistant/components/habitica/util.py index b3241aa5787..0ac3ea2a4e2 100644 --- a/homeassistant/components/habitica/util.py +++ b/homeassistant/components/habitica/util.py @@ -3,7 +3,7 @@ from __future__ import annotations import datetime -from typing import Any +from typing import TYPE_CHECKING, Any from homeassistant.components.automation import automations_with_entity from homeassistant.components.script import scripts_with_entity @@ -14,25 +14,44 @@ from homeassistant.util import dt as dt_util def next_due_date(task: dict[str, Any], last_cron: str) -> datetime.date | None: """Calculate due date for dailies and yesterdailies.""" + today = to_date(last_cron) + startdate = to_date(task["startDate"]) + if TYPE_CHECKING: + assert today + assert startdate + if task["isDue"] and not task["completed"]: - return dt_util.as_local(datetime.datetime.fromisoformat(last_cron)).date() + return to_date(last_cron) + + if startdate > today: + if task["frequency"] == "daily" or ( + task["frequency"] in ("monthly", "yearly") and task["daysOfMonth"] + ): + return startdate + + if ( + task["frequency"] in ("weekly", "monthly") + and (nextdue := to_date(task["nextDue"][0])) + and startdate > nextdue + ): + return to_date(task["nextDue"][1]) + + return to_date(task["nextDue"][0]) + + +def to_date(date: str) -> datetime.date | None: + """Convert an iso date to a datetime.date object.""" try: - return dt_util.as_local( - datetime.datetime.fromisoformat(task["nextDue"][0]) - ).date() + return dt_util.as_local(datetime.datetime.fromisoformat(date)).date() except ValueError: - # sometimes nextDue dates are in this format instead of iso: + # sometimes nextDue dates are JavaScript datetime strings instead of iso: # "Mon May 06 2024 00:00:00 GMT+0200" try: return dt_util.as_local( - datetime.datetime.strptime( - task["nextDue"][0], "%a %b %d %Y %H:%M:%S %Z%z" - ) + datetime.datetime.strptime(date, "%a %b %d %Y %H:%M:%S %Z%z") ).date() except ValueError: return None - except IndexError: - return None def entity_used_in(hass: HomeAssistant, entity_id: str) -> list[str]: