Skip to content

Commit d955e13

Browse files
Fix invalid date formats (#4460)
1 parent c3ae84a commit d955e13

2 files changed

Lines changed: 40 additions & 7 deletions

File tree

custom_components/battery_notes/common.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Common functions for battery_notes."""
22

3+
import re
34
from datetime import datetime
45

56
from homeassistant.helpers.device_registry import DeviceEntry
@@ -23,6 +24,29 @@ def utcnow_no_timezone() -> datetime:
2324
return dt_util.utcnow().replace(tzinfo=None)
2425

2526

27+
def fix_datetime_string(datetime_str) -> str:
28+
"""Fix datetime string by replacing colon with period before microseconds."""
29+
# Prior to 3.3.2 there was an issue where microseconds were formatted with a colon and are held in storage.
30+
# New dates are stored correctly, over time the last_reported, last_replaced will be updated with the correct format.
31+
32+
# Look for timezone offset at the end (e.g., +00:00, -05:00, Z)
33+
tz_match = re.search(r"([+-]\d{2}:\d{2}|[+-]\d{4}|Z)$", datetime_str)
34+
35+
if tz_match:
36+
# Split into datetime and timezone parts
37+
tz_start = tz_match.start()
38+
datetime_part = datetime_str[:tz_start]
39+
tz_part = datetime_str[tz_start:]
40+
else:
41+
datetime_part = datetime_str
42+
tz_part = ""
43+
44+
# Replace colon with period only if followed by exactly 6 digits (microseconds)
45+
datetime_part = re.sub(r":(\d{6})$", r".\1", datetime_part)
46+
47+
return datetime_part + tz_part
48+
49+
2650
def get_device_model_id(device_entry: DeviceEntry) -> str | None:
2751
"""Get the device model if available."""
2852
return device_entry.model_id if hasattr(device_entry, "model_id") else None

custom_components/battery_notes/coordinator.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
3030
from homeassistant.util.hass_dict import HassKey
3131

32-
from .common import utcnow_no_timezone, validate_is_float
32+
from .common import fix_datetime_string, utcnow_no_timezone, validate_is_float
3333
from .const import (
3434
ATTR_BATTERY_LAST_REPLACED,
3535
ATTR_BATTERY_LEVEL,
@@ -636,11 +636,15 @@ def last_replaced(self) -> datetime | None:
636636
self.device_id
637637
)
638638

639-
if entry:
640-
if LAST_REPLACED in entry and entry[LAST_REPLACED] is not None:
641-
entry_last_replaced = str(entry[LAST_REPLACED])
642-
if not entry_last_replaced.endswith("+00:00"):
643-
entry_last_replaced += "+00:00"
639+
if entry and LAST_REPLACED in entry and entry[LAST_REPLACED] is not None:
640+
entry_last_replaced = str(entry[LAST_REPLACED])
641+
if not entry_last_replaced.endswith("+00:00"):
642+
entry_last_replaced += "+00:00"
643+
644+
try:
645+
return datetime.fromisoformat(entry_last_replaced)
646+
except ValueError:
647+
entry_last_replaced = fix_datetime_string(entry_last_replaced)
644648
return datetime.fromisoformat(entry_last_replaced)
645649
return None
646650

@@ -677,7 +681,12 @@ def last_reported(self) -> datetime | None:
677681
entry_last_reported = str(entry[LAST_REPORTED])
678682
if not entry_last_reported.endswith("+00:00"):
679683
entry_last_reported += "+00:00"
680-
return datetime.fromisoformat(entry_last_reported)
684+
685+
try:
686+
return datetime.fromisoformat(entry_last_reported)
687+
except ValueError:
688+
entry_last_reported = fix_datetime_string(entry_last_reported)
689+
return datetime.fromisoformat(entry_last_reported)
681690

682691
return None
683692

0 commit comments

Comments
 (0)