278 files
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user