278 files

This commit is contained in:
Home Assistant Version Control
2026-09-13 20:38:58 +00:00
parent d7a0a08372
commit 1b4f5f68c6
278 changed files with 36892 additions and 7150 deletions
@@ -10,13 +10,57 @@ from __future__ import annotations
import calendar
from collections.abc import Iterator
from datetime import UTC, date, datetime, timedelta
from datetime import UTC, date, datetime, time, timedelta
from typing import Any
from homeassistant.util import dt as dt_util
from ..const import TIME_HHMMSS_PATTERN
from .workday import is_business_day
INTERVAL_UNITS = ("days", "weeks", "months", "years")
def parse_hhmm(value: Any) -> time | None:
"""``"HH:MM"`` or ``"HH:MM:SS"`` (HA's TimeSelector) → ``time`` to the
minute; ``None`` when absent or malformed (hours 0–23, minutes 0–59).
The one parser behind the status sensor, the calendar entity and the
model's sub-day overdue refinement — three hand-copied ``split(":")``
blocks before — and behind :func:`normalize_hhmm`.
"""
if not isinstance(value, str):
return None
text = value.strip()
if not TIME_HHMMSS_PATTERN.match(text):
return None
hours, minutes = text.split(":")[:2]
return time(int(hours), int(minutes))
def local_date_from_iso(value: Any) -> str | None:
"""The calendar date (``YYYY-MM-DD``) of an ISO timestamp *in HA's local
zone*. ``value[:10]`` is right for stamps written locally but off by one
around midnight for imported ``+00:00`` stamps; this is the one place
that does it properly. ``None`` when the value is not a timestamp."""
if not isinstance(value, str) or not value:
return None
try:
parsed = datetime.fromisoformat(value)
except ValueError:
return value[:10] if len(value) >= 10 and value[4] == "-" else None
if parsed.tzinfo is None:
return parsed.date().isoformat()
return dt_util.as_local(parsed).date().isoformat()
def normalize_hhmm(value: Any) -> str | None:
"""Canonical ``"HH:MM"`` for a stored time, dropping TimeSelector seconds;
``None`` when the value does not parse (the caller drops the field)."""
parsed = parse_hhmm(value)
return f"{parsed.hour:02d}:{parsed.minute:02d}" if parsed is not None else None
def parse_iso_date(value: str | None) -> date | None:
"""ISO date string → ``date``; ``None`` when absent or malformed."""
if not value: