Calendar Region/Era Schema for "When am I?"
Place JSON files in src/data/calendars/.
Each file describes a region and its calendar eras.
Schema
{
"region": {
"name": "string (required)",
"geo": {
"type": "Polygon|MultiPolygon|Point|...",
"coordinates": "GeoJSON coordinates array"
}
},
"eras": [
{
"name": "string (required)",
"start": "YYYY-MM-DD|null (start date, null for earliest)",
"end": "YYYY-MM-DD|null (end date, null for present)",
"calendar": "string (calendar system name)",
"usedBy": "string (optional — whose calendar this is, e.g. 'English colonists')",
"description": "string (optional)",
"sources": ["string (URL or citation)"],
"anchorEvents": [
{
"name": "string (how a local names it, e.g. 'the great dying')",
"date": "YYYY-MM-DD (proleptic Gregorian, when it happened)",
"sources": ["string (URL or citation — required, ≥1)"]
}
],
"suppressModernDefault": "boolean (optional, default false — see Notes)"
}
]
}
Validation
Every file here is checked by npm run validate:data
(scripts/validate-data.mts) — the
authoritative, machine-checkable form of this schema. It runs automatically
before every build (prebuild) and in CI, so invalid data can never deploy. It
verifies required fields, that geometry rings are valid [lng, lat] and closed,
that start/end are real proleptic-Gregorian dates, and that start precedes
end. The block above is the human-readable companion; the script is the source
of truth.
Notes
- Dates (
start/end) are proleptic GregorianYYYY-MM-DD. They are compared as half-open intervals[start, end);nullmeans open-ended. - Eras may overlap in time, and regions may overlap in space. Parallel
calendars coexisting at the same place and time is supported — each is shown as
a separate answer. The overlap can be within one file (multiple eras active at
once) or across files (overlapping region polygons): e.g. the Massachusett
reckoning (
massachusett.json) and the colonial Julian/Gregorian calendars (colonial-america.json, a larger polygon) both apply in 1650s Boston. usedBylabels whose answer it is, shown as "Ask <usedBy>: …".- A calendar with no continuous numeric year (e.g. an observational lunar calendar) is shown descriptively rather than converted to a number.
anchorEvents(optional) let such a calendar place you in time the way a local would: the engine reports the most recent one at/before the date as "about N winters since <event>". Each event needs aname, a proleptic- Gregoriandate, and at least onesource— anchors must be documented real events; the engine only counts the winters, it never invents an event.suppressModernDefault(optional, defaultfalse) opts a region out of the app's modern global Gregorian default (shown everywhere from 1927 on, seeresolve.ts) for any date this era covers. This is for the rare place where no outside civil calendar has ever applied at all — e.g.north-sentinel-island.json, where the Sentinelese have never adopted the Gregorian calendar — not for a region that simply has its own different calendar (those already suppress the default automatically once they supply their own Gregorian-calendar era, or coexist with it like modern Iran). Almost no region should set this.