Skip to content

Commit 6bf562c

Browse files
committed
fix(duration): preserve weeks in toISOString output
Fix issue where weeks were being converted to days in ISO duration strings. Now weeks are preserved as a separate unit in the output, conforming to ISO 8601 standard. Fixes [#2859](#2859)
1 parent 807face commit 6bf562c

2 files changed

Lines changed: 51 additions & 10 deletions

File tree

src/plugin/duration/index.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,8 @@ class Duration {
127127
toISOString() {
128128
const Y = getNumberUnitFormat(this.$d.years, 'Y')
129129
const M = getNumberUnitFormat(this.$d.months, 'M')
130-
131-
let days = +this.$d.days || 0
132-
if (this.$d.weeks) {
133-
days += this.$d.weeks * 7
134-
}
135-
136-
const D = getNumberUnitFormat(days, 'D')
130+
const W = getNumberUnitFormat(this.$d.weeks, 'W')
131+
const D = getNumberUnitFormat(this.$d.days, 'D')
137132
const H = getNumberUnitFormat(this.$d.hours, 'H')
138133
const m = getNumberUnitFormat(this.$d.minutes, 'M')
139134

@@ -148,6 +143,7 @@ class Duration {
148143
const negativeMode =
149144
Y.negative ||
150145
M.negative ||
146+
W.negative ||
151147
D.negative ||
152148
H.negative ||
153149
m.negative ||
@@ -156,7 +152,7 @@ class Duration {
156152
const T = H.format || m.format || S.format ? 'T' : ''
157153
const P = negativeMode ? '-' : ''
158154

159-
const result = `${P}P${Y.format}${M.format}${D.format}${T}${H.format}${m.format}${S.format}`
155+
const result = `${P}P${Y.format}${M.format}${W.format}${D.format}${T}${H.format}${m.format}${S.format}`
160156
return result === 'P' || result === '-P' ? 'P0D' : result
161157
}
162158

@@ -261,6 +257,7 @@ class Duration {
261257
const manipulateDuration = (date, duration, k) =>
262258
date.add(duration.years() * k, 'y')
263259
.add(duration.months() * k, 'M')
260+
.add((duration.$d.weeks || 0) * k, 'w')
264261
.add(duration.days() * k, 'd')
265262
.add(duration.hours() * k, 'h')
266263
.add(duration.minutes() * k, 'm')

test/plugin/duration.test.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,18 @@ describe('Creating', () => {
5555
weeks: 5,
5656
months: 6,
5757
years: 7
58-
}).toISOString()).toBe('P7Y6M39DT3H2M1.1S')
58+
}).toISOString()).toBe('P7Y6M5W4DT3H2M1.1S')
59+
})
60+
it('object with only weeks', () => {
61+
expect(dayjs.duration({
62+
weeks: 2
63+
}).toISOString()).toBe('P2W')
64+
})
65+
it('object with year and weeks', () => {
66+
expect(dayjs.duration({
67+
year: 1,
68+
week: 2
69+
}).toISOString()).toBe('P1Y2W')
5970
})
6071
it('object with millisecond', () => {
6172
expect(dayjs.duration({
@@ -100,14 +111,29 @@ describe('Parse ISO string', () => {
100111
})
101112
it('ISO string with week', () => {
102113
const d = dayjs.duration('P2M3W4D')
103-
expect(d.toISOString()).toBe('P2M25D')
114+
expect(d.toISOString()).toBe('P2M3W4D')
104115
expect(d.asDays()).toBe(85.83333333333333) // moment 86, count 2M as 61 days
105116
expect(d.asWeeks()).toBe(12.261904761904763) // moment 12.285714285714286
106117
expect(d.asMonths()).toBe(2.8219178082191783) // moment 2.8213721020965523
107118
})
119+
it('ISO string with only weeks', () => {
120+
expect(dayjs.duration('P3W').toISOString()).toBe('P3W')
121+
})
122+
it('ISO string preserves weeks and days separately', () => {
123+
expect(dayjs.duration('P2Y3W5D').toISOString()).toBe('P2Y3W5D')
124+
})
125+
it('ISO string with weeks and time components', () => {
126+
expect(dayjs.duration('P1Y2W3DT4H5M6S').toISOString()).toBe('P1Y2W3DT4H5M6S')
127+
})
108128
it('Invalid ISO string', () => {
109129
expect(dayjs.duration('Invalid').toISOString()).toBe('P0D')
110130
})
131+
it('Negative duration with weeks', () => {
132+
expect(dayjs.duration({
133+
weeks: -2,
134+
days: -3
135+
}).toISOString()).toBe('-P2W3D')
136+
})
111137
})
112138

113139
it('Is duration', () => {
@@ -209,6 +235,15 @@ test('Add duration', () => {
209235
const b = dayjs('2023-02-01 00:00:00')
210236
const p = dayjs.duration('P1Y1M1DT1H1M1S')
211237
expect(b.add(p).format('YYYY-MM-DD HH:mm:ss')).toBe('2024-03-02 01:01:01')
238+
239+
// Test duration with weeks
240+
const c = dayjs('2020-01-01')
241+
const weeks = dayjs.duration({ week: 2 })
242+
expect(c.add(weeks).format('YYYY-MM-DD')).toBe('2020-01-15')
243+
244+
const d = dayjs('2020-01-01')
245+
const weeksAndDays = dayjs.duration('P2W3D')
246+
expect(d.add(weeksAndDays).format('YYYY-MM-DD')).toBe('2020-01-18')
212247
})
213248

214249
describe('Subtract', () => {
@@ -225,6 +260,15 @@ test('Subtract duration', () => {
225260
const b = dayjs('2023-03-02 02:02:02')
226261
const p = dayjs.duration('P1Y1M1DT1H1M1S')
227262
expect(b.subtract(p).format('YYYY-MM-DD HH:mm:ss')).toBe('2022-02-01 01:01:01')
263+
264+
// Test duration with weeks
265+
const c = dayjs('2020-01-20')
266+
const weeks = dayjs.duration({ week: 2 })
267+
expect(c.subtract(weeks).format('YYYY-MM-DD')).toBe('2020-01-06')
268+
269+
const d = dayjs('2020-01-20')
270+
const weeksAndDays = dayjs.duration('P1W5D')
271+
expect(d.subtract(weeksAndDays).format('YYYY-MM-DD')).toBe('2020-01-08')
228272
})
229273

230274
describe('Seconds', () => {

0 commit comments

Comments
 (0)