Skip to content

Commit

Permalink
Use UTC datetimes internally (#84)
Browse files Browse the repository at this point in the history
Python datetime object comparisons and math don't work as expected
when the objects are aware and have the same tzinfo attribute.
Basically, the fold attribute is ignored in this case, which can lead to
wrong results. Avoid this problem by using aware times in UTC internally.
Only use local time zone for user visible attributes.
  • Loading branch information
pnbruckner authored Nov 20, 2024
1 parent aa45575 commit 372ea19
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 13 deletions.
30 changes: 19 additions & 11 deletions custom_components/composite/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,13 @@ async def _restore_state(self) -> None:
last_seen = dt_util.parse_datetime(
self._attr_extra_state_attributes[ATTR_LAST_SEEN]
)
self._attr_extra_state_attributes[ATTR_LAST_SEEN] = last_seen
self._prev_seen = last_seen
if last_seen is None:
self._attr_extra_state_attributes[ATTR_LAST_SEEN] = None
else:
self._attr_extra_state_attributes[ATTR_LAST_SEEN] = dt_util.as_local(
last_seen
)
self._prev_seen = dt_util.as_utc(last_seen)
if self.source_type in _SOURCE_TYPE_NON_GPS and (
self.latitude is None or self.longitude is None
):
Expand Down Expand Up @@ -410,21 +415,21 @@ async def _entity_updated( # noqa: C901
# attributes defined by _LAST_SEEN_ATTRS, as a datetime.

def get_last_seen() -> datetime | None:
"""Get last_seen from one of the possible attributes."""
"""Get last_seen (in UTC) from one of the possible attributes."""
if (raw_last_seen := new_attrs.get(_LAST_SEEN_ATTRS)) is None:
return None
if isinstance(raw_last_seen, datetime):
return raw_last_seen
return dt_util.as_utc(raw_last_seen)
with suppress(TypeError, ValueError):
return dt_util.utc_from_timestamp(float(raw_last_seen))
with suppress(TypeError):
return dt_util.parse_datetime(raw_last_seen)
if (parsed_last_seen := dt_util.parse_datetime(raw_last_seen)) is None:
return None
return dt_util.as_utc(parsed_last_seen)
return None

# Make sure last_seen is timezone aware in local timezone.
# Note that dt_util.as_local assumes naive datetime is in local timezone.
# Use last_updated from the new state object if no valid "last seen" was found.
last_seen = dt_util.as_local(get_last_seen() or new_state.last_updated)
last_seen = get_last_seen() or new_state.last_updated

old_last_seen = entity.seen
if old_last_seen and last_seen < old_last_seen:
Expand Down Expand Up @@ -549,8 +554,8 @@ def get_last_seen() -> datetime | None:
"last_seen not newer than previous update (%s) <= (%s)",
self.entity_id,
entity_id,
last_seen,
self._prev_seen,
dt_util.as_local(last_seen),
dt_util.as_local(self._prev_seen),
)
return

Expand All @@ -563,7 +568,7 @@ def get_last_seen() -> datetime | None:
if _entity.source_type
),
ATTR_LAST_ENTITY_ID: entity_id,
ATTR_LAST_SEEN: _nearest_second(last_seen),
ATTR_LAST_SEEN: dt_util.as_local(_nearest_second(last_seen)),
}
if charging is not None:
attrs[ATTR_BATTERY_CHARGING] = charging
Expand Down Expand Up @@ -617,6 +622,9 @@ def _set_state(
assert attributes
last_ent = cast(str, attributes[ATTR_LAST_ENTITY_ID])
last_seen = cast(datetime, attributes[ATTR_LAST_SEEN])
# It's ok that last_seen is in local tz and self._prev_seen is in UTC.
# last_seen's value will automatically be converted to UTC during the
# subtraction operation.
seconds = (last_seen - self._prev_seen).total_seconds()
min_seconds = MIN_SPEED_SECONDS
if last_ent != prev_ent:
Expand Down
4 changes: 2 additions & 2 deletions custom_components/composite/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"codeowners": ["@pnbruckner"],
"config_flow": true,
"dependencies": ["file_upload"],
"documentation": "https://github.com/pnbruckner/ha-composite-tracker/blob/3.4.4/README.md",
"documentation": "https://github.com/pnbruckner/ha-composite-tracker/blob/3.4.5b0/README.md",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/pnbruckner/ha-composite-tracker/issues",
"requirements": ["filetype==1.2.0"],
"version": "3.4.4"
"version": "3.4.5b0"
}

0 comments on commit 372ea19

Please sign in to comment.