@@ -185,7 +185,7 @@ const monthsDesignator = character('Mm');
185
185
const durationDesignator = character ( 'Pp' ) ;
186
186
const secondsDesignator = character ( 'Ss' ) ;
187
187
const dateTimeSeparator = character ( ' Tt' ) ;
188
- const durationTimeDesignator = character ( 'Tt' ) ;
188
+ const timeDesignator = character ( 'Tt' ) ;
189
189
const weeksDesignator = character ( 'Ww' ) ;
190
190
const yearsDesignator = character ( 'Yy' ) ;
191
191
const utcDesignator = withCode ( character ( 'Zz' ) , ( data ) => {
@@ -204,12 +204,26 @@ const dateYear = withCode(
204
204
const dateMonth = withCode ( zeroPaddedInclusive ( 1 , 12 , 2 ) , ( data , result ) => ( data . month = + result ) ) ;
205
205
const dateDay = withCode ( zeroPaddedInclusive ( 1 , 31 , 2 ) , ( data , result ) => ( data . day = + result ) ) ;
206
206
207
- const timeHour = withCode ( hour , ( data , result ) => ( data . hour = + result ) ) ;
208
- const timeMinute = withCode ( minuteSecond , ( data , result ) => ( data . minute = + result ) ) ;
209
- const timeSecond = withCode ( choice ( minuteSecond , '60' ) , ( data , result ) => {
207
+ function saveHour ( data , result ) {
208
+ data . hour = + result ;
209
+ }
210
+ function saveMinute ( data , result ) {
211
+ data . minute = + result ;
212
+ }
213
+ function saveSecond ( data , result ) {
210
214
data . second = + result ;
211
215
if ( data . second === 60 ) data . second = 59 ;
212
- } ) ;
216
+ }
217
+ const timeHour = withCode ( hour , saveHour ) ;
218
+ const timeMinute = withCode ( minuteSecond , saveMinute ) ;
219
+ const timeSecond = withCode ( choice ( minuteSecond , '60' ) , saveSecond ) ;
220
+ const timeHourNotValidMonth = withCode ( choice ( '00' , zeroPaddedInclusive ( 13 , 23 , 2 ) ) , saveHour ) ;
221
+ const timeHourNot31DayMonth = withCode ( choice ( '02' , '04' , '06' , '09' , '11' ) , saveHour ) ;
222
+ const timeHour2Only = withCode ( '02' , saveHour ) ;
223
+ const timeMinuteNotValidDay = withCode ( choice ( '00' , zeroPaddedInclusive ( 32 , 59 , 2 ) ) , saveMinute ) ;
224
+ const timeMinute30Only = withCode ( '30' , saveMinute ) ;
225
+ const timeMinute31Only = withCode ( '31' , saveMinute ) ;
226
+ const timeSecondNotValidMonth = withCode ( choice ( '00' , zeroPaddedInclusive ( 13 , 60 , 2 ) ) , saveSecond ) ;
213
227
const timeFraction = withCode ( fraction , ( data , result ) => {
214
228
result = result . slice ( 1 ) ;
215
229
const fraction = result . padEnd ( 9 , '0' ) ;
@@ -221,14 +235,34 @@ const timeZoneUTCOffsetSign = withCode(
221
235
sign ,
222
236
( data , result ) => ( data . offsetSign = result === '-' || result === '\u2212' ? '-' : '+' )
223
237
) ;
224
- const timeZoneUTCOffsetHour = withCode ( hour , ( data , result ) => ( data . offsetHour = + result ) ) ;
238
+ function saveOffsetHour ( data , result ) {
239
+ data . offsetHour = + result ;
240
+ }
241
+ const timeZoneUTCOffsetHour = withCode ( hour , saveOffsetHour ) ;
242
+ const timeZoneUTCOffsetHourNotValidMonth = withCode ( zeroPaddedInclusive ( 13 , 23 , 2 ) , saveOffsetHour ) ;
225
243
const timeZoneUTCOffsetMinute = withCode ( minuteSecond , ( data , result ) => ( data . offsetMinute = + result ) ) ;
226
244
const timeZoneUTCOffsetSecond = withCode ( minuteSecond , ( data , result ) => ( data . offsetSecond = + result ) ) ;
227
245
const timeZoneUTCOffsetFraction = withCode ( fraction , ( data , result ) => {
228
246
result = result . slice ( 1 ) ;
229
247
const fraction = result . padEnd ( 9 , '0' ) ;
230
248
data . offsetFraction = + fraction ;
231
249
} ) ;
250
+ function saveOffset ( data ) {
251
+ if ( data . offsetSign !== undefined && data . offsetHour !== undefined ) {
252
+ const h = `${ data . offsetHour } ` . padStart ( 2 , '0' ) ;
253
+ const m = `${ data . offsetMinute || 0 } ` . padStart ( 2 , '0' ) ;
254
+ const s = `${ data . offsetSecond || 0 } ` . padStart ( 2 , '0' ) ;
255
+ data . offset = `${ data . offsetSign } ${ h } :${ m } ` ;
256
+ if ( data . offsetFraction ) {
257
+ let fraction = `${ data . offsetFraction } ` . padStart ( 9 , '0' ) ;
258
+ while ( fraction . endsWith ( '0' ) ) fraction = fraction . slice ( 0 , - 1 ) ;
259
+ data . offset += `:${ s } .${ fraction } ` ;
260
+ } else if ( data . offsetSecond ) {
261
+ data . offset += `:${ s } ` ;
262
+ }
263
+ if ( data . offset === '-00:00' ) data . offset = '+00:00' ;
264
+ }
265
+ }
232
266
const timeZoneNumericUTCOffset = withCode (
233
267
seq (
234
268
timeZoneUTCOffsetSign ,
@@ -238,22 +272,25 @@ const timeZoneNumericUTCOffset = withCode(
238
272
seq ( ':' , timeZoneUTCOffsetMinute , [ ':' , timeZoneUTCOffsetSecond , [ timeZoneUTCOffsetFraction ] ] )
239
273
)
240
274
) ,
241
- ( data ) => {
242
- if ( data . offsetSign !== undefined && data . offsetHour !== undefined ) {
243
- const h = `${ data . offsetHour } ` . padStart ( 2 , '0' ) ;
244
- const m = `${ data . offsetMinute || 0 } ` . padStart ( 2 , '0' ) ;
245
- const s = `${ data . offsetSecond || 0 } ` . padStart ( 2 , '0' ) ;
246
- data . offset = `${ data . offsetSign } ${ h } :${ m } ` ;
247
- if ( data . offsetFraction ) {
248
- let fraction = `${ data . offsetFraction } ` . padStart ( 9 , '0' ) ;
249
- while ( fraction . endsWith ( '0' ) ) fraction = fraction . slice ( 0 , - 1 ) ;
250
- data . offset += `:${ s } .${ fraction } ` ;
251
- } else if ( data . offsetSecond ) {
252
- data . offset += `:${ s } ` ;
253
- }
254
- if ( data . offset === '-00:00' ) data . offset = '+00:00' ;
255
- }
256
- }
275
+ saveOffset
276
+ ) ;
277
+ const timeZoneNumericUTCOffsetNotAmbiguous = withCode (
278
+ choice (
279
+ seq ( character ( '+\u2212' ) , timeZoneUTCOffsetHour ) ,
280
+ seq (
281
+ timeZoneUTCOffsetSign ,
282
+ timeZoneUTCOffsetHour ,
283
+ choice (
284
+ seq ( timeZoneUTCOffsetMinute , [ timeZoneUTCOffsetSecond , [ timeZoneUTCOffsetFraction ] ] ) ,
285
+ seq ( ':' , timeZoneUTCOffsetMinute , [ ':' , timeZoneUTCOffsetSecond , [ timeZoneUTCOffsetFraction ] ] )
286
+ )
287
+ )
288
+ ) ,
289
+ saveOffset
290
+ ) ;
291
+ const timeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour = withCode (
292
+ choice ( timeZoneNumericUTCOffsetNotAmbiguous , seq ( '-' , timeZoneUTCOffsetHourNotValidMonth ) ) ,
293
+ saveOffset
257
294
) ;
258
295
const timeZoneUTCOffset = choice ( utcDesignator , timeZoneNumericUTCOffset ) ;
259
296
const timeZoneUTCOffsetName = seq (
@@ -286,14 +323,42 @@ const timeSpec = seq(
286
323
timeHour ,
287
324
choice ( [ ':' , timeMinute , [ ':' , timeSecond , [ timeFraction ] ] ] , seq ( timeMinute , [ timeSecond , [ timeFraction ] ] ) )
288
325
) ;
326
+ const timeSpecWithOptionalTimeZoneNotAmbiguous = choice (
327
+ seq ( timeHour , [ timeZoneNumericUTCOffsetNotAmbiguous ] , [ timeZoneBracketedAnnotation ] ) ,
328
+ seq ( timeHourNotValidMonth , timeZone ) ,
329
+ seq (
330
+ choice (
331
+ seq ( timeHourNotValidMonth , timeMinute ) ,
332
+ seq ( timeHour , timeMinuteNotValidDay ) ,
333
+ seq ( timeHourNot31DayMonth , timeMinute31Only ) ,
334
+ seq ( timeHour2Only , timeMinute30Only )
335
+ ) ,
336
+ [ timeZoneBracketedAnnotation ]
337
+ ) ,
338
+ seq (
339
+ timeHour ,
340
+ timeMinute ,
341
+ choice (
342
+ seq ( timeZoneNumericUTCOffsetNotAmbiguousAllowedNegativeHour , [ timeZoneBracketedAnnotation ] ) ,
343
+ seq ( timeSecondNotValidMonth , [ timeZone ] ) ,
344
+ seq ( timeSecond , timeFraction , [ timeZone ] )
345
+ )
346
+ ) ,
347
+ seq ( timeHour , ':' , timeMinute , [ ':' , timeSecond , [ timeFraction ] ] , [ timeZone ] )
348
+ ) ;
289
349
const timeSpecSeparator = seq ( dateTimeSeparator , timeSpec ) ;
290
350
291
351
const dateSpecMonthDay = seq ( [ '--' ] , dateMonth , [ '-' ] , dateDay ) ;
292
352
const dateSpecYearMonth = seq ( dateYear , [ '-' ] , dateMonth ) ;
293
353
const date = choice ( seq ( dateYear , '-' , dateMonth , '-' , dateDay ) , seq ( dateYear , dateMonth , dateDay ) ) ;
294
354
const dateTime = seq ( date , [ timeSpecSeparator ] , [ timeZone ] ) ;
295
355
const calendarDateTime = seq ( dateTime , [ calendar ] ) ;
296
- const calendarTime = seq ( timeSpec , [ timeZone ] , [ calendar ] ) ;
356
+ const calendarDateTimeTimeRequired = seq ( date , timeSpecSeparator , [ timeZone ] , [ calendar ] ) ;
357
+ const calendarTime = choice (
358
+ seq ( timeDesignator , timeSpec , [ timeZone ] , [ calendar ] ) ,
359
+ seq ( timeSpec , [ timeZone ] , calendar ) ,
360
+ seq ( timeSpecWithOptionalTimeZoneNotAmbiguous )
361
+ ) ;
297
362
298
363
const durationFractionalPart = withCode ( between ( 1 , 9 , digit ( ) ) , ( data , result ) => {
299
364
const fraction = result . padEnd ( 9 , '0' ) ;
@@ -317,7 +382,7 @@ const durationHours = seq(
317
382
hoursDesignator ,
318
383
[ choice ( durationMinutes , durationSeconds ) ]
319
384
) ;
320
- const durationTime = seq ( durationTimeDesignator , choice ( durationHours , durationMinutes , durationSeconds ) ) ;
385
+ const durationTime = seq ( timeDesignator , choice ( durationHours , durationMinutes , durationSeconds ) ) ;
321
386
const durationDays = seq (
322
387
withCode ( oneOrMore ( digit ( ) ) , ( data , result ) => ( data . days = + result * data . factor ) ) ,
323
388
daysDesignator
@@ -354,7 +419,7 @@ const goals = {
354
419
DateTime : calendarDateTime ,
355
420
Duration : duration ,
356
421
MonthDay : choice ( dateSpecMonthDay , calendarDateTime ) ,
357
- Time : choice ( calendarTime , calendarDateTime ) ,
422
+ Time : choice ( calendarTime , calendarDateTimeTimeRequired ) ,
358
423
TimeZone : choice ( temporalTimeZoneIdentifier , seq ( date , [ timeSpecSeparator ] , timeZone , [ calendar ] ) ) ,
359
424
YearMonth : choice ( dateSpecYearMonth , calendarDateTime ) ,
360
425
ZonedDateTime : zonedDateTime
0 commit comments