fix: preserve Mi Fitness local timezone offsets

This commit is contained in:
binglua committed 2026-06-22 02:10:55 +00:00
1 parent eef795672d
commit c901e712d7
2 files changed
+54 -31

No files matched your search

+23 -12
View File
@@ -6,7 +6,7 @@ import os
import struct
from collections import defaultdict
from collections.abc import AsyncIterator
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta, timezone
from typing import Any
from urllib.parse import urlencode
@@ -184,6 +184,21 @@ class MiFitnessCloudAdapter(DataAdapter):
await asyncio.sleep(0.5 * (attempt + 1))
raise RuntimeError(f"Mi Fitness request failed: {last_error}")
def _request_timezone(self, region: str | None = None) -> timezone:
region_name = self.region if region is None else region
if region_name in ("", "cn"):
return timezone(timedelta(hours=8))
return UTC
def _date_range_to_timestamps(
self, start_date: str, end_date: str, region: str | None = None
) -> tuple[int, int]:
tz = self._request_timezone(region)
start_dt = datetime.fromisoformat(start_date).replace(tzinfo=tz)
end_dt = datetime.fromisoformat(end_date + "T23:59:59").replace(tzinfo=tz)
return int(start_dt.timestamp()), int(end_dt.timestamp())
async def _fetch_key(
self, key: str, start_date: str, end_date: str, region: str | None = None
) -> list[dict]:
@@ -193,10 +208,7 @@ class MiFitnessCloudAdapter(DataAdapter):
if region_name in ("", "cn")
else f"https://{region_name}.hlth.io.mi.com"
)
start_time = int(datetime.fromisoformat(start_date).replace(tzinfo=UTC).timestamp())
end_time = int(
datetime.fromisoformat(end_date + "T23:59:59").replace(tzinfo=UTC).timestamp()
)
start_time, end_time = self._date_range_to_timestamps(start_date, end_date, region_name)
next_key = None
items: list[dict] = []
@@ -259,8 +271,9 @@ class MiFitnessCloudAdapter(DataAdapter):
def _record_datetime(self, item: dict) -> datetime:
timestamp = int(item.get("time", 0))
zone_offset = int(item.get("zone_offset", 0))
return datetime.fromtimestamp(timestamp + zone_offset, tz=UTC).replace(tzinfo=None)
zone_offset = int(item.get("zone_offset", 0) or 0)
tz = timezone(timedelta(seconds=zone_offset))
return datetime.fromtimestamp(timestamp, tz=tz)
def _parse_value(self, item: dict) -> dict[str, Any]:
raw = item.get("value", "{}")
@@ -269,7 +282,8 @@ class MiFitnessCloudAdapter(DataAdapter):
return json.loads(raw)
def _timestamp_to_datetime(self, timestamp: Any, zone_offset: int = 0) -> datetime:
return datetime.fromtimestamp(int(timestamp) + int(zone_offset), tz=UTC).replace(tzinfo=None)
tz = timezone(timedelta(seconds=int(zone_offset or 0)))
return datetime.fromtimestamp(int(timestamp), tz=tz)
async def _fetch_sport_records_by_time(
self, start_date: str, end_date: str, region: str | None = None
@@ -280,10 +294,7 @@ class MiFitnessCloudAdapter(DataAdapter):
if region_name in ("", "cn")
else f"https://{region_name}.hlth.io.mi.com"
)
start_time = int(datetime.fromisoformat(start_date).replace(tzinfo=UTC).timestamp())
end_time = int(
datetime.fromisoformat(end_date + "T23:59:59").replace(tzinfo=UTC).timestamp()
)
start_time, end_time = self._date_range_to_timestamps(start_date, end_date, region_name)
next_key = None
items: list[dict] = []
+31 -19
View File
@@ -457,7 +457,11 @@ class Database:
timezone, collected_at, timestamp, bpm, sample_type
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
timezone = excluded.timezone,
collected_at = excluded.collected_at,
timestamp = excluded.timestamp,
bpm = excluded.bpm,
sample_type = excluded.sample_type,
updated_at = CURRENT_TIMESTAMP
""",
(
@@ -486,6 +490,9 @@ class Database:
timezone, collected_at, timestamp, spo2_pct
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
timezone = excluded.timezone,
collected_at = excluded.collected_at,
timestamp = excluded.timestamp,
spo2_pct = excluded.spo2_pct,
updated_at = CURRENT_TIMESTAMP
""",
@@ -514,6 +521,9 @@ class Database:
timezone, collected_at, timestamp, stress_score, level
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
timezone = excluded.timezone,
collected_at = excluded.collected_at,
timestamp = excluded.timestamp,
stress_score = excluded.stress_score,
level = excluded.level,
updated_at = CURRENT_TIMESTAMP
@@ -544,6 +554,8 @@ class Database:
timezone, collected_at, event_id, start_at, end_at, duration_seconds
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(user_id, event_id) DO UPDATE SET
timezone = excluded.timezone,
collected_at = excluded.collected_at,
start_at = excluded.start_at,
end_at = excluded.end_at,
duration_seconds = excluded.duration_seconds,
@@ -622,7 +634,7 @@ class Database:
"""
SELECT * FROM sleep_sessions
WHERE user_id = ?
AND date(start_at) >= ? AND date(start_at) <= ?
AND substr(start_at, 1, 10) >= ? AND substr(start_at, 1, 10) <= ?
ORDER BY start_at
""",
(user_id, start_date, end_date),
@@ -641,7 +653,7 @@ class Database:
"""
SELECT * FROM workouts
WHERE user_id = ?
AND date(start_at) >= ? AND date(start_at) <= ?
AND substr(start_at, 1, 10) >= ? AND substr(start_at, 1, 10) <= ?
ORDER BY start_at
""",
(user_id, start_date, end_date),
@@ -660,7 +672,7 @@ class Database:
"""
SELECT * FROM body_measurements
WHERE user_id = ?
AND date(timestamp) >= ? AND date(timestamp) <= ?
AND substr(timestamp, 1, 10) >= ? AND substr(timestamp, 1, 10) <= ?
ORDER BY timestamp
""",
(user_id, start_date, end_date),
@@ -678,7 +690,7 @@ class Database:
"""
SELECT * FROM heart_rate_samples
WHERE user_id = ?
AND date(timestamp) >= ? AND date(timestamp) <= ?
AND substr(timestamp, 1, 10) >= ? AND substr(timestamp, 1, 10) <= ?
ORDER BY timestamp
""",
(user_id, start_date, end_date),
@@ -696,7 +708,7 @@ class Database:
"""
SELECT * FROM spo2_samples
WHERE user_id = ?
AND date(timestamp) >= ? AND date(timestamp) <= ?
AND substr(timestamp, 1, 10) >= ? AND substr(timestamp, 1, 10) <= ?
ORDER BY timestamp
""",
(user_id, start_date, end_date),
@@ -714,7 +726,7 @@ class Database:
"""
SELECT * FROM stress_samples
WHERE user_id = ?
AND date(timestamp) >= ? AND date(timestamp) <= ?
AND substr(timestamp, 1, 10) >= ? AND substr(timestamp, 1, 10) <= ?
ORDER BY timestamp
""",
(user_id, start_date, end_date),
@@ -732,7 +744,7 @@ class Database:
"""
SELECT * FROM abnormal_heart_beat_events
WHERE user_id = ?
AND date(start_at) >= ? AND date(start_at) <= ?
AND substr(start_at, 1, 10) >= ? AND substr(start_at, 1, 10) <= ?
ORDER BY start_at
""",
(user_id, start_date, end_date),
@@ -768,9 +780,9 @@ class Database:
row = conn.execute(
"""
SELECT
MIN(date(start_at)) as first_date,
MAX(date(start_at)) as last_date,
COUNT(DISTINCT date(start_at)) as days_with_data
MIN(substr(start_at, 1, 10)) as first_date,
MAX(substr(start_at, 1, 10)) as last_date,
COUNT(DISTINCT substr(start_at, 1, 10)) as days_with_data
FROM sleep_sessions
WHERE user_id = ?
""",
@@ -788,9 +800,9 @@ class Database:
row = conn.execute(
"""
SELECT
MIN(date(start_at)) as first_date,
MAX(date(start_at)) as last_date,
COUNT(DISTINCT date(start_at)) as days_with_data
MIN(substr(start_at, 1, 10)) as first_date,
MAX(substr(start_at, 1, 10)) as last_date,
COUNT(DISTINCT substr(start_at, 1, 10)) as days_with_data
FROM workouts
WHERE user_id = ?
""",
@@ -808,9 +820,9 @@ class Database:
row = conn.execute(
"""
SELECT
MIN(date(timestamp)) as first_date,
MAX(date(timestamp)) as last_date,
COUNT(DISTINCT date(timestamp)) as days_with_data
MIN(substr(timestamp, 1, 10)) as first_date,
MAX(substr(timestamp, 1, 10)) as last_date,
COUNT(DISTINCT substr(timestamp, 1, 10)) as days_with_data
FROM body_measurements
WHERE user_id = ?
""",
@@ -827,9 +839,9 @@ class Database:
row = conn.execute(
"""
SELECT
MIN(date(timestamp)) as first_date,
MAX(date(timestamp)) as last_date,
COUNT(DISTINCT date(timestamp)) as days_with_data
MIN(substr(timestamp, 1, 10)) as first_date,
MAX(substr(timestamp, 1, 10)) as last_date,
COUNT(DISTINCT substr(timestamp, 1, 10)) as days_with_data
FROM heart_rate_samples
WHERE user_id = ?
""",