fuzzyworld

when-am-i / files / README.md

README.md

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

c2d75cfd 2026-10-27 Pavel Safronov1# 🚀 [Live site](https://apps.fuzzyworld.net/when-am-i/)
3186bef2 2026-03-20 Pavel Safronov2
6bcd5898 2026-06-19 Pavel Safronov3<!-- badges:start -->
4<!-- generated by `npm run gen:badges` — do not edit by hand -->
c2d75cfd 2026-10-27 Pavel Safronov5[![live demo](https://img.shields.io/badge/demo-live-success)](https://apps.fuzzyworld.net/when-am-i/)
6bcd5898 2026-06-19 Pavel Safronov6![version](https://img.shields.io/badge/version-v0.1.0-blue)
cedd4e9c 2026-06-23 Pavel Safronov7![calendar data](https://img.shields.io/badge/calendar%20data-20-orange)
6bcd5898 2026-06-19 Pavel Safronov8![license](https://img.shields.io/badge/license-MIT-green)
9
10![React](https://img.shields.io/badge/React-20232A?logo=react&logoColor=61DAFB)
11![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=fff)
12![Vite](https://img.shields.io/badge/Vite-646CFF?logo=vite&logoColor=fff)
13![Leaflet](https://img.shields.io/badge/Leaflet-199900?logo=leaflet&logoColor=fff)
14<!-- badges:end -->
15
26a02b71 2026-06-19 Pavel Safronov16## When am I?
edbab6ce 2026-03-20 Pavel Safronov17
26a02b71 2026-06-19 Pavel Safronov18Imagine you're a time traveler who ends up in a random place and time in history. What does a local say when you ask, "When am I?" This project is a fun and educational React app that lets you explore different calendar systems around the world by selecting a location on an interactive map and choosing any year in history. It then shows you the local calendar year and system for that place and time.
edbab6ce 2026-03-20 Pavel Safronov19
76ecf55e 2026-06-21 Pavel Safronov20![Landing page: a map and date picker, no spot chosen yet](screenshots/readme/01-landing.png)
21
22Pin a spot and a date, and see every valid answer a local could give — including
23when more than one calendar applies at once:
24
25![Boston, 1650: both the colonial Julian calendar and the Massachusett lunar reckoning shown as valid answers](screenshots/readme/02-example.png)
26
edbab6ce 2026-03-20 Pavel Safronov27## Features
28
29- Interactive world map for selecting a location
12bc9942 2026-06-23 Pavel Safronov30- A **year timeline** showing the picked spot's calendar eras as color-coded
31 bands (colors stay consistent across the map, timeline, and answers). Its
32 scale breaks at the region's **key dates** — the calendar changeovers — so
33 they're evenly spaced and labelled (e.g. 1954 for Vietnam) instead of crammed
34 into recent centuries; dashed lines mark where the calendar actually changes,
35 and each year label is **clickable to jump** straight to that date
68dcf3ed 2026-06-25 Pavel Safronov36- A **day-of-year track** color-coded the same way for the *selected* year, so a
37 changeover that falls mid-year shows up as a color boundary within the year —
38 e.g. for Boston in 1752 it switches from Julian to Gregorian in mid-September
39 (the British calendar reform), with a dashed line on the exact day
3adbd147 2026-06-20 Pavel Safronov40- **Shuffle** button that drops you at a random place and date on a non-default
483f1c57 2026-06-21 Pavel Safronov41 (non-Gregorian) calendar — e.g. Chinese, Japanese, Saka, Buddhist, Ethiopian,
42 Islamic, Persian, Julian, or a lunar reckoning
f9fc6714 2026-06-21 Pavel Safronov43- Displays the local calendar year and system for the chosen place and time,
44 plus the modern geography of the spot — the country and nearest large city
45 (offline lookup, no network)
46- A **two-column layout** (map + controls beside the answers) on wide screens
47 that stacks on mobile, and a **shareable URL** that captures the pinned spot,
48 date, and zoom so you can link someone to the exact view
b92a4280 2026-06-20 Pavel Safronov49- Shows **all** valid answers at once — different people at the same place and time
50 reckoned dates differently (e.g. colonial Julian alongside an Indigenous lunar calendar)
12bc9942 2026-06-23 Pavel Safronov51- Each answer notes **when that calendar was in use** at the spot (e.g. "in use
52 here since 1954"), so a changeover is called out plainly rather than buried
483f1c57 2026-06-21 Pavel Safronov53- For calendars with no numbered year, gives the kind of answer a local would —
54 the moon and night ("a full moon, about night 15"), and, where the data
55 records a documented event, **"about N winters since &lt;event&gt;"**
b92a4280 2026-06-20 Pavel Safronov56- A global **modern Gregorian default** for dates from 1927 on, so most recent dates
cedd4e9c 2026-06-23 Pavel Safronov57 resolve anywhere — shown alongside any local calendar (e.g. modern Iran → Persian *and* Gregorian),
58 with one deliberate exception: North Sentinel Island, where the uncontacted
59 Sentinelese have never adopted any outside calendar
edbab6ce 2026-03-20 Pavel Safronov60- Educational and fun way to explore world calendars and history
61
483f1c57 2026-06-21 Pavel Safronov62### Supported calendars
63
64The engine returns every valid answer for a place and date. A calendar is
65**convertible** when a deterministic rule yields a numbered local year, or
66**descriptive-only** when it has no continuous numbered year (we still place you
67in time — the moon/season, the named year, and "winters since" a recorded event —
68and show the Gregorian year for reference).
69
70- **Convertible:** Gregorian, Julian, Islamic (Umm al-Qura), Hebrew,
71 Persian (Solar Hijri), Indian (Saka), Buddhist, Japanese (nengō, Meiji onward),
c571572f 2026-06-21 Pavel Safronov72 Ethiopian, Maya Long Count, French Republican calendar (year only, from a
5f04d90e 2026-06-21 Pavel Safronov73 sourced table of each year's actual equinox-based start date), Dangi
12bc9942 2026-06-23 Pavel Safronov74 (Dangun era, South Korea), Juche (North Korea, 1997–2024), ab urbe condita
75 (AUC, from Rome's founding, 753 BC) and the Seleucid era (Anno Graecorum,
76 312/311 BC) — both year-only arithmetic offsets.
483f1c57 2026-06-21 Pavel Safronov77- **Descriptive-only:** the Chinese sexagenary cycle (the named 60-year
f24ee458 2026-06-21 Pavel Safronov78 stem-branch / zodiac-animal year), the Korean, Vietnamese, and Japanese
79 pre-modern lunisolar calendars, the Tzolk'in/Haab' Calendar Round, the
80 Aztec/Mexica year-bearer (52-year cycle) and Tōnalpōhualli (260-day ritual
81 cycle), the Ottoman Rumi fiscal calendar, the Māori maramataka, and
82 observational lunar calendars such as the Algonquian (Massachusett)
83 reckoning.
483f1c57 2026-06-21 Pavel Safronov84
edbab6ce 2026-03-20 Pavel Safronov85## Tech Stack
86
87- React (SPA)
88- Vite (modern, fast bundler)
89- TypeScript
90- Open-source map library (e.g., Leaflet or MapLibre)
91
92## Getting Started
93
941. **Install dependencies:**
95
96 ```bash
97 npm install
98 ```
99
1002. **Start the development server:**
101
102 ```bash
103 npm run dev
104 ```
105
1063. **Build for production:**
107
108 ```bash
109 npm run build
110 ```
111
60741172 2026-06-17 Pavel Safronov112## Development
113
114Quality checks (also run in CI before any deploy):
115
116```bash
30de6faf 2026-06-20 Pavel Safronov117npm run lint # ESLint (flat config)
118npm run typecheck # tsc --noEmit
119npm test # Vitest unit tests
120npm run test:watch # Vitest in watch mode
121npm run validate:data # Validate every calendar data file against the schema
60741172 2026-06-17 Pavel Safronov122```
123
30de6faf 2026-06-20 Pavel Safronov124`validate:data` also runs automatically before every build (`prebuild`) and in
125CI, so malformed calendar data can never be deployed. See
126[Contributing Calendar Data](#contributing-calendar-data).
127
6bcd5898 2026-06-19 Pavel Safronov128### Badges
129
130The README badge block (between the `badges:start`/`badges:end` markers above) is
131generated, not hand-edited. It pulls the app version from `package.json` and counts
132the calendar data files, so the badges never drift. Run it before opening a PR:
133
134```bash
135npm run gen:badges # rewrite the badge block in README.md
136npm run gen:badges -- --check # fail if the block is stale (no write) — for CI
137```
138
48a625b7 2026-06-19 Pavel Safronov139### Screenshots
140
141Screenshots are generated with Playwright, which forces deterministic app state
142via URL params (`?lat=&lng=&zoom=&year=&month=&day=&era=`). The scripts reuse a
68dcf3ed 2026-06-25 Pavel Safronov143running dev server on port 5173 or start one automatically. Set
144`SCREENSHOT_PORT` to use a different port — handy when another checkout (e.g. a
145parallel workspace) already holds 5173, so you capture *this* code, not theirs.
48a625b7 2026-06-19 Pavel Safronov146
147```bash
148npm run screenshot # app states → screenshots/app/
26a02b71 2026-06-19 Pavel Safronov149npm run screenshot:calendar -- src/data/calendars/massachusett.json # one calendar → screenshots/calendars/<file>/
73912a47 2026-06-20 Pavel Safronov150npm run screenshot:calendars # regenerate ALL calendars → screenshots/calendars/
76ecf55e 2026-06-21 Pavel Safronov151npm run screenshot:readme # the two README images → screenshots/readme/
48a625b7 2026-06-19 Pavel Safronov152```
153
154`screenshot:calendar` pins a point inside the region and a representative date
155in each era, capturing the answer panel and shaded map per era.
73912a47 2026-06-20 Pavel Safronov156`screenshot:calendars` does the same for every calendar data file at once
157(reusing one dev server + browser) — run it after adding or editing calendar
158data so the checked-in screenshots stay current.
48a625b7 2026-06-19 Pavel Safronov159
76ecf55e 2026-06-21 Pavel Safronov160`screenshot:readme` regenerates the two images embedded above: a full-viewport
161landing shot, and an example state cropped to just the map+answers column
162(skipping the header/intro, which a README image doesn't need) — narrower and
163more deliberately cropped than the full-page shots above, since a hero image
164needs to read at a glance.
165
e6fe2bc7 2026-06-17 Pavel Safronov166## Reporting issues
167
c2d75cfd 2026-10-27 Pavel Safronov168The app footer links to the [source repository](https://code.fuzzyworld.net/when-am-i/)
e6fe2bc7 2026-06-17 Pavel Safronov169and a **Report an issue** link. The latter opens a pre-addressed email to the
b9ae7fbb 2026-10-27 Pavel Safronov170project's bug-report address (no account needed) prefilled with context
e6fe2bc7 2026-06-17 Pavel Safronov171— build version/commit, browser, URL, and the current selection. If the app
172crashes, the error report additionally includes the error message and stack.
173
edbab6ce 2026-03-20 Pavel Safronov174## Project Structure
175
30b4cdad 2026-03-20 Pavel Safronov176- `src/components/` — React components
177- `src/utils/` — Utility functions
178- `src/types/` — Shared TypeScript types
179- `src/hooks/` — Custom React hooks
180- `src/data/` — Data files
181- `src/assets/` — App images and static assets
182- `public/` — Static files served at root
edbab6ce 2026-03-20 Pavel Safronov183
359276b3 2026-03-20 Pavel Safronov184## Contributing Calendar Data
185
186Calendar region and era data is stored as flat JSON files in `src/data/calendars/`.
187Each file describes a region (using GeoJSON geometry) and its calendar eras over time.
188
26a02b71 2026-06-19 Pavel Safronov189### Example (abbreviated): based on `src/data/calendars/massachusett.json`
143b640c 2026-06-19 Pavel Safronov190
26a02b71 2026-06-19 Pavel Safronov191Note how answers can **overlap** — both in time and in space. A click in 1650s
192Boston returns the Massachusett reckoning (from `massachusett.json`, a tight
193homeland polygon around Massachusetts Bay) **and** the colonial Julian calendar
194(from `colonial-america.json`, a broad "British North America" polygon that also
195covers the same spot). The Julian → Gregorian cutover was empire-wide, so it
196lives in the larger region rather than being duplicated into every town;
197overlapping regions are expected, not an error.
359276b3 2026-03-20 Pavel Safronov198
199```json
200{
201 "region": {
26a02b71 2026-06-19 Pavel Safronov202 "name": "Massachusett homeland",
359276b3 2026-03-20 Pavel Safronov203 "geo": {
204 "type": "Polygon",
205 "coordinates": [
206 [
143b640c 2026-06-19 Pavel Safronov207 [-71.30, 42.25],
208 [-71.05, 42.50],
209 [-70.92, 42.41],
210 [-71.12, 42.22],
211 [-71.30, 42.25]
359276b3 2026-03-20 Pavel Safronov212 ]
213 ]
214 }
215 },
216 "eras": [
217 {
143b640c 2026-06-19 Pavel Safronov218 "name": "Massachusett reckoning",
219 "start": null,
359276b3 2026-03-20 Pavel Safronov220 "end": null,
143b640c 2026-06-19 Pavel Safronov221 "calendar": "Algonquian lunar calendar",
222 "usedBy": "the Massachusett people",
26a02b71 2026-06-19 Pavel Safronov223 "description": "Lunar and seasonal reckoning of the Massachusett, the Algonquian people of the Massachusetts Bay area.",
143b640c 2026-06-19 Pavel Safronov224 "sources": ["https://en.wikipedia.org/wiki/Massachusett"]
359276b3 2026-03-20 Pavel Safronov225 }
226 ]
227}
228```
229
26a02b71 2026-06-19 Pavel Safronov230The colonial Julian and Gregorian eras live in `colonial-america.json`, whose
231larger polygon overlaps this one — so a Boston click resolves against both files.
232
483f1c57 2026-06-21 Pavel Safronov233An era for a calendar with no numbered year may also carry **`anchorEvents`** — a
234list of documented, dated, **sourced** events a local reckons time from. The app
235reports the most recent one at or before the date as "about N winters since
236&lt;event&gt;" (e.g. the Massachusett reckoning counts winters since the
2371616–1619 epidemic). Anchor events must be real and cited — the engine only
238counts the winters, it never invents the event.
239
359276b3 2026-03-20 Pavel Safronov240### Schema
241
483f1c57 2026-06-21 Pavel Safronov242See `src/data/calendars/SCHEMA.md` (human-readable field docs) and
243`scripts/validate-data.mts` (the authoritative, machine-checkable schema run by
244`validate:data`).
359276b3 2026-03-20 Pavel Safronov245
246### How to contribute
247
7922a983 2026-10-27 Pavel Safronov248Contributions arrive by email: share a branch from any git host, or send a
249`git format-patch`, to `contrib-68uq+when-am-i@fuzzyworld.net`. Details:
250[how to contribute](https://code.fuzzyworld.net/contribute/).
c2d75cfd 2026-10-27 Pavel Safronov251
7922a983 2026-10-27 Pavel Safronov252- Add or edit files in `src/data/calendars/`, on a branch or as a patch.
359276b3 2026-03-20 Pavel Safronov253- Use [geojson.io](https://geojson.io/) or similar tools to draw and export region polygons.
483f1c57 2026-06-21 Pavel Safronov254- Include sources/references for each era (and for every anchor event).
255- Working in Claude Code? The repo ships a **`new-calendar`** skill
256 (`.claude/skills/new-calendar/`) that walks the whole flow — researching a
257 place/people's calendar with citations, drafting the polygon and eras, picking
258 the right converter (or descriptive-only), then validating and screenshotting.
48a625b7 2026-06-19 Pavel Safronov259- Run `npm run screenshot:calendar -- src/data/calendars/<your-file>.json` and
7922a983 2026-10-27 Pavel Safronov260 attach the images to your email, so the region, eras, and conversions can be
48a625b7 2026-06-19 Pavel Safronov261 reviewed visually.
6bcd5898 2026-06-19 Pavel Safronov262- Run `npm run gen:badges` so the README's calendar-data count reflects your
263 addition, and commit the updated README.
30de6faf 2026-06-20 Pavel Safronov264- Run `npm run validate:data` to check your file against the schema (required
265 fields, valid geometry, parseable dates). This runs automatically before every
266 build and in CI, so a malformed file will fail the pipeline rather than deploy.
359276b3 2026-03-20 Pavel Safronov267
5f04d90e 2026-06-21 Pavel Safronov268### Calendars we'd like to add
269
270Ranked by rough "years in use × area/population reached" — the systems that
271would let the most human-years of history resolve to a real local answer.
272Contributions for any of these (or anything else, sourced) are very welcome —
273see [How to contribute](#how-to-contribute) above, and check
274`src/utils/calendars.ts` for whether a system needs new converter work or can
275ship as descriptive-only.
276
f24ee458 2026-06-21 Pavel Safronov2771. **Tibetan calendar (Bod rtsis)** — a lunisolar calendar in continuous use
5f04d90e 2026-06-21 Pavel Safronov278 across the Tibetan Plateau for over a thousand years, with its own 60-year
279 cycle and year names.
f24ee458 2026-06-21 Pavel Safronov2802. **Mongolian calendar** — the Mongol Empire and successor states' lunisolar
5f04d90e 2026-06-21 Pavel Safronov281 calendar (related to but distinct from the Chinese and Tibetan systems),
282 spanning a vast historical territory.
f24ee458 2026-06-21 Pavel Safronov2833. **Coptic calendar** — Egypt's calendar since antiquity (descended from the
5f04d90e 2026-06-21 Pavel Safronov284 ancient Egyptian civil calendar), still used liturgically by the Coptic
285 Orthodox Church; distinct from but related to the Ethiopian calendar already
286 supported. (Note: the Temporal `coptic` calendar id is broken in our
287 polyfill version, same class of bug as `chinese`/`ethiopic` — would likely
288 need a hand-rolled converter like Ethiopian's.)
f24ee458 2026-06-21 Pavel Safronov2894. **Ancient Egyptian civil calendar** — a 365-day calendar used for roughly
5f04d90e 2026-06-21 Pavel Safronov290 three millennia along the Nile; descriptive-only (no continuous numbered
291 year survives in common use), but enormous time depth.
f24ee458 2026-06-21 Pavel Safronov2925. **Armenian calendar** — in continuous use since antiquity, with its own
5f04d90e 2026-06-21 Pavel Safronov293 epoch and a fixed 365-day year (no leap day), giving it a slow drift against
294 the Gregorian year worth surfacing.
f24ee458 2026-06-21 Pavel Safronov2956. **Coast Salish / Puget Sound calendar** — a sourced, people-specific lunar
5f04d90e 2026-06-21 Pavel Safronov296 reckoning for the Pacific Northwest, paralleling the Massachusett entry; the
297 place/people this skill's original brainstorm named as an example.
f24ee458 2026-06-21 Pavel Safronov2987. **Inca calendar** — a lunisolar/solar calendar used across the Inca Empire's
5f04d90e 2026-06-21 Pavel Safronov299 considerable territory in the Andes; mostly descriptive (reconstructions of
300 its exact rules are debated, so sourcing needs care).
f24ee458 2026-06-21 Pavel Safronov3018. **Balinese Pawukon and Saka calendars** — Bali's parallel 210-day ritual
9c2d2d06 2026-06-21 Pavel Safronov302 cycle and a Saka-derived solar count, both still in everyday use.
f24ee458 2026-06-21 Pavel Safronov3039. **Bahá'í calendar** — a 19-month, 19-day solar calendar in worldwide use
304 since the mid-19th century; smaller area than the others here but a long
305 enough span and clean enough arithmetic to be a good convertible candidate.
30610. **Sikh Nanakshahi calendar** — a solar calendar adopted by the SGPC in 2003
5f04d90e 2026-06-21 Pavel Safronov307 for Sikh religious dates, with a clear epoch (Guru Nanak's birth year) —
308 likely a simple convertible offset like Dangi/Juche.
3cbfe9ad 2026-06-22 Pavel Safronov30911. **A dedicated South Korea region** — `korea.json`'s polygon covers the whole
310 peninsula, so it can't host South-only eras without colliding with
311 `north-korea.json` (both would fire on the same point). South Korea's own
312 1948–1962 official use of the Dangi (Dangun) era was removed from
313 `korea.json` for this reason and isn't documented anywhere yet; a
314 South-Korea-only polygon could restore it.
5f04d90e 2026-06-21 Pavel Safronov315
edbab6ce 2026-03-20 Pavel Safronov316## License
317
6bcd5898 2026-06-19 Pavel Safronov318[MIT](LICENSE) © Pavel Safronov
edbab6ce 2026-03-20 Pavel Safronov319
30b4cdad 2026-03-20 Pavel Safronov320## Attributions
edbab6ce 2026-03-20 Pavel Safronov321
30b4cdad 2026-03-20 Pavel Safronov322- Map tiles and data: [OpenStreetMap](https://www.openstreetmap.org/)
323- Map library: [Leaflet](https://leafletjs.com/)
f9fc6714 2026-06-21 Pavel Safronov324- City data (for the country / nearest-city readout): [GeoNames](https://www.geonames.org/), licensed [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Regenerate the bundled `src/data/cities.json` with `npm run gen:cities`.
359276b3 2026-03-20 Pavel Safronov325- Calendar icon: [Calendar icons created by Icongeek26 - Flaticon](https://www.flaticon.com/free-icons/calendar)
dc97539a 2026-06-21 Pavel Safronov326
327## Implementation
328
329This entire app was written by Claude. Pavel supplied the ideas, Claude did all the heavy lifting. Pavel wrote this paragraph. In part, this is a continuation of my exploration of agentic coding. See my other similarly-developed projects: [https://fuzzyworld.net/projects/](https://fuzzyworld.net/projects/).