3.5 KiB
Forecast Derivation Internals
internal/forecast deterministically selects and summarizes normalized
forecast data. It has no transport, filesystem, CLI, subprocess, or report
registry dependency. Its summaries are consumed by
fact contracts and later module builders.
Period and daypart semantics
Selections use timeutil.Period half-open overlap: a value is selected only
when both intervals share time. BuildDailySummary creates one local civil
day; BuildPeriodDailySummaries intersects every local civil day with the
requested period, preserving partial first and last days.
ResolveDayparts converts each configured name, start clock, and end clock
into a local window. An end clock at or before its start clock wraps into the
next civil day. The daypart and timezone defaults are defined in the
configuration reference, not here.
Deterministic summaries
BuildDailySummary requires an hourly run with at least one period. It adds
the selected narrative periods, discussion, alert overlaps, source provenance,
source warnings, and one DaypartSummary per resolved window. A daypart keeps
its selected hourly periods and derives temperature and apparent-temperature
ranges, timed precipitation and wind maxima, dominant and notable conditions,
and weather indicators.
Hourly precipitation probabilities must be finite percentages from 0 through 100. Daily-summary construction rejects invalid values before they can affect timed maxima or precipitation windows.
Indicators are deterministic checks over normalized values and condition text:
heat, cold, and wind use package-owned numeric cutoffs; snow, ice, fog, and
wind text are detected from the forecast description. BuildPrecipTiming
sorts periods, records the maximum and first precipitation, groups contiguous
periods at or above its package-owned probability threshold, and records
thunder mentions.
Ice detection uses the bounded condition vocabulary ice, icy, freezing,
and sleet as whole words.
Daypart temperature and apparent-temperature ranges are Fahrenheit values, and timed wind maxima are mph values. When only metric source fields are present, they are converted to those units before they are stored or evaluated against indicator thresholds; populated US-customary fields take precedence.
Alert overlap parsing supports the normalized alert payload's available timing
fields. Unparseable alerts and invalid intervals are ignored; valid overlaps
are clipped to the requested period and ordered by alert start time.
DailySummary.AlertOverlaps is limited to the local civil day, while each
daypart evaluates the full alert run against its complete window, including the
next-day portion of an overnight window.
Missing data and failures
Empty selections yield empty summary fields rather than generated prose. Direct daily or period-summary calls fail when their required bundle, valid period, hourly data, or daypart definitions are invalid. A nil location uses UTC when these APIs are called directly. Optional narrative, discussion, and alerts remain absent when their normalized products are absent.
Forecast thresholds used for brief indicators and precipitation timing are implementation rules.
Verification and invariants
Focused tests cover local civil days, clipped periods, daypart resolution, summary metrics, precipitation windows, threshold helpers, and alert overlap:
go test ./internal/forecast ./internal/timeutil
The package preserves normalized inputs as inspectable structured values and never decides report identity, delivery, or presentation wording.