Time and clocks
Computed, observed and played are three different times. Which one a field means, and what day it is in the game.
Storage is UTC everywhere; timezone is a display concern. That one rule produces three different timestamps on most responses, a policy calendar the game itself does not publish, and a small set of window conventions every tool shares. This page holds all three so no tool has to explain them.
Computed, observed, played
A response can carry three kinds of time, and they answer different questions:
| Time | Field | Question it answers |
|---|---|---|
| computed | meta.as_of |
when the sums were done. It is now, at the moment of the call, and says nothing about the data's age |
| observed | meta.source_polls.<endpoint>.observed_at |
when the recorder last admitted a payload from that source (profile, battle log, clan, current race) |
| played | battle_time |
when the battle happened, from the battle log's own stamp. Every battle window filters on this, never on when the battle was captured |
meta.freshness_seconds is the age at as_of of the oldest relevant
poll, not the newest poll of any kind: a fresh profile must never disguise a
stale battle log. It is null when any required source has never been polled,
because unknown is not zero-age evidence. recorded_since is the earliest
stored history for the subject, which can predate anyone tracking it (an
opponent's log, an import); recording_active_since is when the current
recording began. The two are kept apart because history is never deleted when
a recording stops. The fields are defined on
Reading a response.
Daily snapshots are keyed by the game day, the policy day
below: a day runs from 10:00 UTC to 10:00 UTC and is named for the date it
starts on, so a snapshot taken at 09:00 UTC on the 18th belongs to the 17th,
the same day as the war day and the season roll it sits inside. (Before
2026-09-17 the key was the UTC calendar date; every row was moved onto the
game day then.) players_timeline returns one point per snapshot day and
only from snapshots_available_from, and players_summary.trophies_as_of
is a date, not an instant, because it names the snapshot the trophy count
came from. Snapshot-series tools take YYYY-MM-DD bounds only; an instant
is refused.
Events carry created_at, the moment the recorder noticed the change, which
is "observed between two polls": a member who left at 09:05 and was noticed
at 09:15 has an event stamped 09:15.
The game day
Every daily series in the record (players_timeline, clans_timeline,
clans_members_timeline, and the progress series) is keyed by one day: the
game day, the date whose 10:00 UTC start the observation falls after. It is
the same partition as the policy day below, so a war day, a
season roll (the first Monday of the month at 10:00 UTC) and a series row never
straddle: the snapshot taken at 09:00 UTC on the roll Monday is the last row of
the old season, and the first row after 10:00 UTC is the first of the new one.
The function is pure UTC arithmetic (game_day(at) = date(at - 10 hours)), so
daylight-saving changes cannot move it. A point's day is the game's; your
timezone is a display concern, and applied.window.timezone says which zone
date-only bounds were resolved in.
Within a game day the last observation wins: a row is the state as of its
newest observation, and every point carries the instants that produced it -
observed_at (the newest observation of either writer), profile_observed_at
(the profile poll that wrote the lifetime columns) and, on member rows,
roster_observed_at (the roster poll that wrote the clan columns). Null means
that writer never touched the row. Two extra rows are kept where a counter is
about to reset: kind: pre_reset in the hour before the Monday 00:10 UTC
donation reset, and kind: season_roll in the hour before the season rolls.
The policy day
Clash Royale does not publish a calendar. The river race resets once a day and the reset drifts per clan: matchmaking assigns each clan's race a start that wanders from the nominal hour, and the drift is different for every clan. Elixir MCP therefore keeps one policy day for every clan: the day rolls at 10:00 UTC, the same hour the season rolls, and the recorder follows that grid rather than each clan's drifted start. One clock means every war number is comparable across clans and across weeks.
The vocabulary, from smallest to largest:
| Word | Meaning |
|---|---|
war_day |
1-based: the four battle days of a week are war days 1 to 4; null on a training day |
day_in_week |
0-based: days 0 to 2 are training, 3 to 6 are battle days. war_day = day_in_week - 2 on a battle day |
| period | one policy day; period_index counts them from the season start |
| section | the game's word for a week; section_index is 0-based and week is 1-based |
| season | first Monday of the month to first Monday of the next, resetting at 10:00 UTC; the weeks are the Mondays between |
| Colosseum | always the season's final section, scored differently in the game; its practice days still report as training |
The record holds that grid as rows, one per policy day of every season,
and a war battle is filed by where its battle_time falls on it, for
the clan the player was in when it was played. The API stamps nothing on a
war battle beyond its type and time, so the record stamps nothing either:
attendance in war_current, war_days in war_history and the per-day
columns of clans_participation all count every recorded war battle
this way, however long after the fact it was recorded. The one thing that
is a clan's own is the instant its race actually closed, which sits inside
the half hour before 10:00 UTC and differs per race; a battle played in
that gap belongs to the new day in the game and to the old day on the grid.
game_clock answers all of this for nobody in particular (pass at to learn
what day a recorded battle fell on) and is the right first call when a
question begins "today" or "this week". war_current is what a specific clan
is doing inside that day.
It also carries the next boundaries, so a scheduled routine can decide for
itself when to look: day_ends_at (this policy day), war_day_closes_at
(the same instant on a war day, null on a training day),
next_war_day_opens_at, next_training_starts_at, week_ends_at and
season_ends_at. Nothing in the event feed announces the time; a routine that
wants to act three hours before a war day closes reads the clock once and sets
its own timer.
Because the recorder follows the policy grid, war_current gives you both the
grid and what it actually saw. The period itself is the calendar's - the
record holds every policy day as a row, so which day it is never depends on
a clan's last poll:
period_start_nominal,period_end_nominalandweek_end_nominalare the policy instants. Cite these when you say when a day ends.started_observed_atis when the recorder first saw this period open,nullwhen it has not seen it yet;observed_offset_minutesis that sighting's distance from the policy hour, including polling latency, so it is an upper bound on the clan's real drift.- Battles in the drift gap, played after the clan's real reset but before
10:00 UTC, land on the previous policy day. The first sign is a member
counted with five decks:
decks_today.over_caplists members observed with more than four decks in a policy day rather than rounding them away. nominal_period_elapsedis alwaysfalsenow that the period comes from the calendar (a day that has ended is simply not the current one); it stays on the wire for readers that check it.period.source_observed_atsays how fresh the race itself is.
Windows and timezones
Every windowed tool takes the same bounds and echoes what it used:
fromis inclusive andtoexclusive. Each is an ISO 8601 instant, taken as given, or a dateYYYY-MM-DD, resolved in a timezone. A date-onlyfromis local midnight; a date-onlytocovers that whole day, ending at the next local midnight (exclusive), sofrom: "2026-09-01", to: "2026-09-07"is seven full days.- The timezone is the account's, or the call's own
timezoneargument (an IANA name such asEurope/Paris), which also drivesbattle_time_localand every other local label. Agents serving people in several zones pass the asker's.meta.timezone_appliednames the zone that applied. daysandweeksare sugar:days: 7isfromseven days before now with noto.seasonsonwar_historycounts seasons back the same way.- Every windowed response echoes
applied.windowwithfrom,to,timezoneand asource:argumentwhen you gave bounds,defaultwhen the tool's default applied,unboundedwhen nothing bounded the window,seasonwhen a season's bounds applied (below), andfixedon the one tool whose window is not an argument. An agent quoting "your last 30 days" readssourcebefore it says so.
The defaults differ by tool, and each says which applied:
| Tool | Window when you give none |
|---|---|
players_summary |
fixed 30 days (source: "fixed"); not an argument |
battles_meta_decks, battles_meta_cards, cards_synergy |
the current season to date (source: "season"); season selects another |
clans_standings |
30 days |
clans_pilot_scores |
90 days |
clans_participation |
5 ISO weeks, the current one included |
battles_trends |
12 weeks |
battles_performance, battles_decks, battles_query, battles_cards, battles_opponents |
unbounded: the whole recorded history, said so in applied.window.source |
Seasons
A season is a row in the record: first Monday of the month 10:00 UTC to
the next first Monday 10:00 UTC, named the way the API names it, by the
month it starts in (2026-09), and carrying the river race season number
the war surfaces speak (136). The record derives that number from the
month and confirms it against every war log entry it admits; the two have
never disagreed, and if they ever do the record alarms rather than
relabelling anything. The in-game Pass season (its number and its name) is
not in the API and is not in the record; an agent that knows it may say
it, the record never will.
Balance changes are not modelled, deliberately: nothing hand-fed or
scraped enters the data layer. Supercell ships balance changes on the
season roll, so a season is the window that honours them: card and
deck numbers inside one season are one population, and numbers that span a
roll are not. That is why the meta tools (battles_meta_decks,
battles_meta_cards, cards_synergy) default to the current season to
date rather than a rolling number of days, which on most days of the
month mixes two seasons without saying so.
seasonon those three tools and onbattles_trendsbounds the window to one season:"current"(the default on the meta tools; to date),"previous", the month ("2026-08") or the war number (135).from/to/days/weeksgiven still win.applied.window.seasonnames the season the window starts in (month,war,starts_at,ends_at), whatever set the window;nullwhen it starts before the record's calendar.applied.window.crosseslists every season roll inside the window (kind,at,from_season,to_season); an empty array means the window is season-clean. Nothing is refused: an agent asking across a roll may mean it, and a note says so in a sentence so the caveat travels with the numbers.crossesis what lets a consumer refuse for itself.applied.window.season_age_daysis how old that season is at the window's end. On the first days of a season the default window is thin and the record says so rather than widening it:insufficient_samplefires as it always did, and a note namesseason: "previous"as the settled comparison.battles_trendskeeps its twelve-week default and crosses rolls by design; every week row carriesseason_month, the season its Tuesday to Sunday fall in, so a chart can draw the line wherecrossesputs it.
game_clock carries the same two names (season_id, season_month) and
the season's bounds for now.
group_by: "week" on battles_performance and every row of
battles_trends use ISO weeks, Monday to Sunday. A war week, by
contrast, runs on the policy grid above, Monday 10:00 UTC to Monday 10:00
UTC; the two start on the same weekday and ten hours apart, so never join
them by date alone.