Weather.com Historical API ReturnsEmpty on DST Transition Days

Summary

Twice a year the Weather.com historical endpoint returns no data for the DST transition day. The root cause is the service’s expectation of a YYYYMMDD date in UTC, while callers often supply a local calendar date. On the spring‑forward and fall‑back dates the local‑to‑UTC conversion can shift the requested 24‑hour window out of the provider’s data buckets, resulting in an empty response.

Key takeaway: Always send dates in UTC and, for DST‑sensitive days, include an explicit 24‑hour range (startDate + endDate) covering the whole UTC day.


Root Cause

  • The API internally stores observations in UTC and slices them by calendar day in UTC.
  • The startDate parameter is parsed as a date‑only string (YYYYMMDD) that the service treats as midnight UTC.
  • When a caller passes a local date that falls on a DST transition, the midnight‑local → UTC conversion can shift the window by ±1 hour.
  • That shift pushes the requested 24‑hour window partially or entirely into the previous/next UTC day, which the backend does not associate with the requested startDate, so it returns no records.

Why This Happens in Real Systems

  • Implicit time‑zone assumptions: Many legacy APIs were designed before the prevalence of ISO‑8601 timestamps and still rely on implicit UTC handling.
  • Daylight‑saving edge cases: DST transitions are the only times a local calendar day does not map cleanly to a 24‑hour UTC interval.
  • Historical data bucketing: Storing raw observations in UTC simplifies aggregation but forces callers to be precise about the date range they request.

Real-World Impact

  • Missing data on critical transition days can break downstream analytics (e.g., energy demand forecasts that rely on hourly temperature).
  • Silent failures: The API returns a successful HTTP 200 with an empty observation list, making the problem hard to detect.
  • Increased support load: Customers repeatedly open tickets when their dashboards show gaps exactly on DST change dates.

Example or Code (if necessary and relevant)

import requests
from datetime import datetime, timedelta, timezone

def fetch_day(api_key, station, dt):
    # dt is a date object representing the local day you want
    # Convert to UTC midnight for the start of that UTC day
    utc_start = datetime(dt.year, dt.month, dt.day, tzinfo=timezone.utc)
    utc_end = utc_start + timedelta(days=1)

    start_str = utc_start.strftime("%Y%m%d")
    end_str   = utc_end.strftime("%Y%m%d")

    url = (
        f"https://api.weather.com/v1/location/{station}:9:US/observations/historical.json"
        f"?apiKey={api_key}&units=e&startDate={start_str}&endDate={end_str}"
    )
    resp = requests.get(url)
    resp.raise_for_status()
    return resp.json()

How Senior Engineers Fix It

  • Always request a full 24‑hour window (startDate + endDate) when dealing with historical data.
  • Normalize dates to UTC before building the query string.
  • Add retry logic that detects an empty observations array on a DST day and automatically expands the window by ±1 day.
  • Document the behavior in internal API wrappers and educate junior teams about the UTC‑only contract.
  • When possible, use the newer timestamp‑based endpoint (if available) that accepts ISO‑8601 datetimes with explicit offsets, eliminating the ambiguity.

Why Juniors Miss It

  • They assume the YYYYMMDD string is interpreted in the local time zone of the requestor.
  • They often test only with “normal” days and overlook the edge cases introduced by DST.
  • Lack of familiarity with UTC‑first design patterns leads them to forget to convert dates before concatenation.
  • Junior engineers may treat an empty result set as “no data for that day” rather than a sign of a malformed request.

Leave a Comment