Skip to content

nwrfc.textplot: a page spanning a DST transition stamps pre-transition rows an hour off #231

Description

@mousebrains

Pre-existing, seasonal, low severity. Filed out of review of #227, which found it and correctly scoped it out — it predates that branch and affects every pe.

Mechanism

nwrfc_textplot.py reads one timezone marker for the whole page:

tz = "PDT" if "(pdt)" in header_lower else "PST" if "(pst)" in header_lower else None

But a textPlot page carries ~11 days of rows. Observed live:

HCRO3 pe=TW span: 2026-07-04 17:00 -> 2026-07-15 11:35  (3090 stamps); (PDT)=1 (PST)=0

TIMEZONE_OFFSETS (utils/conversions.py) is a fixed-offset map, so every row on the page gets the header's single offset.

Impact

For the ~11 days after a transition, the header reads (PST) and rows logged before the transition — which were actually PDT — get −8 instead of −7. 2026-10-31 08:00 PDT (15:00 UTC) stores as 16:00 UTC.

Next occurrence: 2026-11-01. Affects the ~11-day window after each spring/fall transition, on any wired textPlot source, one hour, one direction.

Why it's minor

  • Only the overlap window, twice a year.
  • One hour on a river-level series is rarely actionable.
  • Newer rows — the ones the site actually shows — are stamped correctly; it's the tail of the page that skews.
  • Observations upsert on (source_id, observed_at, data_type), so a skewed row lands on a neighbouring slot rather than corrupting a good one.

Possible fix

Derive the offset per-row rather than per-page: resolve the naive stamp against a real IANA zone (America/Los_Angeles) instead of the page's fixed marker, letting zoneinfo apply the transition. That's a real change to parse_datetime's contract, hence not bundled into #227.

Adjacent, noted while here: if a page ever carried no marker, tz=None makes parse_datetime stamp naive local time as UTC — a silent 7–8h shift that dump_to_db's _localize can't rescue, because the value is already tz-aware by then. No live page does this today (every data-bearing page carries exactly one (PDT)), so it's theoretical — but it's the same root cause and worth fixing together.

— Claude · Opus-4.8(1M) · x-high

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions