diff --git a/lib/DateTimeUtils.test.ts b/lib/DateTimeUtils.test.ts index c6c61f4..b5f6149 100644 --- a/lib/DateTimeUtils.test.ts +++ b/lib/DateTimeUtils.test.ts @@ -23,3 +23,16 @@ describe('DateTimeUtils.UTCDateTimeToString', () => { expect(out).toContain('12:30:45'); }); }); + +describe('DateTimeUtils.timestampToString', () => { + it('returns an empty string for NaN instead of throwing RangeError', () => { + // new Date(NaN).toISOString() throws "Invalid time value". A malformed + // time field (e.g. convertHHMMSSToTod("ABCD")) must not abort the decode. + expect(() => DateTimeUtils.timestampToString(NaN)).not.toThrow(); + expect(DateTimeUtils.timestampToString(NaN)).toBe(''); + }); + + it('still formats a valid time-of-day', () => { + expect(DateTimeUtils.timestampToString(4980)).toBe('01:23:00'); + }); +}); diff --git a/lib/DateTimeUtils.ts b/lib/DateTimeUtils.ts index 3bea6b1..9912829 100644 --- a/lib/DateTimeUtils.ts +++ b/lib/DateTimeUtils.ts @@ -77,6 +77,9 @@ export class DateTimeUtils { * @returns */ public static timestampToString(time: number): string { + if (isNaN(time)) { + return ''; + } const date = new Date(time * 1000); if (time < 86400) { diff --git a/lib/plugins/Label_44_ETA.test.ts b/lib/plugins/Label_44_ETA.test.ts index 2a2c720..4464999 100644 --- a/lib/plugins/Label_44_ETA.test.ts +++ b/lib/plugins/Label_44_ETA.test.ts @@ -58,6 +58,31 @@ describe('Label 44 Preamble ETA', () => { expect(decodeResult.formatted.items[8].value).toBe('8.1'); }); + test('skips a malformed timestamp field instead of throwing (issue #498)', () => { + // convertHHMMSSToTod('ABCD') yields NaN; before the fix this aborted + // the decode with "RangeError: Invalid time value". + message.text = '00ETA03,N38241W081357,330,KBNA,KBWI,1107,ABCD,0208,008.1'; + let decodeResult; + expect(() => { + decodeResult = plugin.decode(message); + }).not.toThrow(); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.raw.message_timestamp).toBeUndefined(); + expect(decodeResult.raw.eta_time).toBe(7680); + expect(decodeResult.raw.departure_icao).toBe('KBNA'); + expect(decodeResult.raw.arrival_icao).toBe('KBWI'); + // 9 items minus the skipped Message Timestamp + expect(decodeResult.formatted.items.length).toBe(8); + expect(decodeResult.formatted.items[6].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[6].value).toBe('02:08:00'); + expect( + decodeResult.formatted.items.some((i) => i.code === 'TIMESTAMP'), + ).toBe(false); + }); + test('does not decode invalid', () => { message.text = '00OFF01 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/utils/result_formatter.test.ts b/lib/utils/result_formatter.test.ts index f51a9a2..510c5c5 100644 --- a/lib/utils/result_formatter.test.ts +++ b/lib/utils/result_formatter.test.ts @@ -63,3 +63,35 @@ describe('ResultFormatter.checksum', () => { expect(item.value).toBe('0x0abc'); }); }); + +describe('ResultFormatter timestamp family NaN guards', () => { + // Malformed time fields (e.g. convertHHMMSSToTod('ABCD') -> NaN) must not + // abort the whole decode via timestampToString's RangeError (issue #498). + const cases: Array<[string, (dr: DecodeResult) => void, string]> = [ + ['timestamp', (dr) => ResultFormatter.timestamp(dr, NaN), 'message_timestamp'], + ['eta', (dr) => ResultFormatter.eta(dr, NaN), 'eta_time'], + ['out', (dr) => ResultFormatter.out(dr, NaN), 'out_time'], + ['off', (dr) => ResultFormatter.off(dr, NaN), 'off_time'], + ['on', (dr) => ResultFormatter.on(dr, NaN), 'on_time'], + ['in', (dr) => ResultFormatter.in(dr, NaN), 'in_time'], + ['engineStart', (dr) => ResultFormatter.engineStart(dr, NaN), 'engine_start_time'], + ['engineStop', (dr) => ResultFormatter.engineStop(dr, NaN), 'engine_stop_time'], + ]; + + test.each(cases)( + '%s skips NaN instead of throwing', + (_name, invoke, rawKey) => { + const dr = makeDecodeResult(); + expect(() => invoke(dr)).not.toThrow(); + expect(dr.formatted.items.length).toBe(0); + expect((dr.raw as Record)[rawKey]).toBeUndefined(); + }, + ); + + test('eta still formats a valid time-of-day', () => { + const dr = makeDecodeResult(); + ResultFormatter.eta(dr, 7680); + expect(dr.raw.eta_time).toBe(7680); + expect(dr.formatted.items[0].value).toBe('02:08:00'); + }); +}); diff --git a/lib/utils/result_formatter.ts b/lib/utils/result_formatter.ts index a816404..515d4c4 100644 --- a/lib/utils/result_formatter.ts +++ b/lib/utils/result_formatter.ts @@ -169,6 +169,9 @@ export class ResultFormatter { } static eta(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.eta_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -380,6 +383,9 @@ export class ResultFormatter { } static out(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.out_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -390,6 +396,9 @@ export class ResultFormatter { } static off(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.off_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -400,6 +409,9 @@ export class ResultFormatter { } static on(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.on_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -410,6 +422,9 @@ export class ResultFormatter { } static in(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.in_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -420,6 +435,9 @@ export class ResultFormatter { } static engineStart(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.engine_start_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -429,6 +447,9 @@ export class ResultFormatter { }); } static engineStop(decodeResult: DecodeResult, time: number) { + if (isNaN(time)) { + return; + } decodeResult.raw.engine_stop_time = time; decodeResult.formatted.items.push({ type: 'time', @@ -651,6 +672,9 @@ export class ResultFormatter { } static timestamp(decodeResult: DecodeResult, value: number) { + if (isNaN(value)) { + return; + } decodeResult.raw.message_timestamp = value; decodeResult.formatted.items.push({ type: 'time',