@@ -108,6 +108,30 @@ impl TryFrom<(&str, u32, u32)> for Date {
108
108
}
109
109
}
110
110
111
+ impl TryFrom < ( u32 , u32 ) > for Date {
112
+ type Error = & ' static str ;
113
+
114
+ /// Create a `Date` from a tuple of `(month, day)`.
115
+ fn try_from ( ( month, day) : ( u32 , u32 ) ) -> Result < Self , Self :: Error > {
116
+ if !( 1 ..=12 ) . contains ( & month) {
117
+ return Err ( "month must be between 1 and 12" ) ;
118
+ }
119
+
120
+ if !( 1 ..=31 ) . contains ( & day)
121
+ || ( month == 2 && day > 29 )
122
+ || ( ( month == 4 || month == 6 || month == 9 || month == 11 ) && day > 30 )
123
+ {
124
+ return Err ( "day is not valid for the given month" ) ;
125
+ }
126
+
127
+ Ok ( Date {
128
+ day,
129
+ month,
130
+ year : None ,
131
+ } )
132
+ }
133
+ }
134
+
111
135
pub fn parse ( input : & mut & str ) -> ModalResult < Date > {
112
136
alt ( ( iso1, iso2, us, literal1, literal2) ) . parse_next ( input)
113
137
}
@@ -154,15 +178,46 @@ pub fn iso2(input: &mut &str) -> ModalResult<Date> {
154
178
. map_err ( |e| ErrMode :: Cut ( ctx_err ( e) ) )
155
179
}
156
180
157
- /// Parse `MM/DD/YYYY`, `MM/DD/YY ` or `MM/DD`
181
+ /// Parse `[year]/[month]/[day]` or `[month]/[day]/[year] ` or `[month]/[day]`.
158
182
fn us ( input : & mut & str ) -> ModalResult < Date > {
159
- seq ! ( Date {
160
- month: month,
161
- _: s( '/' ) ,
162
- day: day,
163
- year: opt( preceded( s( '/' ) , year) ) ,
164
- } )
165
- . parse_next ( input)
183
+ let ( s1, _, n, s2) = (
184
+ s ( take_while ( 1 .., AsChar :: is_dec_digit) ) ,
185
+ s ( '/' ) ,
186
+ s ( dec_uint) ,
187
+ opt ( preceded ( s ( '/' ) , s ( take_while ( 1 .., AsChar :: is_dec_digit) ) ) ) ,
188
+ )
189
+ . parse_next ( input) ?;
190
+
191
+ match s2 {
192
+ Some ( s2) if s1. len ( ) >= 4 => {
193
+ // [year]/[month]/[day]
194
+ //
195
+ // GNU quirk: interpret as [year]/[month]/[day] if the first part is at
196
+ // least 4 characters long.
197
+ let day = s2
198
+ . parse :: < u32 > ( )
199
+ . map_err ( |_| ErrMode :: Cut ( ctx_err ( "day must be a valid number" ) ) ) ?;
200
+ ( s1, n, day)
201
+ . try_into ( )
202
+ . map_err ( |e| ErrMode :: Cut ( ctx_err ( e) ) )
203
+ }
204
+ Some ( s2) => {
205
+ // [month]/[day]/[year]
206
+ let month = s1
207
+ . parse :: < u32 > ( )
208
+ . map_err ( |_| ErrMode :: Cut ( ctx_err ( "month must be a valid number" ) ) ) ?;
209
+ ( s2, month, n)
210
+ . try_into ( )
211
+ . map_err ( |e| ErrMode :: Cut ( ctx_err ( e) ) )
212
+ }
213
+ None => {
214
+ // [month]/[day]
215
+ let month = s1
216
+ . parse :: < u32 > ( )
217
+ . map_err ( |_| ErrMode :: Cut ( ctx_err ( "month must be a valid number" ) ) ) ?;
218
+ ( month, n) . try_into ( ) . map_err ( |e| ErrMode :: Cut ( ctx_err ( e) ) )
219
+ }
220
+ }
166
221
}
167
222
168
223
/// Parse `14 November 2022`, `14 Nov 2022`, "14nov2022", "14-nov-2022", "14-nov2022", "14nov-2022"
@@ -213,17 +268,6 @@ pub fn year(input: &mut &str) -> ModalResult<u32> {
213
268
. parse_next ( input)
214
269
}
215
270
216
- fn month ( input : & mut & str ) -> ModalResult < u32 > {
217
- s ( dec_uint)
218
- . try_map ( |x| {
219
- ( 1 ..=12 )
220
- . contains ( & x)
221
- . then_some ( x)
222
- . ok_or ( ParseDateTimeError :: InvalidInput )
223
- } )
224
- . parse_next ( input)
225
- }
226
-
227
271
fn day ( input : & mut & str ) -> ModalResult < u32 > {
228
272
s ( dec_uint)
229
273
. try_map ( |x| {
@@ -280,11 +324,11 @@ mod tests {
280
324
fn iso1 ( ) {
281
325
let reference = Date {
282
326
year : Some ( 1 ) ,
283
- month : 1 ,
284
- day : 1 ,
327
+ month : 2 ,
328
+ day : 3 ,
285
329
} ;
286
330
287
- for mut s in [ "1-1-1 " , "1 - 1 - 1 " , "1-01-01 " , "1-001-001 " , "001-01-01 " ] {
331
+ for mut s in [ "1-2-3 " , "1 - 2 - 3 " , "1-02-03 " , "1-002-003 " , "001-02-03 " ] {
288
332
let old_s = s. to_owned ( ) ;
289
333
assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
290
334
}
@@ -293,11 +337,11 @@ mod tests {
293
337
// smaller, 2000 is added to it.
294
338
let reference = Date {
295
339
year : Some ( 2001 ) ,
296
- month : 1 ,
297
- day : 1 ,
340
+ month : 2 ,
341
+ day : 3 ,
298
342
} ;
299
343
300
- for mut s in [ "01-1-1 " , "01-01-01 " ] {
344
+ for mut s in [ "01-2-3 " , "01-02-03 " ] {
301
345
let old_s = s. to_owned ( ) ;
302
346
assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
303
347
}
@@ -306,11 +350,11 @@ mod tests {
306
350
// than 100, 1900 is added to it.
307
351
let reference = Date {
308
352
year : Some ( 1970 ) ,
309
- month : 1 ,
310
- day : 1 ,
353
+ month : 2 ,
354
+ day : 3 ,
311
355
} ;
312
356
313
- for mut s in [ "70-1-1 " , "70-01-01 " ] {
357
+ for mut s in [ "70-2-3 " , "70-02-03 " ] {
314
358
let old_s = s. to_owned ( ) ;
315
359
assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
316
360
}
@@ -325,11 +369,11 @@ mod tests {
325
369
fn iso2 ( ) {
326
370
let reference = Date {
327
371
year : Some ( 1 ) ,
328
- month : 1 ,
329
- day : 1 ,
372
+ month : 2 ,
373
+ day : 3 ,
330
374
} ;
331
375
332
- for mut s in [ "10101 " , "0010101 " , "00010101 " , "000010101 " ] {
376
+ for mut s in [ "10203 " , "0010203 " , "00010203 " , "000010203 " ] {
333
377
let old_s = s. to_owned ( ) ;
334
378
assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
335
379
}
@@ -338,23 +382,23 @@ mod tests {
338
382
// smaller, 2000 is added to it.
339
383
let reference = Date {
340
384
year : Some ( 2001 ) ,
341
- month : 1 ,
342
- day : 1 ,
385
+ month : 2 ,
386
+ day : 3 ,
343
387
} ;
344
388
345
- let mut s = "010101 " ;
389
+ let mut s = "010203 " ;
346
390
let old_s = s. to_owned ( ) ;
347
391
assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
348
392
349
393
// GNU quirk: when year string is 2 characters long and year is less
350
394
// than 100, 1900 is added to it.
351
395
let reference = Date {
352
396
year : Some ( 1970 ) ,
353
- month : 1 ,
354
- day : 1 ,
397
+ month : 2 ,
398
+ day : 3 ,
355
399
} ;
356
400
357
- let mut s = "700101 " ;
401
+ let mut s = "700203 " ;
358
402
let old_s = s. to_owned ( ) ;
359
403
assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
360
404
@@ -364,6 +408,60 @@ mod tests {
364
408
}
365
409
}
366
410
411
+ #[ test]
412
+ fn us ( ) {
413
+ let reference = Date {
414
+ year : Some ( 1 ) ,
415
+ month : 2 ,
416
+ day : 3 ,
417
+ } ;
418
+
419
+ for mut s in [ "2/3/1" , "2 / 3 / 1" , "02/03/ 001" , "0001/2/3" ] {
420
+ let old_s = s. to_owned ( ) ;
421
+ assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
422
+ }
423
+
424
+ let reference = Date {
425
+ year : None ,
426
+ month : 2 ,
427
+ day : 3 ,
428
+ } ;
429
+
430
+ for mut s in [ "2/3" , "2 / 3" ] {
431
+ let old_s = s. to_owned ( ) ;
432
+ assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
433
+ }
434
+
435
+ // GNU quirk: when year string is 2 characters long and year is 68 or
436
+ // smaller, 2000 is added to it.
437
+ let reference = Date {
438
+ year : Some ( 2001 ) ,
439
+ month : 2 ,
440
+ day : 3 ,
441
+ } ;
442
+
443
+ let mut s = "2/3/01" ;
444
+ let old_s = s. to_owned ( ) ;
445
+ assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
446
+
447
+ // GNU quirk: when year string is 2 characters long and year is less
448
+ // than 100, 1900 is added to it.
449
+ let reference = Date {
450
+ year : Some ( 1970 ) ,
451
+ month : 2 ,
452
+ day : 3 ,
453
+ } ;
454
+
455
+ let mut s = "2/3/70" ;
456
+ let old_s = s. to_owned ( ) ;
457
+ assert_eq ! ( parse( & mut s) . unwrap( ) , reference, "Format string: {old_s}" ) ;
458
+
459
+ for mut s in [ "00/01/01" , "13/01/01" , "01/32/01" , "02/30/01" , "04/31/01" ] {
460
+ let old_s = s. to_owned ( ) ;
461
+ assert ! ( parse( & mut s) . is_err( ) , "Format string: {old_s}" ) ;
462
+ }
463
+ }
464
+
367
465
#[ test]
368
466
fn with_year ( ) {
369
467
let reference = Date {
0 commit comments