1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

date -d supports - 1 year ago, 2 second, etc

This commit is contained in:
Sylvestre Ledru 2023-04-26 00:11:11 +02:00
parent 6f0e4ece9d
commit 825d240ef0
4 changed files with 47 additions and 3 deletions

2
Cargo.lock generated
View file

@ -2538,7 +2538,9 @@ version = "0.0.18"
dependencies = [
"chrono",
"clap",
"humantime_to_duration",
"libc",
"time",
"uucore",
"windows-sys 0.45.0",
]

View file

@ -1,3 +1,4 @@
# spell-checker:ignore humantime
[package]
name = "uu_date"
version = "0.0.18"
@ -16,8 +17,11 @@ path = "src/date.rs"
[dependencies]
chrono = { workspace=true }
#/ TODO: check if we can avoid chrono+time
time = { workspace=true }
clap = { workspace=true }
uucore = { workspace=true }
humantime_to_duration = { workspace=true }
[target.'cfg(unix)'.dependencies]
libc = { workspace=true }

View file

@ -6,10 +6,10 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes humantime
use chrono::format::{Item, StrftimeItems};
use chrono::{DateTime, FixedOffset, Local, Offset, Utc};
use chrono::{DateTime, Duration as ChronoDuration, FixedOffset, Local, Offset, Utc};
#[cfg(windows)]
use chrono::{Datelike, Timelike};
use clap::{crate_version, Arg, ArgAction, Command};
@ -18,6 +18,7 @@ use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
use time::Duration;
use uucore::display::Quotable;
#[cfg(not(any(target_os = "redox")))]
use uucore::error::FromIo;
@ -96,6 +97,7 @@ enum DateSource {
Now,
Custom(String),
File(PathBuf),
Human(Duration),
}
enum Iso8601Format {
@ -168,7 +170,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
DateSource::Custom(date.into())
if let Ok(duration) = humantime_to_duration::from_str(date.as_str()) {
DateSource::Human(duration)
} else {
DateSource::Custom(date.into())
}
} else if let Some(file) = matches.get_one::<String>(OPT_FILE) {
DateSource::File(file.into())
} else {
@ -219,6 +225,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let iter = std::iter::once(date);
Box::new(iter)
}
DateSource::Human(ref input) => {
// Get the current DateTime<FixedOffset> and convert the input time::Duration to chrono::Duration
// for things like "1 year ago"
let current_time = DateTime::<FixedOffset>::from(Local::now());
let input_chrono = ChronoDuration::seconds(input.as_seconds_f32() as i64)
+ ChronoDuration::nanoseconds(input.subsec_nanoseconds() as i64);
let iter = std::iter::once(Ok(current_time + input_chrono));
Box::new(iter)
}
DateSource::File(ref path) => {
if path.is_dir() {
return Err(USimpleError::new(

View file

@ -334,6 +334,29 @@ fn test_invalid_format_string() {
assert!(result.stderr_str().starts_with("date: invalid format "));
}
#[test]
fn test_date_string_human() {
let date_formats = vec![
"1 year ago",
"1 year",
"2 months ago",
"15 days ago",
"1 week ago",
"5 hours ago",
"30 minutes ago",
"10 seconds",
];
let re = Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\n$").unwrap();
for date_format in date_formats {
new_ucmd!()
.arg("-d")
.arg(date_format)
.arg("+%Y-%m-%d %S:%M")
.succeeds()
.stdout_matches(&re);
}
}
#[test]
fn test_invalid_date_string() {
new_ucmd!()