@@ -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