Spaces:
Sleeping
Sleeping
File size: 3,253 Bytes
199bfa3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | """
Timezone utilities for CSH2 Web Dashboard.
The PLC logs in US/Eastern. The database has two time columns:
- dateandtime: Eastern local time (no timezone info)
- utc_full_timestamp: real UTC (timestamptz)
The cycle_periods table stores Eastern times incorrectly labeled as UTC.
This module provides consistent conversion and display helpers.
"""
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
import pandas as pd
from core.config import DISPLAY_TZ, DISPLAY_TZ_LABEL, CYCLE_PERIODS_UTC_OFFSET
def to_eastern(dt) -> datetime:
"""Convert a UTC datetime to Eastern time for display.
Accepts timezone-aware (UTC) or naive (assumed UTC) datetimes.
Returns a timezone-aware datetime in US/Eastern.
"""
if dt is None:
return None
if isinstance(dt, pd.Timestamp):
dt = dt.to_pydatetime()
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.astimezone(DISPLAY_TZ)
def to_utc(dt) -> datetime:
"""Convert an Eastern datetime to UTC for database queries.
Accepts timezone-aware (Eastern) or naive (assumed Eastern) datetimes.
Returns a timezone-aware datetime in UTC.
"""
if dt is None:
return None
if isinstance(dt, pd.Timestamp):
dt = dt.to_pydatetime()
if dt.tzinfo is None:
dt = dt.replace(tzinfo=DISPLAY_TZ)
return dt.astimezone(timezone.utc)
def cycle_periods_to_utc(dt) -> datetime:
"""Convert a cycle_periods timestamp (Eastern mislabeled as UTC) to real UTC.
The cycle_periods table stores Eastern local times with a +00:00 suffix.
Adding 4 hours converts to real UTC for sensor data queries.
"""
if dt is None:
return None
if isinstance(dt, pd.Timestamp):
dt = dt.to_pydatetime()
return dt + CYCLE_PERIODS_UTC_OFFSET
def cycle_periods_to_eastern(dt) -> datetime:
"""Convert a cycle_periods timestamp to a properly labeled Eastern datetime.
The stored values ARE Eastern — just strip the wrong UTC label and apply
the correct US/Eastern timezone.
"""
if dt is None:
return None
if isinstance(dt, pd.Timestamp):
dt = dt.to_pydatetime()
# Strip the incorrect UTC tzinfo, replace with Eastern
naive = dt.replace(tzinfo=None)
return naive.replace(tzinfo=DISPLAY_TZ)
def format_et(dt, fmt: str = '%b %d %H:%M') -> str:
"""Format a datetime for display in Eastern time.
If the datetime is UTC, converts to Eastern first.
Appends the timezone label.
"""
if dt is None:
return "—"
eastern = to_eastern(dt) if (dt.tzinfo and dt.tzinfo != DISPLAY_TZ) else dt
return f"{eastern.strftime(fmt)} {DISPLAY_TZ_LABEL}"
def convert_df_timestamps_to_eastern(df: pd.DataFrame, col: str = 'timestamp') -> pd.DataFrame:
"""Convert a DataFrame's timestamp column from UTC to Eastern for display.
Returns a new DataFrame (does not modify in place).
"""
if col not in df.columns:
return df
df = df.copy()
if df[col].dt.tz is not None:
df[col] = df[col].dt.tz_convert(DISPLAY_TZ)
else:
# Naive timestamps assumed UTC
df[col] = df[col].dt.tz_localize('UTC').dt.tz_convert(DISPLAY_TZ)
return df
|