fuzzyworld

when-am-i / files / src / data / calendars / SCHEMA.md

SCHEMA.md

Blame: the commit that last changed each line · file · history

26a02b71 2026-06-19 Pavel Safronov1# Calendar Region/Era Schema for "When am I?"
359276b3 2026-03-20 Pavel Safronov2
3Place JSON files in `src/data/calendars/`.
4Each file describes a region and its calendar eras.
5
6## Schema
7
8```json
9{
10 "region": {
11 "name": "string (required)",
12 "geo": {
13 "type": "Polygon|MultiPolygon|Point|...",
14 "coordinates": "GeoJSON coordinates array"
15 }
16 },
17 "eras": [
18 {
19 "name": "string (required)",
20 "start": "YYYY-MM-DD|null (start date, null for earliest)",
21 "end": "YYYY-MM-DD|null (end date, null for present)",
22 "calendar": "string (calendar system name)",
143b640c 2026-06-19 Pavel Safronov23 "usedBy": "string (optional — whose calendar this is, e.g. 'English colonists')",
359276b3 2026-03-20 Pavel Safronov24 "description": "string (optional)",
5c66fc6a 2026-06-21 Pavel Safronov25 "sources": ["string (URL or citation)"],
26 "anchorEvents": [
27 {
28 "name": "string (how a local names it, e.g. 'the great dying')",
29 "date": "YYYY-MM-DD (proleptic Gregorian, when it happened)",
30 "sources": ["string (URL or citation — required, ≥1)"]
31 }
cedd4e9c 2026-06-23 Pavel Safronov32 ],
33 "suppressModernDefault": "boolean (optional, default false — see Notes)"
359276b3 2026-03-20 Pavel Safronov34 }
35 ]
36}
37```
143b640c 2026-06-19 Pavel Safronov38
30de6faf 2026-06-20 Pavel Safronov39## Validation
40
41Every file here is checked by `npm run validate:data`
42([`scripts/validate-data.mts`](../../../scripts/validate-data.mts)) — the
43authoritative, machine-checkable form of this schema. It runs automatically
44before every build (`prebuild`) and in CI, so invalid data can never deploy. It
45verifies required fields, that geometry rings are valid `[lng, lat]` and closed,
46that `start`/`end` are real proleptic-Gregorian dates, and that `start` precedes
47`end`. The block above is the human-readable companion; the script is the source
48of truth.
49
143b640c 2026-06-19 Pavel Safronov50## Notes
51
52- Dates (`start`/`end`) are **proleptic Gregorian** `YYYY-MM-DD`. They are
53 compared as half-open intervals `[start, end)`; `null` means open-ended.
26a02b71 2026-06-19 Pavel Safronov54- **Eras may overlap in time, and regions may overlap in space.** Parallel
55 calendars coexisting at the same place and time is supported — each is shown as
56 a separate answer. The overlap can be within one file (multiple eras active at
57 once) or across files (overlapping region polygons): e.g. the Massachusett
58 reckoning (`massachusett.json`) and the colonial Julian/Gregorian calendars
59 (`colonial-america.json`, a larger polygon) both apply in 1650s Boston.
143b640c 2026-06-19 Pavel Safronov60- `usedBy` labels whose answer it is, shown as "Ask <usedBy>: …".
61- A calendar with no continuous numeric year (e.g. an observational lunar
62 calendar) is shown descriptively rather than converted to a number.
5c66fc6a 2026-06-21 Pavel Safronov63- **`anchorEvents`** (optional) let such a calendar place you in time the way a
64 local would: the engine reports the most recent one at/before the date as
65 "about N winters since <event>". Each event needs a `name`, a proleptic-
66 Gregorian `date`, and at least one **`source`** — anchors must be documented
67 real events; the engine only counts the winters, it never invents an event.
cedd4e9c 2026-06-23 Pavel Safronov68- **`suppressModernDefault`** (optional, default `false`) opts a region out of
69 the app's modern global Gregorian default (shown everywhere from 1927 on,
70 see `resolve.ts`) for any date this era covers. This is for the rare place
71 where no outside civil calendar has ever applied at all — e.g.
72 `north-sentinel-island.json`, where the Sentinelese have never adopted the
73 Gregorian calendar — not for a region that simply has its own *different*
74 calendar (those already suppress the default automatically once they supply
75 their own Gregorian-calendar era, or coexist with it like modern Iran).
76 Almost no region should set this.