fuzzyworld

when-am-i / files / src / utils / historicalDate.test.ts

historicalDate.test.ts

3.0 KB · history · blame

1import { describe, it, expect } from 'vitest';
2import {
3 daysInMonth,
4 daysInYear,
5 dayOfYear,
6 fromDayOfYear,
7 toHistoricalDate,
8 fromHistoricalDate,
9 clampDay,
10} from './historicalDate';
11
12describe('daysInMonth', () => {
13 it('handles February leap years (Gregorian rule)', () => {
14 expect(daysInMonth(2000, 2)).toBe(29); // divisible by 400
15 expect(daysInMonth(1900, 2)).toBe(28); // divisible by 100, not 400
16 expect(daysInMonth(2004, 2)).toBe(29);
17 expect(daysInMonth(2001, 2)).toBe(28);
18 });
19
20 it('handles 30- and 31-day months', () => {
21 expect(daysInMonth(2026, 4)).toBe(30);
22 expect(daysInMonth(2026, 12)).toBe(31);
23 });
24});
25
26describe('day-of-year helpers', () => {
27 it('daysInYear handles leap years', () => {
28 expect(daysInYear(2000)).toBe(366);
29 expect(daysInYear(1900)).toBe(365);
30 expect(daysInYear(2001)).toBe(365);
31 });
32
33 it('dayOfYear counts from Jan 1', () => {
34 expect(dayOfYear(2001, 1, 1)).toBe(1);
35 expect(dayOfYear(2001, 3, 1)).toBe(60); // 31 + 28 + 1
36 expect(dayOfYear(2000, 3, 1)).toBe(61); // leap: 31 + 29 + 1
37 expect(dayOfYear(2001, 12, 31)).toBe(365);
38 });
39
40 it('fromDayOfYear inverts dayOfYear', () => {
41 for (const [year, m, d] of [[2001, 3, 1], [2000, 3, 1], [2026, 6, 18], [1999, 12, 31]] as const) {
42 expect(fromDayOfYear(year, dayOfYear(year, m, d))).toEqual({ month: m, day: d });
43 }
44 });
45
46 it('fromDayOfYear clamps out-of-range input', () => {
47 expect(fromDayOfYear(2001, 999)).toEqual({ month: 12, day: 31 });
48 expect(fromDayOfYear(2001, 0)).toEqual({ month: 1, day: 1 });
49 });
50});
51
52describe('toHistoricalDate / fromHistoricalDate', () => {
53 it('passes CE years through unchanged', () => {
54 expect(toHistoricalDate({ year: 1700, month: 3, day: 22, era: 'CE' })).toEqual({ year: 1700, month: 3, day: 22 });
55 });
56
57 it('maps BCE onto astronomical numbering (1 BCE = 0)', () => {
58 expect(toHistoricalDate({ year: 1, month: 1, day: 1, era: 'BCE' })).toEqual({ year: 0, month: 1, day: 1 });
59 expect(toHistoricalDate({ year: 44, month: 3, day: 15, era: 'BCE' })).toEqual({ year: -43, month: 3, day: 15 });
60 });
61
62 it('round-trips both directions', () => {
63 for (const d of [
64 { year: 2026, month: 6, day: 18, era: 'CE' as const },
65 { year: 1, month: 1, day: 1, era: 'BCE' as const },
66 { year: 44, month: 3, day: 15, era: 'BCE' as const },
67 ]) {
68 expect(fromHistoricalDate(toHistoricalDate(d))).toEqual(d);
69 }
70 });
71});
72
73describe('clampDay', () => {
74 it('clamps a day past the end of the month', () => {
75 expect(clampDay({ year: 2025, month: 2, day: 31, era: 'CE' })).toMatchObject({ month: 2, day: 28 });
76 expect(clampDay({ year: 2024, month: 2, day: 31, era: 'CE' })).toMatchObject({ day: 29 });
77 });
78
79 it('leaves a valid day alone', () => {
80 expect(clampDay({ year: 2026, month: 6, day: 15, era: 'CE' })).toMatchObject({ day: 15 });
81 });
82});