fuzzyworld

when-am-i / files / src / utils / dateRange.ts

dateRange.ts

1.6 KB · history · blame

1// Derives the year-slider's lower bound from the loaded calendar data, so the
2// timeline doesn't waste most of its width on millennia with no eras plotted.
3//
4// Only eras with an explicit numeric `start` count — `start: null` means "since
5// the earliest time" in the schema (e.g. the Hebrew or Ethiopian calendars'
6// traditional epochs run far earlier, but we never invent a date for them), so
7// those eras stay open-ended rather than pushing the floor back further.
8
9import type { CalendarRegion } from '../types';
10import { parseGregorianDate } from './julianDay';
11
12const FALLBACK_MIN_YEAR = -1999; // 2000 BCE, used if no era has a dated start.
13const MARGIN_FRACTION = 0.1; // headroom before the oldest era, as a fraction of its span to "now".
14
15/**
16 * The earliest astronomical year any era explicitly starts in `regions`, minus
17 * a margin (MARGIN_FRACTION of the span from that year to `nowYear`), so the
18 * oldest era isn't pinned at the very edge of the slider. Falls back to
19 * FALLBACK_MIN_YEAR if no era has a dated (non-null) start.
20 */
21export function deriveMinYear(regions: CalendarRegion[], nowYear: number): number {
22 let oldest: number | undefined;
23 for (const { eras } of regions) {
24 for (const era of eras) {
25 if (!era.start) continue;
26 const year = parseGregorianDate(era.start).year;
27 if (oldest === undefined || year < oldest) oldest = year;
28 }
29 }
30 if (oldest === undefined) return FALLBACK_MIN_YEAR;
31
32 const margin = (nowYear - oldest) * MARGIN_FRACTION;
33 return Math.floor(oldest - margin);
34}