import { describe, expect, it } from 'vitest'; import { approxLocalTime, approxZoneForLon } from './localTime'; describe('approxZoneForLon', () => { it('maps longitude to the Etc/GMT±N zone with the POSIX sign flip', () => { // UTC+1 (lon +15) is "Etc/GMT-1"; UTC−2 (lon −30) is "Etc/GMT2". expect(approxZoneForLon(15)).toBe('Etc/GMT-1'); expect(approxZoneForLon(-30)).toBe('Etc/GMT2'); expect(approxZoneForLon(0)).toBe('Etc/GMT'); }); it('rounds to the nearest 15° band and clamps at ±12', () => { expect(approxZoneForLon(10)).toBe('Etc/GMT-1'); // Frankfurt ≈ UTC+1 expect(approxZoneForLon(-6.2)).toBe('Etc/GMT'); // Lisbon: round(-0.41) = 0 → plain Etc/GMT (UTC) expect(approxZoneForLon(179)).toBe('Etc/GMT-12'); expect(approxZoneForLon(-179)).toBe('Etc/GMT12'); }); }); describe('approxLocalTime', () => { const noonUtc = Date.UTC(2026, 8, 17, 12, 0); // 2026-09-17 12:00 UTC it('formats HH:MM in the approximate zone (deterministic)', () => { expect(approxLocalTime(15, noonUtc)).toBe('13:00'); // east of Greenwich → UTC+1 expect(approxLocalTime(-30, noonUtc)).toBe('10:00'); // west of Greenwich → UTC−2 expect(approxLocalTime(0, noonUtc)).toBe('12:00'); // UTC }); it('wraps across midnight', () => { const lateUtc = Date.UTC(2026, 8, 17, 23, 30); expect(approxLocalTime(45, lateUtc)).toBe('02:30'); // UTC+3 → next day const earlyUtc = Date.UTC(2026, 8, 17, 1, 30); expect(approxLocalTime(-45, earlyUtc)).toBe('22:30'); // UTC−3 → previous day }); it('always yields a two-digit HH:MM string', () => { for (const lon of [-170, -6.2, 8.56, 45, 179]) { expect(approxLocalTime(lon, noonUtc)).toMatch(/^\d{2}:\d{2}$/); } }); });