mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(shared): read the regex groups defensively in the datetime helper
shared compiles with noUncheckedIndexedAccess, so a capture group is string | undefined there even when the pattern guarantees it. Pull the groups through optional access with explicit fallbacks instead of indexing into the match, which also drops the two throwaway locals. No behaviour change: same inputs, same outputs, the spec is untouched.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
const MERIDIEM_CLOCK = /^(\d{1,2})(?::(\d{2}))?(?::\d{2})?\s*([ap])\.?\s?m\.?$/i;
|
||||
const LEADING_DATE = /^(\d{4}-\d{2}-\d{2})/;
|
||||
|
||||
/**
|
||||
* '3:00 PM' / '02:30:00 pm' / '3 PM' → '15:00'.
|
||||
@@ -27,34 +28,40 @@ const MERIDIEM_CLOCK = /^(\d{1,2})(?::(\d{2}))?(?::\d{2})?\s*([ap])\.?\s?m\.?$/i
|
||||
*/
|
||||
export function parseMeridiemClock(value: string | null | undefined): string | null {
|
||||
const m = MERIDIEM_CLOCK.exec((value || '').trim());
|
||||
if (!m) return null;
|
||||
let hour = Number.parseInt(m[1], 10);
|
||||
const minute = m[2] ? Number.parseInt(m[2], 10) : 0;
|
||||
const rawHour = m?.[1];
|
||||
const half = m?.[3];
|
||||
if (!rawHour || !half) return null;
|
||||
|
||||
let hour = Number.parseInt(rawHour, 10);
|
||||
const minute = Number.parseInt(m?.[2] ?? '0', 10);
|
||||
if (!Number.isFinite(hour) || hour > 12) return null;
|
||||
const isPm = m[3].toLowerCase() === 'p';
|
||||
|
||||
const isPm = half.toLowerCase() === 'p';
|
||||
// 12 AM is midnight and 12 PM is noon: the hour does not shift at 12.
|
||||
if (hour === 12) hour = isPm ? 12 : 0;
|
||||
else if (isPm) hour += 12;
|
||||
|
||||
return `${String(hour).padStart(2, '0')}:${String(Math.min(59, minute)).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/** The part after a leading `YYYY-MM-DD`, with its `T` or space removed. */
|
||||
function clockPart(value: string): string {
|
||||
return value.slice(10).replace(/^[T\s]/, '').trim();
|
||||
}
|
||||
|
||||
/** Split a naive date-time string into its date and its 24-hour clock. */
|
||||
export function splitLocalDateTime(value: string | null | undefined): { date: string | null; time: string | null } {
|
||||
const raw = (value || '').trim();
|
||||
if (!raw) return { date: null, time: null };
|
||||
const date = LEADING_DATE.exec(raw)?.[1];
|
||||
if (!date) return { date: null, time: null };
|
||||
|
||||
const dateMatch = /^(\d{4}-\d{2}-\d{2})/.exec(raw);
|
||||
if (!dateMatch) return { date: null, time: null };
|
||||
const date = dateMatch[1];
|
||||
|
||||
const rest = raw.slice(10).replace(/^[T\s]/, '').trim();
|
||||
const rest = clockPart(raw);
|
||||
if (!rest) return { date, time: null };
|
||||
|
||||
const meridiem = parseMeridiemClock(rest);
|
||||
if (meridiem) return { date, time: meridiem };
|
||||
|
||||
const clock = /^(\d{2}:\d{2})/.exec(rest);
|
||||
return { date, time: clock ? clock[1] : null };
|
||||
return { date, time: /^(\d{2}:\d{2})/.exec(rest)?.[1] ?? null };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,17 +71,18 @@ export function splitLocalDateTime(value: string | null | undefined): { date: st
|
||||
*/
|
||||
export function normalizeLocalDateTime(value: string): string {
|
||||
if (!value) return value;
|
||||
const dateMatch = /^(\d{4}-\d{2}-\d{2})/.exec(value.trim());
|
||||
if (!dateMatch) return value;
|
||||
const trimmed = value.trim();
|
||||
const date = LEADING_DATE.exec(trimmed)?.[1];
|
||||
if (!date) return value;
|
||||
|
||||
// Only a printed 12-hour clock is rewritten. Everything else comes back
|
||||
// untouched, which is what makes this safe to drop into the middle of the
|
||||
// mapper: a value carrying a UTC offset, a date-only value, or a shape this
|
||||
// does not know keeps whatever it meant before.
|
||||
const rest = value.trim().slice(10).replace(/^[T\s]/, '').trim();
|
||||
const rest = clockPart(trimmed);
|
||||
const clock = parseMeridiemClock(rest);
|
||||
if (!clock) return value;
|
||||
|
||||
const seconds = /^\d{1,2}:\d{2}:(\d{2})/.exec(rest);
|
||||
return `${dateMatch[1]}T${clock}:${seconds ? seconds[1] : '00'}`;
|
||||
const seconds = /^\d{1,2}:\d{2}:(\d{2})/.exec(rest)?.[1] ?? '00';
|
||||
return `${date}T${clock}:${seconds}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user