diff --git a/lib/DateTimeUtils.test.ts b/lib/DateTimeUtils.test.ts new file mode 100644 index 0000000..73ef1e6 --- /dev/null +++ b/lib/DateTimeUtils.test.ts @@ -0,0 +1,31 @@ +import { DateTimeUtils } from './DateTimeUtils'; + +describe('DateTimeUtils.UTCDateTimeToString', () => { + it('decodes a DDMMYY date with the ACARS 1-12 month convention', () => { + // 15 February 2026, 12:30 UTC. The ACARS string is DDMMYY = "150226". + const out = DateTimeUtils.UTCDateTimeToString('150226', '1230'); + expect(out).toContain('Feb'); + expect(out).toContain('15'); + expect(out).toContain('2026'); + }); + + it('does not roll the month forward when the system date is later than the target month-end', () => { + // Pin "now" to 31 March 2026 — a 31-day month. Encoding DDMMYY = 280226 + // (28 Feb 2026) used to roll forward to March because the order of + // setUTC* calls applied "set day=28" to a Date already at the 31st. + jest.useFakeTimers(); + jest.setSystemTime(new Date(Date.UTC(2026, 2, 31, 0, 0, 0))); + try { + const out = DateTimeUtils.UTCDateTimeToString('280226', '1230'); + expect(out).toContain('Feb'); + expect(out).toContain('28'); + } finally { + jest.useRealTimers(); + } + }); + + it('handles a six-digit time with seconds', () => { + const out = DateTimeUtils.UTCDateTimeToString('150226', '123045'); + expect(out).toContain('12:30:45'); + }); +}); diff --git a/lib/DateTimeUtils.ts b/lib/DateTimeUtils.ts index f436148..3bea6b1 100644 --- a/lib/DateTimeUtils.ts +++ b/lib/DateTimeUtils.ts @@ -9,25 +9,20 @@ export class DateTimeUtils { // Expects a six digit date string and a four digit UTC time string // (DDMMYY) (HHMM) public static UTCDateTimeToString(dateString: string, timeString: string) { - let utcDate = new Date(); - utcDate.setUTCDate(+dateString.substr(0, 2)); - utcDate.setUTCMonth(+dateString.substr(2, 2)); - if (dateString.length === 6) { - utcDate.setUTCFullYear(2000 + +dateString.substr(4, 2)); - } - if (timeString.length === 6) { - utcDate.setUTCHours( - +timeString.substr(0, 2), - +timeString.substr(2, 2), - +timeString.substr(4, 2), - ); - } else { - utcDate.setUTCHours( - +timeString.substr(0, 2), - +timeString.substr(2, 2), - 0, - ); - } + const day = +dateString.substr(0, 2); + // ACARS month is 1-12; JS Date months are 0-11, so subtract one. + const month = +dateString.substr(2, 2) - 1; + const year = + dateString.length === 6 + ? 2000 + +dateString.substr(4, 2) + : new Date().getUTCFullYear(); + const hours = +timeString.substr(0, 2); + const minutes = +timeString.substr(2, 2); + const seconds = timeString.length === 6 ? +timeString.substr(4, 2) : 0; + // Build the date in one call so an incremental setUTC* on a Date that + // was initialised to "now" can't roll the month forward when the + // current real-world day is later than the last day of the target. + const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds)); return utcDate.toUTCString(); }