Show trustworthy local-day activity totals (#90)

Add local-day activity summaries derived from UTC hour observations using
the system timezone, with durable storage in a new local_days table.
The collector derives local-day read/write totals after UTC aggregation;
the TUI displays them with timezone, completeness state, and coverage.

Schema: bump SCHEMA_VERSION to 3, add local_days table (migration 2→3
is pure addition, idempotent, preserves newer-schema refusal).
This commit is contained in:
xavierk
2026-09-18 12:28:23 +05:30
parent 366c2f55b7
commit 4f884b4b73
8 changed files with 834 additions and 3 deletions
+49
View File
@@ -0,0 +1,49 @@
"""System timezone detection for local-day activity totals.
Provides timezone detection (TZ env, /etc/localtime) and offset
computation used by local-day derivation. All functions are
stateless and safe to call from the collector and TUI reader.
"""
import os
from datetime import datetime, timezone
from pathlib import Path
def detect_system_tz() -> str:
"""Detect the system timezone name.
Resolution order:
1. ``TZ`` environment variable (may be empty or ":UTC")
2. Symlink target of ``/etc/localtime``
3. Falls back to ``"UTC"``
Returns a POSIX timezone name like ``"Asia/Kolkata"`` or ``"UTC"``.
"""
tz = os.environ.get("TZ", "").strip()
if tz:
return tz.lstrip(":")
localtime = Path("/etc/localtime")
if localtime.is_symlink():
target = os.readlink(str(localtime))
# Strip common prefixes: /usr/share/zoneinfo/, /usr/lib/zoneinfo/
for prefix in ("/usr/share/zoneinfo/", "/usr/lib/zoneinfo/"):
if target.startswith(prefix):
return target[len(prefix):]
return target
return "UTC"
def get_tz_offset_str(dt: datetime, tz_name: str) -> str:
"""UTC offset as ``+HH:MM`` or ``-HH:MM`` string for *dt* in *tz_name*."""
from zoneinfo import ZoneInfo
local_tz = ZoneInfo(tz_name)
local_dt = dt.astimezone(local_tz)
offset = local_dt.utcoffset()
total_seconds = int(offset.total_seconds())
sign = "+" if total_seconds >= 0 else "-"
total_seconds = abs(total_seconds)
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
return "%s%02d:%02d" % (sign, hours, minutes)