From 048039d3b8f47d24fa1b3957db29fa374869d6bf Mon Sep 17 00:00:00 2001 From: Mahmoud Hamdi Date: Mon, 30 Mar 2026 00:42:12 +0200 Subject: [PATCH] fix: support scientific notation in parse() for roundtrip consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit format() produces scientific notation for very large millisecond values (e.g., `ms(Number.MAX_SAFE_INTEGER)` → `"285616y"`), and for even larger values JavaScript's number-to-string coercion uses exponential notation (e.g., `"5.7e+297y"`). The parse() regex did not accept the `e` notation, causing it to return NaN for format()'s own output. Update the regex number pattern from `-?\d*\.?\d+` to `-?\d*\.?\d+(?:e[+-]?\d+)?` so that parseFloat-compatible scientific notation is matched correctly. Fixes #284 --- src/index.test.ts | 25 ++++++++++++++++++++++++- src/index.ts | 2 +- src/parse.test.ts | 23 ++++++++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 5035ab9..4d27c42 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import { ms } from './index'; +import { ms, type StringValue } from './index'; describe('ms(string)', () => { it('should not throw an error', () => { @@ -329,6 +329,29 @@ describe('ms(number)', () => { }); }); +// roundtrip + +describe('ms(roundtrip)', () => { + it('should roundtrip for common values', () => { + expect(ms(ms('1s'))).toBe('1s'); + expect(ms(ms('1m'))).toBe('1m'); + expect(ms(ms('1h'))).toBe('1h'); + expect(ms(ms('1d'))).toBe('1d'); + expect(ms(ms('1w'))).toBe('1w'); + expect(ms(ms('1y'))).toBe('1y'); + }); + + it('should not return NaN for format() output with large numbers', () => { + const formatted = ms(Number.MAX_SAFE_INTEGER) as StringValue; + expect(Number.isNaN(ms(formatted))).toBe(false); + }); + + it('should not return NaN for format() output with large negative numbers', () => { + const formatted = ms(-Number.MAX_SAFE_INTEGER) as StringValue; + expect(Number.isNaN(ms(formatted))).toBe(false); + }); +}); + // invalid inputs describe('ms(invalid inputs)', () => { diff --git a/src/index.ts b/src/index.ts index d50e3c7..388705e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -75,7 +75,7 @@ export function parse(str: string): number { ); } const match = - /^(?-?\d*\.?\d+) *(?milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)?$/i.exec( + /^(?-?\d*\.?\d+(?:e[+-]?\d+)?) *(?milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)?$/i.exec( str, ); diff --git a/src/parse.test.ts b/src/parse.test.ts index 9182411..e53dfdc 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import { parse } from './index'; +import { format, parse } from './index'; describe('parse(string)', () => { it('should not throw an error', () => { @@ -142,6 +142,27 @@ describe('parse(long string)', () => { }); }); +// scientific notation + +describe('parse(scientific notation)', () => { + it('should handle scientific notation in number part', () => { + expect(parse('1e3ms')).toBe(1000); + expect(parse('1.5e2ms')).toBe(150); + expect(parse('1e+3ms')).toBe(1000); + expect(parse('1e-3ms')).toBe(0.001); + }); + + it('should handle format() output for very large values', () => { + const formatted = format(Number.MAX_SAFE_INTEGER); + expect(Number.isNaN(parse(formatted))).toBe(false); + }); + + it('should handle format() output for very small negative values', () => { + const formatted = format(-Number.MAX_SAFE_INTEGER); + expect(Number.isNaN(parse(formatted))).toBe(false); + }); +}); + // invalid inputs describe('parse(invalid inputs)', () => {