fuzzyworld

when-am-i

App: https://apps.fuzzyworld.net/when-am-i/

Contribute: contrib-68uq+when-am-i@fuzzyworld.net (how) · Report a bug: bugs-k7q4+when-am-i@fuzzyworld.net

phone

git clone https://code.fuzzyworld.net/when-am-i.git

Open in VS Code · Open in VS Code in the browser · License: MIT · CI: ✓ passed

Recent commits

2026-10-27a6030ee✓ Rename .gitlab-ci.yml to .fw-ci.yml, and links to itPavel Safronov
2026-10-267922a98Point calendar contributions to code.fuzzyworld.net/contribute/Pavel Safronov
2026-10-26b9ae7fbSend bug reports to bugs-k7q4+when-am-i@fuzzyworld.netPavel Safronov
2026-10-26c2d75cfDeploy to apps.fuzzyworld.net with fuzzyworld-toolsPavel Safronov
2026-06-2468dcf3eShow calendar-era bands on the day-of-year scrollbar with clickable changeover ticksPavel Safronov

Full log

🚀 Live site

live demo version calendar data license

React TypeScript Vite Leaflet

When am I?

Imagine 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.

Landing page: a map and date picker, no spot chosen yet

Pin a spot and a date, and see every valid answer a local could give — including when more than one calendar applies at once:

Boston, 1650: both the colonial Julian calendar and the Massachusett lunar reckoning shown as valid answers

Features

Supported calendars

The engine returns every valid answer for a place and date. A calendar is convertible when a deterministic rule yields a numbered local year, or descriptive-only when it has no continuous numbered year (we still place you in time — the moon/season, the named year, and "winters since" a recorded event — and show the Gregorian year for reference).

Tech Stack

Getting Started

  1. Install dependencies:

bash npm install

  1. Start the development server:

bash npm run dev

  1. Build for production:

bash npm run build

Development

Quality checks (also run in CI before any deploy):

npm run lint           # ESLint (flat config)
npm run typecheck      # tsc --noEmit
npm test               # Vitest unit tests
npm run test:watch     # Vitest in watch mode
npm run validate:data  # Validate every calendar data file against the schema

validate:data also runs automatically before every build (prebuild) and in CI, so malformed calendar data can never be deployed. See Contributing Calendar Data.

Badges

The README badge block (between the badges:start/badges:end markers above) is generated, not hand-edited. It pulls the app version from package.json and counts the calendar data files, so the badges never drift. Run it before opening a PR:

npm run gen:badges            # rewrite the badge block in README.md
npm run gen:badges -- --check # fail if the block is stale (no write) — for CI

Screenshots

Screenshots are generated with Playwright, which forces deterministic app state via URL params (?lat=&lng=&zoom=&year=&month=&day=&era=). The scripts reuse a running dev server on port 5173 or start one automatically. Set SCREENSHOT_PORT to use a different port — handy when another checkout (e.g. a parallel workspace) already holds 5173, so you capture this code, not theirs.

npm run screenshot                                   # app states → screenshots/app/
npm run screenshot:calendar -- src/data/calendars/massachusett.json   # one calendar → screenshots/calendars/<file>/
npm run screenshot:calendars                         # regenerate ALL calendars → screenshots/calendars/
npm run screenshot:readme                            # the two README images → screenshots/readme/

screenshot:calendar pins a point inside the region and a representative date in each era, capturing the answer panel and shaded map per era. screenshot:calendars does the same for every calendar data file at once (reusing one dev server + browser) — run it after adding or editing calendar data so the checked-in screenshots stay current.

screenshot:readme regenerates the two images embedded above: a full-viewport landing shot, and an example state cropped to just the map+answers column (skipping the header/intro, which a README image doesn't need) — narrower and more deliberately cropped than the full-page shots above, since a hero image needs to read at a glance.

Reporting issues

The app footer links to the source repository and a Report an issue link. The latter opens a pre-addressed email to the project's bug-report address (no account needed) prefilled with context — build version/commit, browser, URL, and the current selection. If the app crashes, the error report additionally includes the error message and stack.

Project Structure

Contributing Calendar Data

Calendar region and era data is stored as flat JSON files in src/data/calendars/. Each file describes a region (using GeoJSON geometry) and its calendar eras over time.

Example (abbreviated): based on src/data/calendars/massachusett.json

Note how answers can overlap — both in time and in space. A click in 1650s Boston returns the Massachusett reckoning (from massachusett.json, a tight homeland polygon around Massachusetts Bay) and the colonial Julian calendar (from colonial-america.json, a broad "British North America" polygon that also covers the same spot). The Julian → Gregorian cutover was empire-wide, so it lives in the larger region rather than being duplicated into every town; overlapping regions are expected, not an error.

{
   "region": {
      "name": "Massachusett homeland",
      "geo": {
         "type": "Polygon",
         "coordinates": [
            [
               [-71.30, 42.25],
               [-71.05, 42.50],
               [-70.92, 42.41],
               [-71.12, 42.22],
               [-71.30, 42.25]
            ]
         ]
      }
   },
   "eras": [
      {
         "name": "Massachusett reckoning",
         "start": null,
         "end": null,
         "calendar": "Algonquian lunar calendar",
         "usedBy": "the Massachusett people",
         "description": "Lunar and seasonal reckoning of the Massachusett, the Algonquian people of the Massachusetts Bay area.",
         "sources": ["https://en.wikipedia.org/wiki/Massachusett"]
      }
   ]
}

The colonial Julian and Gregorian eras live in colonial-america.json, whose larger polygon overlaps this one — so a Boston click resolves against both files.

An era for a calendar with no numbered year may also carry anchorEvents — a list of documented, dated, sourced events a local reckons time from. The app reports the most recent one at or before the date as "about N winters since <event>" (e.g. the Massachusett reckoning counts winters since the 1616–1619 epidemic). Anchor events must be real and cited — the engine only counts the winters, it never invents the event.

Schema

See src/data/calendars/SCHEMA.md (human-readable field docs) and scripts/validate-data.mts (the authoritative, machine-checkable schema run by validate:data).

How to contribute

Contributions arrive by email: share a branch from any git host, or send a git format-patch, to contrib-68uq+when-am-i@fuzzyworld.net. Details: how to contribute.

Calendars we'd like to add

Ranked by rough "years in use × area/population reached" — the systems that would let the most human-years of history resolve to a real local answer. Contributions for any of these (or anything else, sourced) are very welcome — see How to contribute above, and check src/utils/calendars.ts for whether a system needs new converter work or can ship as descriptive-only.

  1. Tibetan calendar (Bod rtsis) — a lunisolar calendar in continuous use across the Tibetan Plateau for over a thousand years, with its own 60-year cycle and year names.
  2. Mongolian calendar — the Mongol Empire and successor states' lunisolar calendar (related to but distinct from the Chinese and Tibetan systems), spanning a vast historical territory.
  3. Coptic calendar — Egypt's calendar since antiquity (descended from the ancient Egyptian civil calendar), still used liturgically by the Coptic Orthodox Church; distinct from but related to the Ethiopian calendar already supported. (Note: the Temporal coptic calendar id is broken in our polyfill version, same class of bug as chinese/ethiopic — would likely need a hand-rolled converter like Ethiopian's.)
  4. Ancient Egyptian civil calendar — a 365-day calendar used for roughly three millennia along the Nile; descriptive-only (no continuous numbered year survives in common use), but enormous time depth.
  5. Armenian calendar — in continuous use since antiquity, with its own epoch and a fixed 365-day year (no leap day), giving it a slow drift against the Gregorian year worth surfacing.
  6. Coast Salish / Puget Sound calendar — a sourced, people-specific lunar reckoning for the Pacific Northwest, paralleling the Massachusett entry; the place/people this skill's original brainstorm named as an example.
  7. Inca calendar — a lunisolar/solar calendar used across the Inca Empire's considerable territory in the Andes; mostly descriptive (reconstructions of its exact rules are debated, so sourcing needs care).
  8. Balinese Pawukon and Saka calendars — Bali's parallel 210-day ritual cycle and a Saka-derived solar count, both still in everyday use.
  9. Bahá'í calendar — a 19-month, 19-day solar calendar in worldwide use since the mid-19th century; smaller area than the others here but a long enough span and clean enough arithmetic to be a good convertible candidate.
  10. Sikh Nanakshahi calendar — a solar calendar adopted by the SGPC in 2003 for Sikh religious dates, with a clear epoch (Guru Nanak's birth year) — likely a simple convertible offset like Dangi/Juche.
  11. A dedicated South Korea region — korea.json's polygon covers the whole peninsula, so it can't host South-only eras without colliding with north-korea.json (both would fire on the same point). South Korea's own 1948–1962 official use of the Dangi (Dangun) era was removed from korea.json for this reason and isn't documented anywhere yet; a South-Korea-only polygon could restore it.

License

MIT © Pavel Safronov

Attributions

Implementation

This 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/.