From a9a2db8edebf0342a44c66f5fd1b6c7cfd34c076 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Tue, 23 Jun 2026 23:34:56 +0100 Subject: [PATCH] fix(duration): apply the leading sign when parsing a negative ISO duration The ISO-8601 duration parser called d.slice(2) and never read the sign captured in d[1], so a leading-minus string parsed as positive. toISOString() correctly emits -P..., so the round-trip duration(d.toISOString()) flipped the sign. Derive a sign multiplier from d[1] === '-' and apply it to each parsed component. Scope is the leading-sign case only; per-component signs are a separate serializer limitation. Refs #1516 --- src/plugin/duration/index.js | 4 +++- test/plugin/duration.test.js | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugin/duration/index.js b/src/plugin/duration/index.js index 5aaacee21..d1f09c90c 100644 --- a/src/plugin/duration/index.js +++ b/src/plugin/duration/index.js @@ -84,7 +84,9 @@ class Duration { const d = input.match(DURATION_REGEX_PARSE) if (d) { const properties = d.slice(2) - const numberD = properties.map(value => (value != null ? Number(value) : 0)); + const signMultiplier = d[1] === '-' ? -1 : 1 + const numberD = properties.map(value => + (value != null ? Number(value) * signMultiplier : 0)); [ this.$d.years, this.$d.months, diff --git a/test/plugin/duration.test.js b/test/plugin/duration.test.js index f9a4c7cf4..e9ce74c16 100644 --- a/test/plugin/duration.test.js +++ b/test/plugin/duration.test.js @@ -108,6 +108,13 @@ describe('Parse ISO string', () => { it('Invalid ISO string', () => { expect(dayjs.duration('Invalid').toISOString()).toBe('P0D') }) + it('Negative ISO string round-trips', () => { + expect(dayjs.duration('-P1Y').asYears()).toBe(-1) + expect(dayjs.duration('-P1DT2H').asHours()).toBe(-26) + expect(dayjs.duration('-PT59S').toISOString()).toBe('-PT59S') + const d = dayjs.duration(-1, 'years') + expect(dayjs.duration(d.toISOString()).asMilliseconds()).toBe(d.asMilliseconds()) + }) }) it('Is duration', () => {