diff --git a/backend/app/services/garmin_connect_sync.py b/backend/app/services/garmin_connect_sync.py index bad334f..c2244f8 100644 --- a/backend/app/services/garmin_connect_sync.py +++ b/backend/app/services/garmin_connect_sync.py @@ -555,10 +555,15 @@ def _parse_day(stats, sleep_data, hrv_data) -> dict: _set(row, "sleep_rem_s", dto.get("remSleepSeconds")) _set(row, "sleep_awake_s", dto.get("awakeSleepSeconds")) - # Timestamps are milliseconds since epoch in local time - for key, col in (("sleepStartTimestampLocal", "sleep_start"), - ("sleepEndTimestampLocal", "sleep_end")): - ms = dto.get(key) + # Use the GMT timestamps (true epoch-ms instants). The *Local fields are + # the GMT value pre-shifted by the local UTC offset, so storing them as + # UTC and letting the frontend convert to local double-applies the offset + # (sleep displayed +1h in BST). GMT stored as UTC converts back correctly. + for gmt_key, local_key, col in ( + ("sleepStartTimestampGMT", "sleepStartTimestampLocal", "sleep_start"), + ("sleepEndTimestampGMT", "sleepEndTimestampLocal", "sleep_end"), + ): + ms = dto.get(gmt_key) or dto.get(local_key) if ms: _set(row, col, datetime.fromtimestamp(ms / 1000, tz=timezone.utc).isoformat())