Skip to content

Commit

Permalink
stat: use chrono instead of time in fsext (uutils#5934)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
tertsdiepraam and cakebaker authored Feb 19, 2024
1 parent 33785c9 commit 177ac7e
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 66 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/uu/stat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
24 changes: 19 additions & 5 deletions src/uu/stat/src/stat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion src/uucore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
64 changes: 4 additions & 60 deletions src/uucore/src/lib/features/fsext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()))
}
}

Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 177ac7e

Please sign in to comment.