mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
016fdeec64
splitIso reached the time by taking characters 11..16, so "2026-06-11T01:11 PM" became 01:11. The hour survived and the half of the day did not, which is why AM and 24-hour times in the same document imported correctly. The arrival then sorted before the departure and the router rolled it onto the next day, so a three-hour hop was rendered as an overnight. The client has done this properly since #1725; this is the server half, which was never pulled across. normalizeLocalDateTime rewrites a value only when it actually finds a meridiem, so a date-only value, a value carrying an offset and anything the parser does not know come back byte for byte. The mapper has only two exits for a date, toIsoString and splitIso, so this covers all eight types and every emitted field at once. It also stops sameConnection comparing a NaN timestamp, which silently merged unrelated legs into one booking.
62 lines
2.8 KiB
TypeScript
62 lines
2.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { parseMeridiemClock, splitLocalDateTime, normalizeLocalDateTime } from './datetime-normalize';
|
|
|
|
describe('parseMeridiemClock', () => {
|
|
it('resolves the half of the day', () => {
|
|
expect(parseMeridiemClock('01:11 pm')).toBe('13:11');
|
|
expect(parseMeridiemClock('09:51 am')).toBe('09:51');
|
|
expect(parseMeridiemClock('3 PM')).toBe('15:00');
|
|
expect(parseMeridiemClock('02:30:00 p.m.')).toBe('14:30');
|
|
});
|
|
|
|
it('keeps noon at 12 and midnight at 00', () => {
|
|
expect(parseMeridiemClock('12:05 pm')).toBe('12:05');
|
|
expect(parseMeridiemClock('12:05 am')).toBe('00:05');
|
|
});
|
|
|
|
it('returns null when there is no meridiem to resolve', () => {
|
|
expect(parseMeridiemClock('13:11')).toBeNull();
|
|
expect(parseMeridiemClock('')).toBeNull();
|
|
expect(parseMeridiemClock(null)).toBeNull();
|
|
// 13 with a meridiem is not a 12-hour clock, so it is not ours to rewrite.
|
|
expect(parseMeridiemClock('13:11 pm')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('splitLocalDateTime', () => {
|
|
it('reads a printed 12-hour clock instead of slicing it', () => {
|
|
// The defect this pins: 'T02:30 PM'.slice(11, 16) is '02:30'. The hour
|
|
// survives and the booking lands twelve hours early (#2094).
|
|
expect(splitLocalDateTime('2026-06-11T02:30 PM')).toEqual({ date: '2026-06-11', time: '14:30' });
|
|
});
|
|
|
|
it('leaves a 24-hour value exactly as it is', () => {
|
|
expect(splitLocalDateTime('2026-06-11T13:11:00')).toEqual({ date: '2026-06-11', time: '13:11' });
|
|
expect(splitLocalDateTime('2026-06-11')).toEqual({ date: '2026-06-11', time: null });
|
|
});
|
|
|
|
it('has no date to give when the value does not start with one', () => {
|
|
expect(splitLocalDateTime('Aug 23 2025 10:00')).toEqual({ date: null, time: null });
|
|
expect(splitLocalDateTime(undefined)).toEqual({ date: null, time: null });
|
|
});
|
|
});
|
|
|
|
describe('normalizeLocalDateTime', () => {
|
|
it('rewrites only the clock, and keeps the seconds it was given', () => {
|
|
expect(normalizeLocalDateTime('2026-06-11T02:30 PM')).toBe('2026-06-11T14:30:00');
|
|
expect(normalizeLocalDateTime('2026-06-11T02:30:45 pm')).toBe('2026-06-11T14:30:45');
|
|
});
|
|
|
|
it('touches nothing that has no meridiem on it', () => {
|
|
// Strictly additive: a date-only value stays date-only (the calendar
|
|
// branches on whether the string contains a 'T' and would turn an all-day
|
|
// event into a midnight point), an offset survives, and an unreadable
|
|
// shape keeps whatever it meant before.
|
|
expect(normalizeLocalDateTime('2026-06-11')).toBe('2026-06-11');
|
|
expect(normalizeLocalDateTime('2026-06-11T13:11:00')).toBe('2026-06-11T13:11:00');
|
|
expect(normalizeLocalDateTime('2026-06-11T13:11:00+02:00')).toBe('2026-06-11T13:11:00+02:00');
|
|
expect(normalizeLocalDateTime('Aug 23 2025 10:00')).toBe('Aug 23 2025 10:00');
|
|
expect(normalizeLocalDateTime('')).toBe('');
|
|
});
|
|
});
|