@@ -343,20 +343,25 @@ extension DateComponents {
343
343
// https://www.rfc-editor.org/rfc/rfc9110.html#http.date
344
344
// <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
345
345
346
+ // Produce an error message to throw
347
+ func error( _ extendedDescription: String ? = nil ) -> CocoaError {
348
+ parseError ( view, exampleFormattedString: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: extendedDescription)
349
+ }
350
+
346
351
var it = view. makeIterator ( )
347
352
var dc = DateComponents ( )
348
353
349
354
// Despite the spec, we allow the weekday name to be optional.
350
355
guard let maybeWeekday1 = it. peek ( ) else {
351
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) )
356
+ throw error ( )
352
357
}
353
358
354
359
if isASCIIDigit ( maybeWeekday1) {
355
360
// This is the first digit of the day. Weekday is not present.
356
361
} else {
357
362
// Anything else must be a day-name (Mon, Tue, ... Sun)
358
363
guard let weekday1 = it. next ( ) , let weekday2 = it. next ( ) , let weekday3 = it. next ( ) else {
359
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) )
364
+ throw error ( )
360
365
}
361
366
362
367
dc. weekday = switch ( weekday1, weekday2, weekday3) {
@@ -375,20 +380,30 @@ extension DateComponents {
375
380
case ( UInt8 ( ascii: " S " ) , UInt8 ( ascii: " a " ) , UInt8 ( ascii: " t " ) ) :
376
381
7
377
382
default :
378
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) , extendedDescription : " Malformed weekday name " )
383
+ throw error ( " Malformed weekday name " )
379
384
}
380
385
381
386
// Move past , and space to weekday
382
- try it. expectCharacter ( UInt8 ( ascii: " , " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Missing , after weekday " )
383
- try it. expectCharacter ( UInt8 ( ascii: " " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Missing space after weekday " )
387
+ guard it. matchByte ( UInt8 ( ascii: " , " ) ) else {
388
+ throw error ( " Missing , after weekday " )
389
+ }
390
+ guard it. matchByte ( UInt8 ( ascii: " " ) ) else {
391
+ throw error ( " Missing space after weekday " )
392
+ }
384
393
}
385
394
386
- dc. day = try it. digits ( minDigits: 2 , maxDigits: 2 , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Missing or malformed day " )
387
- try it. expectCharacter ( UInt8 ( ascii: " " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
395
+ guard let day = it. parseNumber ( minDigits: 2 , maxDigits: 2 ) else {
396
+ throw error ( " Missing or malformed day " )
397
+ }
398
+ dc. day = day
399
+
400
+ guard it. matchByte ( UInt8 ( ascii: " " ) ) else {
401
+ throw error ( )
402
+ }
388
403
389
404
// month-name (Jan, Feb, ... Dec)
390
405
guard let month1 = it. next ( ) , let month2 = it. next ( ) , let month3 = it. next ( ) else {
391
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) , extendedDescription : " Missing month " )
406
+ throw error ( " Missing month " )
392
407
}
393
408
394
409
dc. month = switch ( month1, month2, month3) {
@@ -417,45 +432,68 @@ extension DateComponents {
417
432
case ( UInt8 ( ascii: " D " ) , UInt8 ( ascii: " e " ) , UInt8 ( ascii: " c " ) ) :
418
433
12
419
434
default :
420
- throw parseError ( view, exampleFormattedString: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Month \( String ( describing: dc. month) ) is out of bounds " )
435
+ throw error ( " Month \( String ( describing: dc. month) ) is out of bounds " )
436
+ }
437
+
438
+ guard it. matchByte ( UInt8 ( ascii: " " ) ) else {
439
+ throw error ( )
421
440
}
422
441
423
- try it. expectCharacter ( UInt8 ( ascii: " " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
442
+ guard let year = it. parseNumber ( minDigits: 4 , maxDigits: 4 ) else {
443
+ throw error ( )
444
+ }
445
+ dc. year = year
424
446
425
- dc. year = try it. digits ( minDigits: 4 , maxDigits: 4 , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
426
- try it. expectCharacter ( UInt8 ( ascii: " " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
447
+ guard it. matchByte ( UInt8 ( ascii: " " ) ) else {
448
+ throw error ( )
449
+ }
427
450
428
- let hour = try it. digits ( minDigits: 2 , maxDigits: 2 , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
451
+ guard let hour = it. parseNumber ( minDigits: 2 , maxDigits: 2 ) else {
452
+ throw error ( )
453
+ }
429
454
if hour < 0 || hour > 23 {
430
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) , extendedDescription : " Hour \( hour) is out of bounds " )
455
+ throw error ( " Hour \( hour) is out of bounds " )
431
456
}
432
457
dc. hour = hour
433
458
434
- try it. expectCharacter ( UInt8 ( ascii: " : " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
435
- let minute = try it. digits ( minDigits: 2 , maxDigits: 2 , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
459
+ guard it. matchByte ( UInt8 ( ascii: " : " ) ) else {
460
+ throw error ( )
461
+ }
462
+ guard let minute = it. parseNumber ( minDigits: 2 , maxDigits: 2 ) else {
463
+ throw error ( )
464
+ }
436
465
if minute < 0 || minute > 59 {
437
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) , extendedDescription : " Minute \( minute) is out of bounds " )
466
+ throw error ( " Minute \( minute) is out of bounds " )
438
467
}
439
468
dc. minute = minute
440
469
441
- try it. expectCharacter ( UInt8 ( ascii: " : " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
442
- let second = try it. digits ( minDigits: 2 , maxDigits: 2 , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
470
+ guard it. matchByte ( UInt8 ( ascii: " : " ) ) else {
471
+ throw error ( )
472
+ }
473
+ guard let second = it. parseNumber ( minDigits: 2 , maxDigits: 2 ) else {
474
+ throw error ( )
475
+ }
443
476
// second '60' is supported in the spec for leap seconds, but Foundation does not support leap seconds. 60 is adjusted to 59.
444
477
if second < 0 || second > 60 {
445
- throw parseError ( view , exampleFormattedString : Date . HTTPFormatStyle ( ) . format ( Date . now ) , extendedDescription : " Second \( second) is out of bounds " )
478
+ throw error ( " Second \( second) is out of bounds " )
446
479
}
447
480
// Foundation does not support leap seconds. We convert 60 seconds into 59 seconds.
448
481
if second == 60 {
449
482
dc. second = 59
450
483
} else {
451
484
dc. second = second
452
485
}
453
- try it. expectCharacter ( UInt8 ( ascii: " " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) )
486
+ guard it. matchByte ( UInt8 ( ascii: " " ) ) else {
487
+ throw error ( )
488
+ }
454
489
455
490
// "GMT"
456
- try it. expectCharacter ( UInt8 ( ascii: " G " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Missing GMT time zone " )
457
- try it. expectCharacter ( UInt8 ( ascii: " M " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Missing GMT time zone " )
458
- try it. expectCharacter ( UInt8 ( ascii: " T " ) , input: view, onFailure: Date . HTTPFormatStyle ( ) . format ( Date . now) , extendedDescription: " Missing GMT time zone " )
491
+ guard it. matchByte ( UInt8 ( ascii: " G " ) ) ,
492
+ it. matchByte ( UInt8 ( ascii: " M " ) ) ,
493
+ it. matchByte ( UInt8 ( ascii: " T " ) )
494
+ else {
495
+ throw error ( " Missing GMT time zone " )
496
+ }
459
497
460
498
// Time zone is always GMT, calendar is always Gregorian
461
499
dc. timeZone = . gmt
0 commit comments