Skip to content

Commit 063af93

Browse files
author
Peter Rushforth
committed
Fix bug: use date arithmetic for periods containing years or month like P1M, P1Y, P3M, instead of using approximate milliseconds
1 parent 757abf9 commit 063af93

1 file changed

Lines changed: 51 additions & 13 deletions

File tree

src/script/main.js

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,30 +45,68 @@ function parseISO8601Interval(intervalString) {
4545
const [startStr, endStr, periodStr] = parts;
4646
const startTime = new Date(startStr).getTime();
4747
const endTime = new Date(endStr).getTime();
48-
const periodMs = parseISO8601Duration(periodStr);
4948

50-
if (isNaN(startTime) || isNaN(endTime) || periodMs === 0) {
49+
if (isNaN(startTime) || isNaN(endTime)) {
5150
console.warn('Invalid ISO8601 interval:', intervalString);
5251
return [];
5352
}
5453

55-
const values = [];
56-
let currentTime = startTime;
54+
// Parse the period to determine if it contains year/month components
55+
const periodMatch = periodStr.match(/P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?/);
56+
if (!periodMatch) {
57+
console.warn('Invalid ISO8601 period:', periodStr);
58+
return [];
59+
}
60+
61+
const years = parseInt(periodMatch[1] || 0);
62+
const months = parseInt(periodMatch[2] || 0);
63+
const days = parseInt(periodMatch[3] || 0);
64+
const hours = parseInt(periodMatch[4] || 0);
65+
const minutes = parseInt(periodMatch[5] || 0);
66+
const seconds = parseFloat(periodMatch[6] || 0);
5767

5868
// Detect if original format includes milliseconds
5969
const hasMilliseconds = startStr.includes('.');
6070

61-
// Generate values from start to end with period increments
62-
while (currentTime <= endTime) {
63-
let isoString = new Date(currentTime).toISOString();
64-
65-
// Remove milliseconds if original format didn't have them
66-
if (!hasMilliseconds) {
67-
isoString = isoString.replace(/\.\d{3}Z$/, 'Z');
71+
const values = [];
72+
let currentDate = new Date(startStr);
73+
const endDate = new Date(endStr);
74+
75+
// If period includes years or months, use date arithmetic (not milliseconds)
76+
if (years > 0 || months > 0) {
77+
while (currentDate <= endDate) {
78+
let isoString = currentDate.toISOString();
79+
if (!hasMilliseconds) {
80+
isoString = isoString.replace(/\.\d{3}Z$/, 'Z');
81+
}
82+
values.push(isoString);
83+
84+
// Add period using date methods to handle month/year boundaries correctly
85+
currentDate = new Date(currentDate);
86+
currentDate.setUTCFullYear(currentDate.getUTCFullYear() + years);
87+
currentDate.setUTCMonth(currentDate.getUTCMonth() + months);
88+
currentDate.setUTCDate(currentDate.getUTCDate() + days);
89+
currentDate.setUTCHours(currentDate.getUTCHours() + hours);
90+
currentDate.setUTCMinutes(currentDate.getUTCMinutes() + minutes);
91+
currentDate.setUTCSeconds(currentDate.getUTCSeconds() + seconds);
92+
}
93+
} else {
94+
// For time-only periods (hours, minutes, seconds), use millisecond arithmetic
95+
const periodMs = parseISO8601Duration(periodStr);
96+
if (periodMs === 0) {
97+
console.warn('Invalid period duration:', periodStr);
98+
return [];
6899
}
69100

70-
values.push(isoString);
71-
currentTime += periodMs;
101+
let currentTime = startTime;
102+
while (currentTime <= endTime) {
103+
let isoString = new Date(currentTime).toISOString();
104+
if (!hasMilliseconds) {
105+
isoString = isoString.replace(/\.\d{3}Z$/, 'Z');
106+
}
107+
values.push(isoString);
108+
currentTime += periodMs;
109+
}
72110
}
73111

74112
return values;

0 commit comments

Comments
 (0)