diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 9a4c0fe5db0..331867b5f13 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -17,6 +17,7 @@ use filetime::*; use std::fs::{self, File}; use std::path::{Path, PathBuf}; use time::macros::{format_description, time}; +use time::Duration; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::format_usage; @@ -345,16 +346,25 @@ fn parse_timestamp(s: &str) -> UResult { format = YYYYMMDDHHMM_DOT_SS_FORMAT; ts = "20".to_owned() + &ts; } - if (format == YYYYMMDDHHMM_DOT_SS_FORMAT || format == YYMMDDHHMM_DOT_SS_FORMAT) + + let leap_sec = if (format == YYYYMMDDHHMM_DOT_SS_FORMAT || format == YYMMDDHHMM_DOT_SS_FORMAT) && ts.ends_with(".60") { // Work around to disable leap seconds + // Used in gnu/tests/touch/60-seconds ts = ts.replace(".60", ".59"); - } + true + } else { + false + }; + let tm = time::PrimitiveDateTime::parse(&ts, &format) .map_err(|_| USimpleError::new(1, format!("invalid date ts format {}", ts.quote())))?; - - let local = to_local(tm); + let mut local = to_local(tm); + if leap_sec { + // We are dealing with a leap second, add it + local = local.saturating_add(Duration::SECOND); + } let ft = local_dt_to_filetime(local); // // We have to check that ft is valid time. Due to daylight saving diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 2c538d8b404..d23fb090e1c 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -603,3 +603,21 @@ fn test_no_dereference_no_file() { .stderr_contains("setting times of 'not-a-file-1': No such file or directory") .stderr_contains("setting times of 'not-a-file-2': No such file or directory"); } + +#[test] +fn test_touch_leap_second() { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_touch_leap_sec"; + + ucmd.args(&["-t", "197001010000.60", file]) + .succeeds() + .no_stderr(); + + assert!(at.file_exists(file)); + + let epoch = str_to_filetime("%Y%m%d%H%M", "197001010000"); + let (atime, mtime) = get_file_times(&at, file); + assert_eq!(atime, mtime); + assert_eq!(atime.unix_seconds() - epoch.unix_seconds(), 60); + assert_eq!(mtime.unix_seconds() - epoch.unix_seconds(), 60); +}