Time and timestamps
Time is the most error-prone dimension in a live-data system. This page is the single source of truth for how Scorelytics represents it.
Three time formats
| Format | Where | Example |
|---|---|---|
| Unix epoch seconds (UTC) | All *_ts fields | 1746369000 |
| RFC 3339 / ISO 8601 | All *_at fields | "2026-05-04T16:32:11Z" |
| Wall-clock minute (integer) | minute, added_time | 67 |
*_ts and *_at always represent the same instant — the integer form is
faster to manipulate, the string form is easier to log.
Time zones
Everything we expose is UTC. Convert to local time only at the
presentation layer, using the user's IANA zone (e.g. Europe/Madrid).
The ?date= query filter on listMatches is also UTC. To "list today's
matches in Madrid", compute the UTC range that maps to now in
Europe/Madrid, then pass the matching ?date= value (or call the endpoint
twice if the local day spans two UTC days).
The updated_at field
updated_at is the server's last write to the row. It is the canonical
"freshness" indicator. Use it to:
- Drive cache invalidation in your client.
- Detect a stuck or missing match (
updated_atolder than a few minutes whilestatus_codeis still2). - Interpolate the live clock between polls.
Interpolation
Live clocks update at minute resolution server-side because the upstream
feeds publish at minute resolution. To render a smooth, second-level clock
in your UI, interpolate from updated_at:
function liveSeconds(secsRemaining: number, updatedAt: string) {
const drift = Math.floor((Date.now() - new Date(updatedAt).getTime()) / 1000);
return Math.max(0, secsRemaining - drift);
}
This is what the Scorelytics MCP server does internally, and what every real-time scoreboard does in the browser.
Game clock (basketball)
For basketball, the live game_clock_secs field reports minutes
remaining in the current period as an integer (despite the _secs suffix —
historical name). Interpret it as minutes * 60, then interpolate as above.
When the clock is stopped (timeout, free throw, end of period), the value is
-1. Render this state as "stopped" or by holding the previous value.
Wall-clock minute (football)
The football minute field is wall-clock seconds-since-period-start divided
by 60. It is already coherent with Date.now(), so re-computing every
animation frame from (now - period_start_ts) is the simplest way to render
a smooth ticker — no interpolation needed.
Daylight saving and league scheduling
Match kickoff_ts is fixed at scheduling time. If a daylight-saving
transition happens between scheduling and kickoff, the local time
displayed to fans may shift by an hour while kickoff_ts stays the same —
that's correct behavior. Always render kickoff in the user's current local
zone, never store a local string.