touch: add support of -d '1970-01-01 18:43:33.023456789'

This commit is contained in:
Sylvestre Ledru 2022-04-29 10:32:28 +02:00
parent 31c28eeaa9
commit 9d81d6fef2
3 changed files with 84 additions and 28 deletions

View file

@ -64,7 +64,7 @@ highlight = "all"
# spell-checker: disable
skip = [
# getrandom
{ name = "wasi", version="0.10.2+wasi-snapshot-preview1" },
{ name = "wasi", version="0.10.0+wasi-snapshot-preview1" },
# blake2d_simd
{ name = "arrayvec", version = "=0.7.2" },
# flimit/unix_socket

View file

@ -6,7 +6,7 @@
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm clapv PWSTR lpszfilepath hresult mktime YYYYMMDDHHMM YYMMDDHHMM DATETIME subsecond
// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm clapv PWSTR lpszfilepath hresult mktime YYYYMMDDHHMM YYMMDDHHMM DATETIME YYYYMMDDHHMMS
pub extern crate filetime;
#[macro_use]
@ -255,6 +255,41 @@ const POSIX_LOCALE_FORMAT: &[time::format_description::FormatItem] = format_desc
const ISO_8601_FORMAT: &[time::format_description::FormatItem] =
format_description!("[year]-[month]-[day]");
// "%Y%m%d%H%M.%S" 15 chars
const YYYYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full][month repr:numerical padding:zero]\
[day][hour][minute].[second]"
);
// "%Y-%m-%d %H:%M:%S.%SS" 12 chars
const YYYYMMDDHHMMSS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full]-[month repr:numerical padding:zero]-\
[day] [hour]:[minute]:[second].[subsecond]"
);
// "%Y-%m-%d %H:%M:%S" 12 chars
const YYYYMMDDHHMMS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full]-[month repr:numerical padding:zero]-\
[day] [hour]:[minute]:[second]"
);
// "%Y%m%d%H%M" 12 chars
const YYYYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full][month repr:numerical padding:zero]\
[day][hour][minute]"
);
// "%y%m%d%H%M.%S" 13 chars
const YYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:last_two padding:none][month][day]\
[hour][minute].[second]"
);
// "%y%m%d%H%M" 10 chars
const YYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:last_two padding:none][month padding:zero][day padding:zero]\
[hour repr:24 padding:zero][minute padding:zero]"
);
fn parse_date(s: &str) -> UResult<FileTime> {
// This isn't actually compatible with GNU touch, but there doesn't seem to
// be any simple specification for what format this parameter allows and I'm
@ -269,8 +304,17 @@ fn parse_date(s: &str) -> UResult<FileTime> {
// which is equivalent to the POSIX locale: %a %b %e %H:%M:%S %Y
// Tue Dec 3 ...
// ("%c", POSIX_LOCALE_FORMAT),
if let Ok(parsed) = time::PrimitiveDateTime::parse(s, &POSIX_LOCALE_FORMAT) {
return Ok(local_dt_to_filetime(to_local(parsed)));
//
// But also support other format found in the GNU tests like
// in tests/misc/stat-nanoseconds.sh
for fmt in [
POSIX_LOCALE_FORMAT,
YYYYMMDDHHMMS_FORMAT,
YYYYMMDDHHMMSS_FORMAT,
] {
if let Ok(parsed) = time::PrimitiveDateTime::parse(s, &fmt) {
return Ok(local_dt_to_filetime(to_local(parsed)));
}
}
// "Equivalent to %Y-%m-%d (the ISO 8601 date format). (C99)"
@ -294,30 +338,6 @@ fn parse_date(s: &str) -> UResult<FileTime> {
Err(USimpleError::new(1, format!("Unable to parse date: {}", s)))
}
// "%Y%m%d%H%M.%S" 15 chars
const YYYYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full][month repr:numerical padding:zero]\
[day][hour][minute].[second]"
);
// "%Y%m%d%H%M" 12 chars
const YYYYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:full][month repr:numerical padding:zero]\
[day][hour][minute]"
);
// "%y%m%d%H%M.%S" 13 chars
const YYMMDDHHMM_DOT_SS_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:last_two padding:none][month][day]\
[hour][minute].[second]"
);
// "%y%m%d%H%M" 10 chars
const YYMMDDHHMM_FORMAT: &[time::format_description::FormatItem] = format_description!(
"[year repr:last_two padding:none][month padding:zero][day padding:zero]\
[hour repr:24 padding:zero][minute padding:zero]"
);
fn parse_timestamp(s: &str) -> UResult<FileTime> {
// TODO: handle error
let now = time::OffsetDateTime::now_utc();

View file

@ -427,6 +427,42 @@ fn test_touch_set_date3() {
assert_eq!(mtime, expected);
}
#[test]
fn test_touch_set_date4() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_date";
ucmd.args(&["-d", "1970-01-01 18:43:33", file])
.succeeds()
.no_stderr();
assert!(at.file_exists(file));
let expected = FileTime::from_unix_time(60213, 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_date5() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_touch_set_date";
ucmd.args(&["-d", "1970-01-01 18:43:33.023456789", file])
.succeeds()
.no_stderr();
assert!(at.file_exists(file));
let expected = FileTime::from_unix_time(60213, 023456789);
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_date_wrong_format() {
let (_at, mut ucmd) = at_and_ucmd!();