mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-02 14:07:46 +00:00
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:
parent
33785c93a3
commit
177ac7ea28
5 changed files with 26 additions and 66 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2909,6 +2909,7 @@ dependencies = [
|
||||||
name = "uu_stat"
|
name = "uu_stat"
|
||||||
version = "0.0.24"
|
version = "0.0.24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,6 +17,7 @@ path = "src/stat.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["entries", "libc", "fs", "fsext"] }
|
uucore = { workspace = true, features = ["entries", "libc", "fs", "fsext"] }
|
||||||
|
chrono = { workspace = true }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "stat"
|
name = "stat"
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore datetime
|
||||||
|
|
||||||
use clap::builder::ValueParser;
|
use clap::builder::ValueParser;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
use uucore::fs::display_permissions;
|
use uucore::fs::display_permissions;
|
||||||
use uucore::fsext::{
|
use uucore::fsext::{pretty_filetype, pretty_fstype, read_fs_list, statfs, BirthTime, FsMeta};
|
||||||
pretty_filetype, pretty_fstype, pretty_time, read_fs_list, statfs, BirthTime, FsMeta,
|
|
||||||
};
|
|
||||||
use uucore::libc::mode_t;
|
use uucore::libc::mode_t;
|
||||||
use uucore::{
|
use uucore::{
|
||||||
entries, format_usage, help_about, help_section, help_usage, show_error, show_warning,
|
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 clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
|
@ -809,10 +809,14 @@ impl Stater {
|
||||||
}
|
}
|
||||||
|
|
||||||
// time of file birth, human-readable; - if unknown
|
// 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
|
// 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
|
// time of last access, human-readable
|
||||||
'x' => OutputType::Str(pretty_time(
|
'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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{group_num, Flags, ScanUtil, Stater, Token};
|
use super::{group_num, Flags, ScanUtil, Stater, Token};
|
||||||
|
|
|
@ -77,7 +77,7 @@ colors = []
|
||||||
encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"]
|
encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"]
|
||||||
entries = ["libc"]
|
entries = ["libc"]
|
||||||
fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
||||||
fsext = ["libc", "time", "windows-sys"]
|
fsext = ["libc", "windows-sys"]
|
||||||
fsxattr = ["xattr"]
|
fsxattr = ["xattr"]
|
||||||
lines = []
|
lines = []
|
||||||
format = ["itertools", "quoting-style"]
|
format = ["itertools", "quoting-style"]
|
||||||
|
|
|
@ -7,9 +7,6 @@
|
||||||
|
|
||||||
// spell-checker:ignore DATETIME getmntinfo subsecond (arch) bitrig ; (fs) cifs smbfs
|
// 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"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
const LINUX_MTAB: &str = "/etc/mtab";
|
const LINUX_MTAB: &str = "/etc/mtab";
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[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,
|
mode_t, strerror, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
#[cfg(not(windows))]
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
@ -115,26 +113,16 @@ pub use libc::statfs as statfs_fn;
|
||||||
pub use libc::statvfs as statfs_fn;
|
pub use libc::statvfs as statfs_fn;
|
||||||
|
|
||||||
pub trait BirthTime {
|
pub trait BirthTime {
|
||||||
fn pretty_birth(&self) -> String;
|
fn birth(&self) -> Option<(u64, u32)>;
|
||||||
fn birth(&self) -> u64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::fs::Metadata;
|
use std::fs::Metadata;
|
||||||
impl BirthTime for Metadata {
|
impl BirthTime for Metadata {
|
||||||
fn pretty_birth(&self) -> String {
|
fn birth(&self) -> Option<(u64, u32)> {
|
||||||
self.created()
|
self.created()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|t| t.duration_since(UNIX_EPOCH).ok())
|
.and_then(|t| t.duration_since(UNIX_EPOCH).ok())
|
||||||
.map(|e| pretty_time(e.as_secs() as i64, i64::from(e.subsec_nanos())))
|
.map(|e| (e.as_secs(), 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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)]
|
#[cfg(unix)]
|
||||||
pub fn pretty_filetype<'a>(mode: mode_t, size: u64) -> &'a str {
|
pub fn pretty_filetype<'a>(mode: mode_t, size: u64) -> &'a str {
|
||||||
match mode & S_IFMT {
|
match mode & S_IFMT {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue