2828
2929use winnow:: {
3030 ascii:: { alpha1, multispace1} ,
31- combinator:: { alt, eof, opt, preceded, terminated, trace } ,
31+ combinator:: { alt, eof, opt, preceded, terminated} ,
3232 error:: ErrMode ,
3333 stream:: AsChar ,
3434 token:: take_while,
3535 ModalResult , Parser ,
3636} ;
3737
38- use super :: primitive:: { ctx_err, dec_uint, s} ;
38+ use super :: {
39+ primitive:: { ctx_err, dec_uint, s} ,
40+ year:: { year_from_str, year_str} ,
41+ } ;
3942
4043#[ derive( PartialEq , Eq , Clone , Debug , Default ) ]
4144pub struct Date {
@@ -50,40 +53,11 @@ impl TryFrom<(&str, u32, u32)> for Date {
5053 /// Create a `Date` from a tuple of `(year, month, day)`.
5154 ///
5255 /// Note: The `year` is represented as a `&str` to handle a specific GNU
53- /// compatibility quirk. According to the GNU documentation: "if the year is
54- /// 68 or smaller, then 2000 is added to it; otherwise, if year is less than
55- /// 100, then 1900 is added to it." This adjustment only applies to
56- /// two-digit year strings. For example, `"00"` is interpreted as `2000`,
57- /// whereas `"0"`, `"000"`, or `"0000"` are interpreted as `0`.
56+ /// compatibility quirk. See the comment in [`year`](super::year) for more
57+ /// details.
5858 fn try_from ( value : ( & str , u32 , u32 ) ) -> Result < Self , Self :: Error > {
5959 let ( year_str, month, day) = value;
60-
61- let mut year = year_str
62- . parse :: < u32 > ( )
63- . map_err ( |_| "year must be a valid number" ) ?;
64-
65- // If year is 68 or smaller, then 2000 is added to it; otherwise, if year
66- // is less than 100, then 1900 is added to it.
67- //
68- // GNU quirk: this only applies to two-digit years. For example,
69- // "98-01-01" will be parsed as "1998-01-01", while "098-01-01" will be
70- // parsed as "0098-01-01".
71- if year_str. len ( ) == 2 {
72- if year <= 68 {
73- year += 2000
74- } else {
75- year += 1900
76- }
77- }
78-
79- // 2147485547 is the maximum value accepted by GNU, but chrono only
80- // behaves like GNU for years in the range: [0, 9999], so we keep in the
81- // range [0, 9999].
82- //
83- // See discussion in https://github.com/uutils/parse_datetime/issues/160.
84- if year > 9999 {
85- return Err ( "year must be no greater than 9999" ) ;
86- }
60+ let year = year_from_str ( year_str) ?;
8761
8862 if !( 1 ..=12 ) . contains ( & month) {
8963 return Err ( "month must be between 1 and 12" ) ;
@@ -138,15 +112,8 @@ pub fn parse(input: &mut &str) -> ModalResult<Date> {
138112///
139113/// This is also used by [`combined`](super::combined).
140114pub fn iso1 ( input : & mut & str ) -> ModalResult < Date > {
141- let ( year, _, month, _, day) = (
142- // `year` must be a `&str`, see comment in `TryFrom` impl for `Date`.
143- s ( take_while ( 1 .., AsChar :: is_dec_digit) ) ,
144- s ( '-' ) ,
145- s ( dec_uint) ,
146- s ( '-' ) ,
147- s ( dec_uint) ,
148- )
149- . parse_next ( input) ?;
115+ let ( year, _, month, _, day) =
116+ ( year_str, s ( '-' ) , s ( dec_uint) , s ( '-' ) , s ( dec_uint) ) . parse_next ( input) ?;
150117
151118 ( year, month, day)
152119 . try_into ( )
@@ -160,7 +127,6 @@ pub fn iso2(input: &mut &str) -> ModalResult<Date> {
160127 let date_str = take_while ( 5 .., AsChar :: is_dec_digit) . parse_next ( input) ?;
161128 let len = date_str. len ( ) ;
162129
163- // `year` must be a `&str`, see comment in `TryFrom` impl for `Date`.
164130 let year = & date_str[ ..len - 4 ] ;
165131
166132 let month = date_str[ len - 4 ..len - 2 ]
@@ -226,7 +192,7 @@ fn literal1(input: &mut &str) -> ModalResult<Date> {
226192 opt ( s ( '-' ) ) ,
227193 s ( literal_month) ,
228194 opt ( terminated (
229- preceded ( opt ( s ( '-' ) ) , s ( take_while ( 1 .. , AsChar :: is_dec_digit ) ) ) ,
195+ preceded ( opt ( s ( '-' ) ) , year_str ) ,
230196 // The year must be followed by a space or end of input.
231197 alt ( ( multispace1, eof) ) ,
232198 ) ) ,
@@ -254,7 +220,7 @@ fn literal2(input: &mut &str) -> ModalResult<Date> {
254220 // space between the comma and the year. This is probably to
255221 // distinguish with floats.
256222 opt ( s ( terminated ( ',' , multispace1) ) ) ,
257- s ( take_while ( 1 .. , AsChar :: is_dec_digit ) ) ,
223+ year_str ,
258224 ) ,
259225 // The year must be followed by a space or end of input.
260226 alt ( ( multispace1, eof) ) ,
@@ -272,31 +238,6 @@ fn literal2(input: &mut &str) -> ModalResult<Date> {
272238 }
273239}
274240
275- pub fn year ( input : & mut & str ) -> ModalResult < u32 > {
276- // 2147485547 is the maximum value accepted
277- // by GNU, but chrono only behaves like GNU
278- // for years in the range: [0, 9999], so we
279- // keep in the range [0, 9999]
280- trace (
281- "year" ,
282- s (
283- take_while ( 1 ..=4 , AsChar :: is_dec_digit) . map ( |number_str : & str | {
284- let year = number_str. parse :: < u32 > ( ) . unwrap ( ) ;
285- if number_str. len ( ) == 2 {
286- if year <= 68 {
287- year + 2000
288- } else {
289- year + 1900
290- }
291- } else {
292- year
293- }
294- } ) ,
295- ) ,
296- )
297- . parse_next ( input)
298- }
299-
300241/// Parse the name of a month (case-insensitive)
301242fn literal_month ( input : & mut & str ) -> ModalResult < u32 > {
302243 s ( alpha1)
@@ -639,28 +580,4 @@ mod tests {
639580 assert_eq ! ( parse( & mut s) . unwrap( ) , reference) ;
640581 }
641582 }
642-
643- #[ test]
644- fn test_year ( ) {
645- use super :: year;
646-
647- // the minimun input length is 2
648- // assert!(year(&mut "0").is_err());
649- // -> GNU accepts year 0
650- // test $(date -d '1-1-1' '+%Y') -eq '0001'
651-
652- // test $(date -d '68-1-1' '+%Y') -eq '2068'
653- // 2-characters are converted to 19XX/20XX
654- assert_eq ! ( year( & mut "10" ) . unwrap( ) , 2010u32 ) ;
655- assert_eq ! ( year( & mut "68" ) . unwrap( ) , 2068u32 ) ;
656- assert_eq ! ( year( & mut "69" ) . unwrap( ) , 1969u32 ) ;
657- assert_eq ! ( year( & mut "99" ) . unwrap( ) , 1999u32 ) ;
658- // 3,4-characters are converted verbatim
659- assert_eq ! ( year( & mut "468" ) . unwrap( ) , 468u32 ) ;
660- assert_eq ! ( year( & mut "469" ) . unwrap( ) , 469u32 ) ;
661- assert_eq ! ( year( & mut "1568" ) . unwrap( ) , 1568u32 ) ;
662- assert_eq ! ( year( & mut "1569" ) . unwrap( ) , 1569u32 ) ;
663- // consumes at most 4 characters from the input
664- //assert_eq!(year(&mut "1234567").unwrap(), 1234u32);
665- }
666583}
0 commit comments