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 <daniel.hofstetter@42dh.com>
This commit is contained in:
Terts Diepraam 2024-02-19 10:21:26 +01:00 committed by GitHub
parent 33785c93a3
commit 177ac7ea28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 26 additions and 66 deletions

1
Cargo.lock generated
View file

@ -2909,6 +2909,7 @@ dependencies = [
name = "uu_stat"
version = "0.0.24"
dependencies = [
"chrono",
"clap",
"uucore",
]

View file

@ -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"

View file

@ -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<Local> = tm.into();
tm.format(PRETTY_DATETIME_FORMAT).to_string()
}
#[cfg(test)]
mod tests {
use super::{group_num, Flags, ScanUtil, Stater, Token};

View file

@ -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"]

View file

@ -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 {