diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 047313e64..eb6154f2b 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -640,6 +640,30 @@ fn parse_date(ref_time: DateTime, s: &str) -> Result UResult { + let first_two_digits = s[..2] + .parse::() + .map_err(|_| USimpleError::new(1, format!("invalid date ts format {}", s.quote())))?; + Ok(format!( + "{}{s}", + if first_two_digits > 68 { 19 } else { 20 } + )) +} + +/// Parses a timestamp string into a FileTime. +/// +/// This function attempts to parse a string into a FileTime +/// As expected by gnu touch -t : `[[cc]yy]mmddhhmm[.ss]` +/// +/// Note that If the year is specified with only two digits, +/// then cc is 20 for years in the range 0 … 68, and 19 for years in 69 … 99. +/// in order to be compatible with GNU `touch`. fn parse_timestamp(s: &str) -> UResult { use format::*; @@ -648,9 +672,9 @@ fn parse_timestamp(s: &str) -> UResult { let (format, ts) = match s.chars().count() { 15 => (YYYYMMDDHHMM_DOT_SS, s.to_owned()), 12 => (YYYYMMDDHHMM, s.to_owned()), - // If we don't add "20", we have insufficient information to parse - 13 => (YYYYMMDDHHMM_DOT_SS, format!("20{s}")), - 10 => (YYYYMMDDHHMM, format!("20{s}")), + // If we don't add "19" or "20", we have insufficient information to parse + 13 => (YYYYMMDDHHMM_DOT_SS, prepend_century(s)?), + 10 => (YYYYMMDDHHMM, prepend_century(s)?), 11 => (YYYYMMDDHHMM_DOT_SS, format!("{}{}", current_year(), s)), 8 => (YYYYMMDDHHMM, format!("{}{}", current_year(), s)), _ => { diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 91298ff9e..98004bb71 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -118,6 +118,45 @@ fn test_touch_set_mdhms_time() { assert_eq!(mtime.unix_seconds() - start_of_year.unix_seconds(), 45296); } +#[test] +fn test_touch_2_digit_years_68() { + // 68 and before are 20xx + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_touch_set_two_digit_68_time"; + + ucmd.args(&["-t", "6801010000", file]) + .succeeds() + .no_output(); + + assert!(at.file_exists(file)); + + // January 1, 2068, 00:00:00 + let expected = FileTime::from_unix_time(3_092_601_600, 0); + let (atime, mtime) = get_file_times(&at, file); + assert_eq!(atime, mtime); + assert_eq!(atime, expected); + assert_eq!(mtime, expected); +} + +#[test] +fn test_touch_2_digit_years_69() { + // 69 and after are 19xx + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_touch_set_two_digit_69_time"; + + ucmd.args(&["-t", "6901010000", file]) + .succeeds() + .no_output(); + + assert!(at.file_exists(file)); + // January 1, 1969, 00:00:00 + let expected = FileTime::from_unix_time(-31_536_000, 0); + let (atime, mtime) = get_file_times(&at, file); + assert_eq!(atime, mtime); + assert_eq!(atime, expected); + assert_eq!(mtime, expected); +} + #[test] fn test_touch_set_ymdhm_time() { let (at, mut ucmd) = at_and_ucmd!();