Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions src/uu/date/src/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ enum DateSource {
Now,
Custom(String),
File(PathBuf),
Reference(PathBuf),
Stdin,
Human(TimeDelta),
}
Expand Down Expand Up @@ -178,11 +179,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
"-" => DateSource::Stdin,
_ => DateSource::File(file.into()),
}
} else if let Some(file) = matches.get_one::<String>(OPT_REFERENCE) {
DateSource::Reference(file.into())
} else {
DateSource::Now
};

let set_to = match matches.get_one::<String>(OPT_SET).map(parse_date) {
let set_option = matches.get_one::<String>(OPT_SET);

// Before parsing an eventual OPT_SET, check if it can't be present.
if !matches!(date_source, DateSource::Now) && set_option.is_some() {
return Err(USimpleError::new(
1,
"the options to print and set the time may not be used together",
));
}

let set_to = match set_option.map(parse_date) {
None => None,
Some(Err((input, _err))) => {
return Err(USimpleError::new(
Expand Down Expand Up @@ -260,6 +273,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let iter = lines.map_while(Result::ok).map(parse_date);
Box::new(iter)
}
DateSource::Reference(ref path) => {
let modified_time: std::time::SystemTime = std::fs::metadata(path)?.modified()?;
let chrono_time: DateTime<Local> = modified_time.into();
Box::new(std::iter::once(Ok(chrono_time.into())))
}
DateSource::Now => {
let iter = std::iter::once(Ok(now));
Box::new(iter)
Expand Down Expand Up @@ -318,15 +336,21 @@ pub fn uu_app() -> Command {
.short('d')
.long(OPT_DATE)
.value_name("STRING")
.help("display time described by STRING, not 'now'"),
.action(ArgAction::Set)
.overrides_with(OPT_DATE)
.help("display time described by STRING, not 'now'")
.conflicts_with_all([OPT_FILE, OPT_REFERENCE]),
)
.arg(
Arg::new(OPT_FILE)
.short('f')
.long(OPT_FILE)
.value_name("DATEFILE")
.value_hint(clap::ValueHint::FilePath)
.help("like --date; once for each line of DATEFILE"),
.action(ArgAction::Set)
.overrides_with(OPT_FILE) // several -f can be passed, but only the last is kept.
.help("like --date; once for each line of DATEFILE")
.conflicts_with_all([OPT_REFERENCE]),
)
.arg(
Arg::new(OPT_ISO_8601)
Expand Down Expand Up @@ -366,6 +390,8 @@ pub fn uu_app() -> Command {
.long(OPT_REFERENCE)
.value_name("FILE")
.value_hint(clap::ValueHint::AnyPath)
.action(ArgAction::Set)
.overrides_with(OPT_REFERENCE)
.help("display the last modification time of FILE"),
)
.arg(
Expand Down
77 changes: 77 additions & 0 deletions tests/by-util/test_date.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::SystemTime;

// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
Expand Down Expand Up @@ -478,3 +480,78 @@ fn test_date_from_stdin() {
Sat Apr 15 18:30:00 2023\n",
);
}

#[test]
fn test_date_exclusive_date_sources() {
new_ucmd!()
.arg("-f")
.arg("foo.txt")
.arg("-d")
.arg("1111-11-11")
.fails()
.stderr_matches(&Regex::new("error: the argument .* cannot be used with .*").unwrap());

new_ucmd!()
.arg("-r")
.arg("foo.txt")
.arg("-d")
.arg("1111-11-11")
.fails()
.stderr_matches(&Regex::new("error: the argument .* cannot be used with .*").unwrap());

new_ucmd!()
.arg("-r")
.arg("foo.txt")
.arg("-f")
.arg("bar.txt")
.fails()
.stderr_matches(&Regex::new("error: the argument .* cannot be used with .*").unwrap());
}

#[test]
fn test_date_successive_file() {
const FILE: &str = "file-with-dates";
let (at, mut ucmd) = at_and_ucmd!();

at.write(
FILE,
"2023-03-27 08:30:00\n\
2023-04-01 12:00:00\n\
2023-04-15 18:30:00",
);
ucmd.arg("-f")
.arg("not-existent-file")
.arg("-f")
.arg("file-with-dates")
.succeeds();
}

#[test]
fn test_date_reference() {
let (at, mut ucmd) = at_and_ucmd!();

at.touch_and_set_modified("ref-file", SystemTime::UNIX_EPOCH);

ucmd.arg("-r")
.arg("ref-file")
.succeeds()
.stdout_is("Thu Jan 1 00:00:00 1970\n");
}

#[test]
fn test_date_successive_reference() {
const FILE: &str = "file-with-dates";
let (at, mut ucmd) = at_and_ucmd!();

at.write(
FILE,
"2023-03-27 08:30:00\n\
2023-04-01 12:00:00\n\
2023-04-15 18:30:00",
);
ucmd.arg("-r")
.arg("not-existent-file")
.arg("-r")
.arg("file-with-dates")
.succeeds();
}
9 changes: 8 additions & 1 deletion tests/common/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use std::process::{Child, Command, ExitStatus, Output, Stdio};
use std::rc::Rc;
use std::sync::mpsc::{self, RecvTimeoutError};
use std::thread::{sleep, JoinHandle};
use std::time::{Duration, Instant};
use std::time::{Duration, Instant, SystemTime};
use std::{env, hint, mem, thread};
use tempfile::{Builder, TempDir};

Expand Down Expand Up @@ -974,6 +974,13 @@ impl AtPath {
File::create(self.plus(file)).unwrap();
}

pub fn touch_and_set_modified<P: AsRef<Path>>(&self, file: P, modified: SystemTime) {
let file = file.as_ref();
log_info("touch", self.plus_as_string(file));
let f = File::create(self.plus(file)).unwrap();
f.set_modified(modified).unwrap();
}

#[cfg(not(windows))]
pub fn mkfifo(&self, fifo: &str) {
let full_path = self.plus_as_string(fifo);
Expand Down