Time and clocks
Computed, seen 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, seen, 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 |
| seen | 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 recorded |
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 readings 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 reading 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.
players_timeline returns one point per reading day and
only from snapshots_available_from, and players_summary.trophies_as_of
is a date, not an instant, because it names the reading the trophy count
came from. The series tools take YYYY-MM-DD bounds, game days; an
instant is accepted and floored to its game day, and the response says
which day it became (applied.window.floored).
Timeline items carry observed_at, the moment the recorder noticed the
change, which is "seen between two polls": a member who left at 09:05
and was noticed at 09:15 is seen at 09:15. Their at is when the moment
happened, the battle's own instant where the record holds the battle that
did it, with at_unix and local (the reader's date, time and weekday)
beside it; a timeline item's text never carries a time
(The timeline tool).
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 reading 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 reading 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 reading wins: a row is the state as of its
newest reading, and every point carries the instants that produced it -
observed_at (the newest reading 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 for the weekly donation counters, and
kind: season_roll in the hour before the season rolls. The donation counters
climb all week and drop to 0 once a week, around the start of Monday UTC. The
pre_reset row keeps the highest value the record saw late that Sunday, so it
never holds the new week's numbers. A week's donations everywhere are the
highest value seen in its game days (Monday 10:00 to Monday 10:00 UTC).
The policy day
Clash Royale does not publish a calendar. The river race resets once a day and the reset drifts per race: each race (the five clans matched into it) rolls at its own moment in the half hour before the nominal hour, and the moment differs from race to race. 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 one global grid rather than any race's drifted start. There is no per-clan war clock. 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. decks_today carries the same number as day_in_section, with training_day (1 to 3) on a training 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. A season holds 4 or 5 weeks, so the week number alone does not say Colosseum: game_clock.is_colosseum, weeks_in_season and colosseum_starts_at do |
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.
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. That rollover cannot be placed reliably across
every clan Elixir records, so no surface splits a member's week by war
day: war facts are the game's weekly counters, and
war_current.decks_today, the day still being played, is the one
day-sized figure. It is the game's own decksUsedToday counter for the
current day, as the last race poll recorded it (so it can trail play by a
poll); on a training day it lists the war decks members have played for
reps (day_kind: "training"), which never score.
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 timeline 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 race's real reset but before
10:00 UTC, would land on the previous policy day, which is why no
count places war battles on a day:
decks_todayis the game's own counter, which follows the race's real reset. - The period is the calendar's, open by construction: a day that has
ended is simply not the current one.
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, except on the daily series (whole game days, both ends included; below) andelixir_timeline, which selects over (from,to], by when an item happened or (a poller's read) when the record saw it. 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.seasonbounds one season on every windowed tool (below).- 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. Beside them ride theseasonthe window starts in,crosses(every season roll inside it) andseason_age_days, on every windowed tool.
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 |
players_timeline, clans_members_timeline |
unbounded, on game days: from/to are YYYY-MM-DD game days (inclusive); an instant is floored to its game day, echoed under applied.window.floored with a note; days: N is N game days, today included |
clans_timeline |
the last 30 game days (source: "default", with a note naming series_available_from); bounds as on the other series |
elixir_timeline |
since your read pointer, or the last day without one (source: "pointer" or "default"); capped at 30 days |
game_events |
the current season so far |
cards_card |
the current season to date (source: "season"); season selects another |
clans_standings |
30 days |
clans_participation |
5 ISO weeks, the current one included (each starts Monday 00:00 UTC, ten hours before the war week; see Seasons) |
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. Use a season window to keep comparisons within the same game period; a window spanning seasons may include different card costs or rules.
seasonon every windowed tool (cards_card,battles_trends, the player battle tools,clans_standings, the daily series,game_eventsandelixir_timeline) bounds the window to one season:"current"(to date),"previous", the month ("2026-08") or the war number (135).from/to/days/weeksgiven still win, and each tool's own default (unbounded, 30 days on the standings, the read pointer on the timeline) is unchanged whenseasonis omitted.applied.window.seasonnames the season the window starts in (month,war,starts_at,ends_at), whatever set the window, on every windowed tool;nullwhen it starts before the record's calendar or when the window is unbounded.applied.window.crosseslists every season roll inside the window (kind,at,from_season,to_season); an empty array means the window is season-clean, and an unbounded window crosses every roll on record. Nothing is refused: an agent asking across a roll may mean it, and a note fires only whencrossesis non-empty, in the tool's own terms (balance changes on battle tools; the trophy and ranked resets on personal series and clan standings), 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: on a running season under seven days old, a note says the window is thin and 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, every row of
battles_trends, and clans_participation's weeks (which Standing's Path
of Legends and donation minimums read) 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.