From 7eec4b655047d9fb558b9826a2ca5b9e04297c00 Mon Sep 17 00:00:00 2001 From: JuanLuisAvilaCervera Date: Sun, 14 Sep 2025 17:39:35 +0200 Subject: [PATCH 1/5] First instance of multipleunits, accepts any string that has any number of the previously accepted values so long as they are split by a space --- src/index.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index d50e3c7..f734cc6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -80,7 +80,7 @@ export function parse(str: string): number { ); if (!match?.groups) { - return NaN; + return multipleUnits(str); } // Named capture groups need to be manually typed today. @@ -157,6 +157,25 @@ export function parseStrict(value: StringValue): number { return parse(value); } +/** + * Parse the given string with multiple time units and return milliseconds. + * + */ + +function multipleUnits(value: string): number { + + const splitUnits : string[] = value.split(" "); + + + //No comprueba si están en orden o repetidos + + return splitUnits.reduce( + (accumulator, unit) => parse(unit) + accumulator, 0, + ); + +} + + /** * Short format for `ms`. */ From 6eebd1e5c3f5da7cbeff37986a83bcb829ef2b51 Mon Sep 17 00:00:00 2001 From: JuanLuisAvilaCervera Date: Sun, 14 Sep 2025 22:02:26 +0200 Subject: [PATCH 2/5] Finished multipleUnits function, passes previous tests, pending test for the specific function and change function and variable names --- src/index.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index f734cc6..6aa3838 100644 --- a/src/index.ts +++ b/src/index.ts @@ -159,23 +159,22 @@ export function parseStrict(value: StringValue): number { /** * Parse the given string with multiple time units and return milliseconds. - * + * */ function multipleUnits(value: string): number { + const regEx = + /\d*\.?\d+ *(?:milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)/gi; - const splitUnits : string[] = value.split(" "); - - - //No comprueba si están en orden o repetidos + const match = [...value.matchAll(regEx)].flat(); - return splitUnits.reduce( - (accumulator, unit) => parse(unit) + accumulator, 0, - ); + if (match.length === 0) { + return NaN; + } + return match.reduce((accumulator, unit) => parse(unit) + accumulator, 0); } - /** * Short format for `ms`. */ From f29d4f003f7db20911e4f6424455b1ca0d6e94da Mon Sep 17 00:00:00 2001 From: JuanLuisAvilaCervera Date: Mon, 15 Sep 2025 20:11:52 +0200 Subject: [PATCH 3/5] tests finished and functional --- src/index.test.ts | 21 +++++++++++++++++++++ src/index.ts | 7 +++++-- src/parse-strict.test.ts | 21 +++++++++++++++++++++ src/parse.test.ts | 21 +++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 5035ab9..af8d79b 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -385,3 +385,24 @@ describe('ms(invalid inputs)', () => { }).toThrow(); }); }); + +//multiple units +describe('ms(multipleunits)', () => { + it('should not throw an error, when ms(hasmorethanoneunit)', () => { + expect(() => { + ms('3y40s'); + }).not.toThrow(); + expect(() => { + ms('3y 40s'); + }).not.toThrow(); + expect(() => { + ms('3 y 40 s'); + }).not.toThrow(); + }); + + it('should convert all values into ms', () => { + expect(ms('1 h 45 s')).toBe(3645000); + expect(ms('3seconds40s')).toBe(43000); + expect(ms('1m1mo1ms')).toBe(2629860001) + }); +}); diff --git a/src/index.ts b/src/index.ts index 6aa3838..f1856cd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,7 +29,10 @@ type UnitAnyCase = Capitalize | Uppercase | Unit; export type StringValue = | `${number}` | `${number}${UnitAnyCase}` - | `${number} ${UnitAnyCase}`; + | `${number} ${UnitAnyCase}` + | `${`${number}${Years}` | ``}${`${number}${Months}` | ``}${`${number}${Weeks}` | ``}${`${number}${Days}` | ``}${`${number}${Hours}` | ``}${`${number}${Minutes}` | ``}${`${number}${Seconds}` | ``}${`${number}${Milliseconds}` | `${number}` | ``}` + | `${`${number} ${Years}` | ``} ${`${number} ${Months}` | ``} ${`${number} ${Weeks}` | ``} ${`${number} ${Days}` | ``} ${`${number} ${Hours}` | ``} ${`${number} ${Minutes}` | ``} ${`${number} ${Seconds}` | ``} ${`${number} ${Milliseconds}` | `${number}` | ``}` + | `${`${number}${Years}` | ``} ${`${number}${Months}` | ``} ${`${number}${Weeks}` | ``} ${`${number}${Days}` | ``} ${`${number}${Hours}` | ``} ${`${number}${Minutes}` | ``} ${`${number}${Seconds}` | ``} ${`${number}${Milliseconds}` | `${number}` | ``}`; interface Options { /** @@ -164,7 +167,7 @@ export function parseStrict(value: StringValue): number { function multipleUnits(value: string): number { const regEx = - /\d*\.?\d+ *(?:milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)/gi; + /\d*\.?\d+ *(?:milliseconds?|msecs?|ms|seconds?|secs?|s|months?|mo|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)/gi; const match = [...value.matchAll(regEx)].flat(); diff --git a/src/parse-strict.test.ts b/src/parse-strict.test.ts index 9c30a5e..57a79a1 100644 --- a/src/parse-strict.test.ts +++ b/src/parse-strict.test.ts @@ -219,3 +219,24 @@ describe('parseStrict(invalid inputs)', () => { }).toThrow(); }); }); + +//multiple units +describe('ms(multipleunits)', () => { + it('should not throw an error, when ms(hasmorethanoneunit)', () => { + expect(() => { + parseStrict('3y40s'); + }).not.toThrow(); + expect(() => { + parseStrict('3y 40s'); + }).not.toThrow(); + expect(() => { + parseStrict('3 y 40 s'); + }).not.toThrow(); + }); + + it('should convert all values into ms', () => { + expect(parseStrict('1 h 45 s')).toBe(3645000); + expect(parseStrict('3seconds40s')).toBe(43000); + }); + +}); diff --git a/src/parse.test.ts b/src/parse.test.ts index 9182411..6f2b25c 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -206,3 +206,24 @@ describe('parse(invalid inputs)', () => { }).toThrow(); }); }); + +//multiple units +describe('ms(multipleunits)', () => { + it('should not throw an error, when ms(hasmorethanoneunit)', () => { + expect(() => { + parse('3y40s'); + }).not.toThrow(); + expect(() => { + parse('3y 40s'); + }).not.toThrow(); + expect(() => { + parse('3 y 40 s'); + }).not.toThrow(); + }); + + it('should convert all values into ms', () => { + expect(parse('1 h 45 s')).toBe(3645000); + expect(parse('3seconds40s')).toBe(43000); + }); + +}); From f973f1587bb57844925c73e2605b068f0271d4b3 Mon Sep 17 00:00:00 2001 From: JuanLuisAvilaCervera Date: Mon, 15 Sep 2025 22:39:31 +0200 Subject: [PATCH 4/5] last details, tests fully functional, checks if invalid input returns NaN --- src/index.test.ts | 6 +++++- src/index.ts | 11 ++++++++++- src/parse-strict.test.ts | 1 - src/parse.test.ts | 3 +++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index af8d79b..a4cebc9 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -403,6 +403,10 @@ describe('ms(multipleunits)', () => { it('should convert all values into ms', () => { expect(ms('1 h 45 s')).toBe(3645000); expect(ms('3seconds40s')).toBe(43000); - expect(ms('1m1mo1ms')).toBe(2629860001) + expect(ms('1m1mo1ms')).toBe(2629860001); }); + + it('should return NaN if invalid' , () => { + expect(Number.isNaN(ms('3h2failure'))).toBe(true); + }) }); diff --git a/src/index.ts b/src/index.ts index f1856cd..9bd2fbe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,6 +85,7 @@ export function parse(str: string): number { if (!match?.groups) { return multipleUnits(str); } + // Named capture groups need to be manually typed today. // https://github.com/microsoft/TypeScript/issues/32098 @@ -147,6 +148,8 @@ export function parse(str: string): number { `Unknown unit "${matchUnit}" provided to ms.parse(). value=${JSON.stringify(str)}`, ); } + + } /** @@ -163,6 +166,10 @@ export function parseStrict(value: StringValue): number { /** * Parse the given string with multiple time units and return milliseconds. * + * @param value - A typesafe StringValue comprised of multiple strings to convert + * into milliseconds + * @returns The sum of all parsed strings value in milliseconds, or `NaN` if the + * string can't be parsed */ function multipleUnits(value: string): number { @@ -170,8 +177,10 @@ function multipleUnits(value: string): number { /\d*\.?\d+ *(?:milliseconds?|msecs?|ms|seconds?|secs?|s|months?|mo|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)/gi; const match = [...value.matchAll(regEx)].flat(); + const unmatchedString = value.replaceAll(regEx , ''); + const spaceRegEx = /^ *$/; - if (match.length === 0) { + if (match.length === 0 || spaceRegEx.exec(unmatchedString) === null) { return NaN; } diff --git a/src/parse-strict.test.ts b/src/parse-strict.test.ts index 57a79a1..3ee9869 100644 --- a/src/parse-strict.test.ts +++ b/src/parse-strict.test.ts @@ -238,5 +238,4 @@ describe('ms(multipleunits)', () => { expect(parseStrict('1 h 45 s')).toBe(3645000); expect(parseStrict('3seconds40s')).toBe(43000); }); - }); diff --git a/src/parse.test.ts b/src/parse.test.ts index 6f2b25c..1895e49 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -226,4 +226,7 @@ describe('ms(multipleunits)', () => { expect(parse('3seconds40s')).toBe(43000); }); + it('should return NaN if invalid' , () => { + expect(Number.isNaN(parse('3h2failure'))).toBe(true); + }) }); From 112644a1d22774b85f907d5ed07b9f4ae06bd0f1 Mon Sep 17 00:00:00 2001 From: JuanLuisAvilaCervera Date: Tue, 16 Sep 2025 16:30:18 +0200 Subject: [PATCH 5/5] format run biome --- src/index.test.ts | 4 ++-- src/index.ts | 5 +---- src/parse.test.ts | 6 +++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index a4cebc9..baa08c0 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -406,7 +406,7 @@ describe('ms(multipleunits)', () => { expect(ms('1m1mo1ms')).toBe(2629860001); }); - it('should return NaN if invalid' , () => { + it('should return NaN if invalid', () => { expect(Number.isNaN(ms('3h2failure'))).toBe(true); - }) + }); }); diff --git a/src/index.ts b/src/index.ts index 9bd2fbe..e516bab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,7 +85,6 @@ export function parse(str: string): number { if (!match?.groups) { return multipleUnits(str); } - // Named capture groups need to be manually typed today. // https://github.com/microsoft/TypeScript/issues/32098 @@ -148,8 +147,6 @@ export function parse(str: string): number { `Unknown unit "${matchUnit}" provided to ms.parse(). value=${JSON.stringify(str)}`, ); } - - } /** @@ -177,7 +174,7 @@ function multipleUnits(value: string): number { /\d*\.?\d+ *(?:milliseconds?|msecs?|ms|seconds?|secs?|s|months?|mo|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)/gi; const match = [...value.matchAll(regEx)].flat(); - const unmatchedString = value.replaceAll(regEx , ''); + const unmatchedString = value.replaceAll(regEx, ''); const spaceRegEx = /^ *$/; if (match.length === 0 || spaceRegEx.exec(unmatchedString) === null) { diff --git a/src/parse.test.ts b/src/parse.test.ts index 1895e49..a560334 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -226,7 +226,7 @@ describe('ms(multipleunits)', () => { expect(parse('3seconds40s')).toBe(43000); }); - it('should return NaN if invalid' , () => { - expect(Number.isNaN(parse('3h2failure'))).toBe(true); - }) + it('should return NaN if invalid', () => { + expect(Number.isNaN(parse('3h2failure'))).toBe(true); + }); });