From 177ac7ea287394ad876f5848ff670ea1da413124 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 19 Feb 2024 10:21:26 +0100 Subject: [PATCH] `stat`: use chrono instead of time in fsext (#5934) * stat: use chrono instead of time in fsext This removes the dependency of `fsext` on `time` and it cleans up the code. * stat: use chrono instead of time in fsext This removes the dependency of `fsext` on `time` and it cleans up the code. * stat: fix two errors from clippy & spell-checker * stat: move fn to fix clippy error * stat: print - if birth time unknown * uucore/fsext: fix "unused import" error on Windows --------- Co-authored-by: Daniel Hofstetter --- Cargo.lock | 1 + src/uu/stat/Cargo.toml | 1 + src/uu/stat/src/stat.rs | 24 ++++++++--- src/uucore/Cargo.toml | 2 +- src/uucore/src/lib/features/fsext.rs | 64 ++-------------------------- 5 files changed, 26 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a2125f36..3f656b7d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2909,6 +2909,7 @@ dependencies = [ name = "uu_stat" version = "0.0.24" dependencies = [ + "chrono", "clap", "uucore", ] diff --git a/src/uu/stat/Cargo.toml b/src/uu/stat/Cargo.toml index dd28fc5f3..e19d707f7 100644 --- a/src/uu/stat/Cargo.toml +++ b/src/uu/stat/Cargo.toml @@ -17,6 +17,7 @@ path = "src/stat.rs" [dependencies] clap = { workspace = true } uucore = { workspace = true, features = ["entries", "libc", "fs", "fsext"] } +chrono = { workspace = true } [[bin]] name = "stat" diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index 1c5bd8919..f9fe5ee59 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -2,19 +2,19 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +// spell-checker:ignore datetime use clap::builder::ValueParser; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; use uucore::fs::display_permissions; -use uucore::fsext::{ - pretty_filetype, pretty_fstype, pretty_time, read_fs_list, statfs, BirthTime, FsMeta, -}; +use uucore::fsext::{pretty_filetype, pretty_fstype, read_fs_list, statfs, BirthTime, FsMeta}; use uucore::libc::mode_t; use uucore::{ entries, format_usage, help_about, help_section, help_usage, show_error, show_warning, }; +use chrono::{DateTime, Local}; use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; use std::borrow::Cow; use std::convert::AsRef; @@ -809,10 +809,14 @@ impl Stater { } // time of file birth, human-readable; - if unknown - 'w' => OutputType::Str(meta.pretty_birth()), + 'w' => OutputType::Str( + meta.birth() + .map(|(sec, nsec)| pretty_time(sec as i64, nsec as i64)) + .unwrap_or(String::from("-")), + ), // time of file birth, seconds since Epoch; 0 if unknown - 'W' => OutputType::Unsigned(meta.birth()), + 'W' => OutputType::Unsigned(meta.birth().unwrap_or_default().0), // time of last access, human-readable 'x' => OutputType::Str(pretty_time( @@ -950,6 +954,16 @@ pub fn uu_app() -> Command { ) } +const PRETTY_DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S.%f %z"; + +fn pretty_time(sec: i64, nsec: i64) -> String { + // Return the date in UTC + let tm = chrono::DateTime::from_timestamp(sec, nsec as u32).unwrap_or_default(); + let tm: DateTime = tm.into(); + + tm.format(PRETTY_DATETIME_FORMAT).to_string() +} + #[cfg(test)] mod tests { use super::{group_num, Flags, ScanUtil, Stater, Token}; diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index b0cb8cd4d..347ef9178 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -77,7 +77,7 @@ colors = [] encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["dunce", "libc", "winapi-util", "windows-sys"] -fsext = ["libc", "time", "windows-sys"] +fsext = ["libc", "windows-sys"] fsxattr = ["xattr"] lines = [] format = ["itertools", "quoting-style"] diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index e6b12b29a..89ffab7f4 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -7,9 +7,6 @@ // spell-checker:ignore DATETIME getmntinfo subsecond (arch) bitrig ; (fs) cifs smbfs -use time::macros::format_description; -use time::UtcOffset; - #[cfg(any(target_os = "linux", target_os = "android"))] const LINUX_MTAB: &str = "/etc/mtab"; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -66,6 +63,7 @@ use libc::{ mode_t, strerror, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, }; use std::borrow::Cow; +#[cfg(not(windows))] use std::convert::From; #[cfg(unix)] use std::ffi::CStr; @@ -115,26 +113,16 @@ pub use libc::statfs as statfs_fn; pub use libc::statvfs as statfs_fn; pub trait BirthTime { - fn pretty_birth(&self) -> String; - fn birth(&self) -> u64; + fn birth(&self) -> Option<(u64, u32)>; } use std::fs::Metadata; impl BirthTime for Metadata { - fn pretty_birth(&self) -> String { + fn birth(&self) -> Option<(u64, u32)> { self.created() .ok() .and_then(|t| t.duration_since(UNIX_EPOCH).ok()) - .map(|e| pretty_time(e.as_secs() as i64, i64::from(e.subsec_nanos()))) - .unwrap_or_else(|| "-".to_owned()) - } - - fn birth(&self) -> u64 { - self.created() - .ok() - .and_then(|t| t.duration_since(UNIX_EPOCH).ok()) - .map(|e| e.as_secs()) - .unwrap_or_default() + .map(|e| (e.as_secs(), e.subsec_nanos())) } } @@ -869,50 +857,6 @@ where } } -// match strftime "%Y-%m-%d %H:%M:%S.%f %z" -const PRETTY_DATETIME_FORMAT: &[time::format_description::FormatItem] = format_description!( - "\ -[year]-[month]-[day padding:zero] \ -[hour]:[minute]:[second].[subsecond digits:9] \ -[offset_hour sign:mandatory][offset_minute]" -); - -pub fn pretty_time(sec: i64, nsec: i64) -> String { - // sec == seconds since UNIX_EPOCH - // nsec == nanoseconds since (UNIX_EPOCH + sec) - let ts_nanos: i128 = (sec * 1_000_000_000 + nsec).into(); - - // Return the date in UTC - let tm = match time::OffsetDateTime::from_unix_timestamp_nanos(ts_nanos) { - Ok(tm) => tm, - Err(e) => { - panic!("error: {e}"); - } - }; - - // Get the offset to convert to local time - // Because of DST (daylight saving), we get the local time back when - // the date was set - let local_offset = match UtcOffset::local_offset_at(tm) { - Ok(lo) => lo, - Err(_) if cfg!(target_os = "redox") => UtcOffset::UTC, - Err(e) => { - panic!("error: {e}"); - } - }; - - // Include the conversion to local time - let res = tm - .to_offset(local_offset) - .format(&PRETTY_DATETIME_FORMAT) - .unwrap(); - if res.ends_with(" -0000") { - res.replace(" -0000", " +0000") - } else { - res - } -} - #[cfg(unix)] pub fn pretty_filetype<'a>(mode: mode_t, size: u64) -> &'a str { match mode & S_IFMT {