mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2026-01-18 11:11:10 +00:00
Merge pull request #7894 from drinkcat/jiff-date-ls
date/ls: Switch from chrono to jiff
This commit is contained in:
commit
dfc2e249ef
14 changed files with 399 additions and 325 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# spell-checker:ignore datetime
|
||||
# spell-checker:ignore datetime tzdb zoneinfo
|
||||
[package]
|
||||
name = "uu_date"
|
||||
description = "date ~ (uutils) display or set the current time"
|
||||
|
|
@ -19,9 +19,14 @@ workspace = true
|
|||
path = "src/date.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true, features = ["custom-tz-fmt", "parser"] }
|
||||
chrono = { workspace = true } # TODO: Eventually we'll want to remove this
|
||||
jiff = { workspace = true, features = [
|
||||
"tzdb-bundle-platform",
|
||||
"tzdb-zoneinfo",
|
||||
"tzdb-concatenated",
|
||||
] }
|
||||
uucore = { workspace = true, features = ["parser"] }
|
||||
parse_datetime = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
|
|
|||
|
|
@ -3,19 +3,17 @@
|
|||
// 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 strtime ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
|
||||
|
||||
use chrono::format::{Item, StrftimeItems};
|
||||
use chrono::{DateTime, FixedOffset, Local, Offset, TimeDelta, Utc};
|
||||
#[cfg(windows)]
|
||||
use chrono::{Datelike, Timelike};
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use jiff::fmt::strtime;
|
||||
use jiff::tz::TimeZone;
|
||||
use jiff::{SignedDuration, Timestamp, Zoned};
|
||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
||||
use libc::{CLOCK_REALTIME, clock_settime, timespec};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use uucore::custom_tz_fmt::custom_time_format;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::FromIo;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
|
|
@ -75,7 +73,7 @@ struct Settings {
|
|||
utc: bool,
|
||||
format: Format,
|
||||
date_source: DateSource,
|
||||
set_to: Option<DateTime<FixedOffset>>,
|
||||
set_to: Option<Zoned>,
|
||||
}
|
||||
|
||||
/// Various ways of displaying the date
|
||||
|
|
@ -93,7 +91,7 @@ enum DateSource {
|
|||
Custom(String),
|
||||
File(PathBuf),
|
||||
Stdin,
|
||||
Human(TimeDelta),
|
||||
Human(SignedDuration),
|
||||
}
|
||||
|
||||
enum Iso8601Format {
|
||||
|
|
@ -167,9 +165,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
};
|
||||
|
||||
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
||||
let ref_time = Local::now();
|
||||
if let Ok(new_time) = parse_datetime::parse_datetime_at_date(ref_time, date.as_str()) {
|
||||
let duration = new_time.signed_duration_since(ref_time);
|
||||
if let Ok(duration) = parse_offset(date.as_str()) {
|
||||
DateSource::Human(duration)
|
||||
} else {
|
||||
DateSource::Custom(date.into())
|
||||
|
|
@ -203,39 +199,37 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
|
||||
if let Some(date) = settings.set_to {
|
||||
// All set time functions expect UTC datetimes.
|
||||
let date: DateTime<Utc> = if settings.utc {
|
||||
date.with_timezone(&Utc)
|
||||
let date = if settings.utc {
|
||||
date.with_time_zone(TimeZone::UTC)
|
||||
} else {
|
||||
date.into()
|
||||
date
|
||||
};
|
||||
|
||||
return set_system_datetime(date);
|
||||
} else {
|
||||
// Get the current time, either in the local time zone or UTC.
|
||||
let now: DateTime<FixedOffset> = if settings.utc {
|
||||
let now = Utc::now();
|
||||
now.with_timezone(&now.offset().fix())
|
||||
let now = if settings.utc {
|
||||
Timestamp::now().to_zoned(TimeZone::UTC)
|
||||
} else {
|
||||
let now = Local::now();
|
||||
now.with_timezone(now.offset())
|
||||
Zoned::now()
|
||||
};
|
||||
|
||||
// Iterate over all dates - whether it's a single date or a file.
|
||||
let dates: Box<dyn Iterator<Item = _>> = match settings.date_source {
|
||||
DateSource::Custom(ref input) => {
|
||||
let date = parse_date(input.clone());
|
||||
let date = parse_date(input);
|
||||
let iter = std::iter::once(date);
|
||||
Box::new(iter)
|
||||
}
|
||||
DateSource::Human(relative_time) => {
|
||||
// Double check the result is overflow or not of the current_time + relative_time
|
||||
// it may cause a panic of chrono::datetime::DateTime add
|
||||
match now.checked_add_signed(relative_time) {
|
||||
Some(date) => {
|
||||
match now.checked_add(relative_time) {
|
||||
Ok(date) => {
|
||||
let iter = std::iter::once(Ok(date));
|
||||
Box::new(iter)
|
||||
}
|
||||
None => {
|
||||
Err(_) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date {relative_time}"),
|
||||
|
|
@ -272,23 +266,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
// Format all the dates
|
||||
for date in dates {
|
||||
match date {
|
||||
Ok(date) => {
|
||||
let format_string = custom_time_format(format_string);
|
||||
// Hack to work around panic in chrono,
|
||||
// TODO - remove when a fix for https://github.com/chronotope/chrono/issues/623 is released
|
||||
let format_items = StrftimeItems::new(format_string.as_str());
|
||||
if format_items.clone().any(|i| i == Item::Error) {
|
||||
// TODO: Switch to lenient formatting.
|
||||
Ok(date) => match strtime::format(format_string, &date) {
|
||||
Ok(s) => println!("{s}"),
|
||||
Err(e) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid format {}", format_string.replace("%f", "%N")),
|
||||
format!("invalid format {} ({e})", format_string),
|
||||
));
|
||||
}
|
||||
let formatted = date
|
||||
.format_with_items(format_items)
|
||||
.to_string()
|
||||
.replace("%f", "%N");
|
||||
println!("{formatted}");
|
||||
}
|
||||
},
|
||||
Err((input, _err)) => show!(USimpleError::new(
|
||||
1,
|
||||
format!("invalid date {}", input.quote())
|
||||
|
|
@ -388,13 +375,13 @@ fn make_format_string(settings: &Settings) -> &str {
|
|||
Iso8601Format::Hours => "%FT%H%:z",
|
||||
Iso8601Format::Minutes => "%FT%H:%M%:z",
|
||||
Iso8601Format::Seconds => "%FT%T%:z",
|
||||
Iso8601Format::Ns => "%FT%T,%f%:z",
|
||||
Iso8601Format::Ns => "%FT%T,%N%:z",
|
||||
},
|
||||
Format::Rfc5322 => "%a, %d %h %Y %T %z",
|
||||
Format::Rfc3339(ref fmt) => match *fmt {
|
||||
Rfc3339Format::Date => "%F",
|
||||
Rfc3339Format::Seconds => "%F %T%:z",
|
||||
Rfc3339Format::Ns => "%F %T.%f%:z",
|
||||
Rfc3339Format::Ns => "%F %T.%N%:z",
|
||||
},
|
||||
Format::Custom(ref fmt) => fmt,
|
||||
Format::Default => "%a %b %e %X %Z %Y",
|
||||
|
|
@ -403,19 +390,43 @@ fn make_format_string(settings: &Settings) -> &str {
|
|||
|
||||
/// Parse a `String` into a `DateTime`.
|
||||
/// If it fails, return a tuple of the `String` along with its `ParseError`.
|
||||
// TODO: Convert `parse_datetime` to jiff and remove wrapper from chrono to jiff structures.
|
||||
fn parse_date<S: AsRef<str> + Clone>(
|
||||
s: S,
|
||||
) -> Result<DateTime<FixedOffset>, (String, parse_datetime::ParseDateTimeError)> {
|
||||
parse_datetime::parse_datetime(s.as_ref()).map_err(|e| (s.as_ref().into(), e))
|
||||
) -> Result<Zoned, (String, parse_datetime::ParseDateTimeError)> {
|
||||
match parse_datetime::parse_datetime(s.as_ref()) {
|
||||
Ok(date) => {
|
||||
let timestamp =
|
||||
Timestamp::new(date.timestamp(), date.timestamp_subsec_nanos() as i32).unwrap();
|
||||
Ok(Zoned::new(timestamp, TimeZone::UTC))
|
||||
}
|
||||
Err(e) => Err((s.as_ref().into(), e)),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Convert `parse_datetime` to jiff and remove wrapper from chrono to jiff structures.
|
||||
// Also, consider whether parse_datetime::parse_datetime_at_date can be renamed to something
|
||||
// like parse_datetime::parse_offset, instead of doing some addition/subtraction.
|
||||
fn parse_offset(date: &str) -> Result<SignedDuration, ()> {
|
||||
let ref_time = chrono::Local::now();
|
||||
if let Ok(new_time) = parse_datetime::parse_datetime_at_date(ref_time, date) {
|
||||
let duration = new_time.signed_duration_since(ref_time);
|
||||
Ok(SignedDuration::new(
|
||||
duration.num_seconds(),
|
||||
duration.subsec_nanos(),
|
||||
))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(_date: Zoned) -> UResult<()> {
|
||||
unimplemented!("setting date not implemented (unsupported target)");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(_date: Zoned) -> UResult<()> {
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
"setting the date is not supported by macOS".to_string(),
|
||||
|
|
@ -423,7 +434,7 @@ fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
|||
}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(_date: Zoned) -> UResult<()> {
|
||||
Err(USimpleError::new(
|
||||
1,
|
||||
"setting the date is not supported by Redox".to_string(),
|
||||
|
|
@ -436,10 +447,11 @@ fn set_system_datetime(_date: DateTime<Utc>) -> UResult<()> {
|
|||
/// `<https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html>`
|
||||
/// `<https://linux.die.net/man/3/clock_settime>`
|
||||
/// `<https://www.gnu.org/software/libc/manual/html_node/Time-Types.html>`
|
||||
fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(date: Zoned) -> UResult<()> {
|
||||
let ts = date.timestamp();
|
||||
let timespec = timespec {
|
||||
tv_sec: date.timestamp() as _,
|
||||
tv_nsec: date.timestamp_subsec_nanos() as _,
|
||||
tv_sec: ts.as_second() as _,
|
||||
tv_nsec: ts.subsec_nanosecond() as _,
|
||||
};
|
||||
|
||||
let result = unsafe { clock_settime(CLOCK_REALTIME, ×pec) };
|
||||
|
|
@ -456,7 +468,7 @@ fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
|||
/// See here for more:
|
||||
/// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-setsystemtime
|
||||
/// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
|
||||
fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
||||
fn set_system_datetime(date: Zoned) -> UResult<()> {
|
||||
let system_time = SYSTEMTIME {
|
||||
wYear: date.year() as u16,
|
||||
wMonth: date.month() as u16,
|
||||
|
|
@ -467,7 +479,7 @@ fn set_system_datetime(date: DateTime<Utc>) -> UResult<()> {
|
|||
wMinute: date.minute() as u16,
|
||||
wSecond: date.second() as u16,
|
||||
// TODO: be careful of leap seconds - valid range is [0, 999] - how to handle?
|
||||
wMilliseconds: ((date.nanosecond() / 1_000_000) % 1000) as u16,
|
||||
wMilliseconds: ((date.subsec_nanosecond() / 1_000_000) % 1000) as u16,
|
||||
};
|
||||
|
||||
let result = unsafe { SetSystemTime(&system_time) };
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# spell-checker:ignore tzdb zoneinfo
|
||||
|
||||
[package]
|
||||
name = "uu_ls"
|
||||
description = "ls ~ (uutils) display directory contents"
|
||||
|
|
@ -23,6 +25,11 @@ chrono = { workspace = true }
|
|||
clap = { workspace = true, features = ["env"] }
|
||||
glob = { workspace = true }
|
||||
hostname = { workspace = true }
|
||||
jiff = { workspace = true, features = [
|
||||
"tzdb-bundle-platform",
|
||||
"tzdb-zoneinfo",
|
||||
"tzdb-concatenated",
|
||||
] }
|
||||
lscolors = { workspace = true }
|
||||
number_prefix = { workspace = true }
|
||||
selinux = { workspace = true, optional = true }
|
||||
|
|
@ -30,7 +37,6 @@ terminal_size = { workspace = true }
|
|||
thiserror = { workspace = true }
|
||||
uucore = { workspace = true, features = [
|
||||
"colors",
|
||||
"custom-tz-fmt",
|
||||
"entries",
|
||||
"format",
|
||||
"fs",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly nohash
|
||||
// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly nohash strtime
|
||||
|
||||
use std::iter;
|
||||
#[cfg(windows)]
|
||||
|
|
@ -16,24 +16,24 @@ use std::{
|
|||
fs::{self, DirEntry, FileType, Metadata, ReadDir},
|
||||
io::{BufWriter, ErrorKind, Stdout, Write, stdout},
|
||||
path::{Path, PathBuf},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
os::unix::fs::{FileTypeExt, MetadataExt},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{collections::HashSet, io::IsTerminal};
|
||||
|
||||
use ansi_width::ansi_width;
|
||||
use chrono::format::{Item, StrftimeItems};
|
||||
use chrono::{DateTime, Local, TimeDelta};
|
||||
use clap::{
|
||||
Arg, ArgAction, Command,
|
||||
builder::{NonEmptyStringValueParser, PossibleValue, ValueParser},
|
||||
};
|
||||
use glob::{MatchOptions, Pattern};
|
||||
use jiff::fmt::StdIoWrite;
|
||||
use jiff::fmt::strtime::BrokenDownTime;
|
||||
use jiff::{Timestamp, Zoned};
|
||||
use lscolors::LsColors;
|
||||
use term_grid::{DEFAULT_SEPARATOR_SIZE, Direction, Filling, Grid, GridOptions, SPACES_IN_TAB};
|
||||
use thiserror::Error;
|
||||
|
|
@ -59,7 +59,6 @@ use uucore::libc::{dev_t, major, minor};
|
|||
use uucore::line_ending::LineEnding;
|
||||
use uucore::quoting_style::{self, QuotingStyle, escape_name};
|
||||
use uucore::{
|
||||
custom_tz_fmt,
|
||||
display::Quotable,
|
||||
error::{UError, UResult, set_exit_code},
|
||||
format_usage,
|
||||
|
|
@ -274,64 +273,37 @@ enum TimeStyle {
|
|||
Format(String),
|
||||
}
|
||||
|
||||
/// A struct/impl used to format a file DateTime, precomputing the format for performance reasons.
|
||||
struct TimeStyler {
|
||||
// default format, always specified.
|
||||
default: Vec<Item<'static>>,
|
||||
// format for a recent time, only specified it is is different from the default
|
||||
recent: Option<Vec<Item<'static>>>,
|
||||
// If `recent` is set, cache the threshold time when we switch from recent to default format.
|
||||
recent_time_threshold: Option<DateTime<Local>>,
|
||||
/// Whether the given date is considered recent (i.e., in the last 6 months).
|
||||
fn is_recent(time: Timestamp, state: &mut ListState) -> bool {
|
||||
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||
time > state.recent_time_threshold
|
||||
}
|
||||
|
||||
impl TimeStyler {
|
||||
/// Create a TimeStyler based on a TimeStyle specification.
|
||||
fn new(style: &TimeStyle) -> TimeStyler {
|
||||
let default: Vec<Item<'static>> = match style {
|
||||
TimeStyle::FullIso => StrftimeItems::new("%Y-%m-%d %H:%M:%S.%f %z").parse(),
|
||||
TimeStyle::LongIso => StrftimeItems::new("%Y-%m-%d %H:%M").parse(),
|
||||
TimeStyle::Iso => StrftimeItems::new("%Y-%m-%d ").parse(),
|
||||
// In this version of chrono translating can be done
|
||||
// The function is chrono::datetime::DateTime::format_localized
|
||||
// However it's currently still hard to get the current pure-rust-locale
|
||||
// So it's not yet implemented
|
||||
TimeStyle::Locale => StrftimeItems::new("%b %e %Y").parse(),
|
||||
TimeStyle::Format(fmt) => {
|
||||
StrftimeItems::new_lenient(custom_tz_fmt::custom_time_format(fmt).as_str())
|
||||
.parse_to_owned()
|
||||
}
|
||||
}
|
||||
.unwrap();
|
||||
let recent = match style {
|
||||
TimeStyle::Iso => Some(StrftimeItems::new("%m-%d %H:%M")),
|
||||
// See comment above about locale
|
||||
TimeStyle::Locale => Some(StrftimeItems::new("%b %e %H:%M")),
|
||||
_ => None,
|
||||
}
|
||||
.map(|x| x.collect());
|
||||
let recent_time_threshold = if recent.is_some() {
|
||||
// According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||
Some(Local::now() - TimeDelta::try_seconds(31_556_952 / 2).unwrap())
|
||||
} else {
|
||||
None
|
||||
impl TimeStyle {
|
||||
/// Format the given time according to this time format style.
|
||||
fn format(
|
||||
&self,
|
||||
date: Zoned,
|
||||
out: &mut Vec<u8>,
|
||||
state: &mut ListState,
|
||||
) -> Result<(), jiff::Error> {
|
||||
let recent = is_recent(date.timestamp(), state);
|
||||
let tm = BrokenDownTime::from(&date);
|
||||
let mut out = StdIoWrite(out);
|
||||
let config = jiff::fmt::strtime::Config::new().lenient(true);
|
||||
|
||||
let fmt = match (self, recent) {
|
||||
(Self::FullIso, _) => "%Y-%m-%d %H:%M:%S.%f %z",
|
||||
(Self::LongIso, _) => "%Y-%m-%d %H:%M",
|
||||
(Self::Iso, true) => "%m-%d %H:%M",
|
||||
(Self::Iso, false) => "%Y-%m-%d ",
|
||||
// TODO: Using correct locale string is not implemented.
|
||||
(Self::Locale, true) => "%b %e %H:%M",
|
||||
(Self::Locale, false) => "%b %e %Y",
|
||||
(Self::Format(fmt), _) => fmt,
|
||||
};
|
||||
|
||||
TimeStyler {
|
||||
default,
|
||||
recent,
|
||||
recent_time_threshold,
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a DateTime, using `recent` format if available, and the DateTime
|
||||
/// is recent enough.
|
||||
fn format(&self, time: DateTime<Local>) -> String {
|
||||
if self.recent.is_none() || time <= self.recent_time_threshold.unwrap() {
|
||||
time.format_with_items(self.default.iter())
|
||||
} else {
|
||||
time.format_with_items(self.recent.as_ref().unwrap().iter())
|
||||
}
|
||||
.to_string()
|
||||
tm.format_with_config(&config, fmt, &mut out)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2093,8 +2065,7 @@ struct ListState<'a> {
|
|||
uid_cache: HashMap<u32, String>,
|
||||
#[cfg(unix)]
|
||||
gid_cache: HashMap<u32, String>,
|
||||
|
||||
time_styler: TimeStyler,
|
||||
recent_time_threshold: Timestamp,
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
|
|
@ -2111,7 +2082,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
|
|||
uid_cache: HashMap::new(),
|
||||
#[cfg(unix)]
|
||||
gid_cache: HashMap::new(),
|
||||
time_styler: TimeStyler::new(&config.time_style),
|
||||
recent_time_threshold: Timestamp::now() - Duration::new(31_556_952 / 2, 0),
|
||||
};
|
||||
|
||||
for loc in locs {
|
||||
|
|
@ -2907,7 +2878,7 @@ fn display_item_long(
|
|||
};
|
||||
|
||||
output_display.extend(b" ");
|
||||
output_display.extend(display_date(md, config, state).as_bytes());
|
||||
display_date(md, config, state, &mut output_display)?;
|
||||
output_display.extend(b" ");
|
||||
|
||||
let item_name = display_item_name(
|
||||
|
|
@ -3106,15 +3077,27 @@ fn get_system_time(md: &Metadata, config: &Config) -> Option<SystemTime> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_time(md: &Metadata, config: &Config) -> Option<DateTime<Local>> {
|
||||
fn get_time(md: &Metadata, config: &Config) -> Option<Zoned> {
|
||||
let time = get_system_time(md, config)?;
|
||||
Some(time.into())
|
||||
time.try_into().ok()
|
||||
}
|
||||
|
||||
fn display_date(metadata: &Metadata, config: &Config, state: &mut ListState) -> String {
|
||||
fn display_date(
|
||||
metadata: &Metadata,
|
||||
config: &Config,
|
||||
state: &mut ListState,
|
||||
out: &mut Vec<u8>,
|
||||
) -> UResult<()> {
|
||||
match get_time(metadata, config) {
|
||||
Some(time) => state.time_styler.format(time),
|
||||
None => "???".into(),
|
||||
// TODO: Some fancier error conversion might be nice.
|
||||
Some(time) => config
|
||||
.time_style
|
||||
.format(time, out, state)
|
||||
.map_err(|x| USimpleError::new(1, x.to_string())),
|
||||
None => {
|
||||
out.extend(b"???");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ path = "src/lib/lib.rs"
|
|||
|
||||
[dependencies]
|
||||
chrono = { workspace = true, optional = true }
|
||||
chrono-tz = { workspace = true, optional = true }
|
||||
clap = { workspace = true }
|
||||
uucore_procs = { workspace = true }
|
||||
number_prefix = { workspace = true }
|
||||
|
|
@ -29,7 +28,6 @@ dns-lookup = { workspace = true, optional = true }
|
|||
dunce = { version = "1.0.4", optional = true }
|
||||
wild = "2.2.1"
|
||||
glob = { workspace = true, optional = true }
|
||||
iana-time-zone = { workspace = true, optional = true }
|
||||
itertools = { workspace = true, optional = true }
|
||||
time = { workspace = true, optional = true, features = [
|
||||
"formatting",
|
||||
|
|
@ -138,6 +136,5 @@ utf8 = []
|
|||
utmpx = ["time", "time/macros", "libc", "dns-lookup"]
|
||||
version-cmp = []
|
||||
wide = []
|
||||
custom-tz-fmt = ["chrono", "chrono-tz", "iana-time-zone"]
|
||||
tty = []
|
||||
uptime = ["chrono", "libc", "windows-sys", "utmpx", "utmp-classic"]
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ pub mod buf_copy;
|
|||
pub mod checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub mod colors;
|
||||
#[cfg(feature = "custom-tz-fmt")]
|
||||
pub mod custom_tz_fmt;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub mod encoding;
|
||||
#[cfg(feature = "extendedbigdecimal")]
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
use chrono_tz::{OffsetName, Tz};
|
||||
use iana_time_zone::get_timezone;
|
||||
|
||||
/// Get the alphabetic abbreviation of the current timezone.
|
||||
///
|
||||
/// For example, "UTC" or "CET" or "PDT"
|
||||
fn timezone_abbreviation() -> String {
|
||||
let tz = match std::env::var("TZ") {
|
||||
// TODO Support other time zones...
|
||||
Ok(s) if s == "UTC0" || s.is_empty() => Tz::Etc__UTC,
|
||||
_ => match get_timezone() {
|
||||
Ok(tz_str) => tz_str.parse().unwrap(),
|
||||
Err(_) => Tz::Etc__UTC,
|
||||
},
|
||||
};
|
||||
|
||||
let offset = tz.offset_from_utc_date(&Utc::now().date_naive());
|
||||
offset.abbreviation().unwrap_or("UTC").to_string()
|
||||
}
|
||||
|
||||
/// Adapt the given string to be accepted by the chrono library crate.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// fmt: the format of the string
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// A string that can be used as parameter of the chrono functions that use formats
|
||||
pub fn custom_time_format(fmt: &str) -> String {
|
||||
// TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970
|
||||
// chrono crashes on %#z, but it's the same as %z anyway.
|
||||
// GNU `date` uses `%N` for nano seconds, however the `chrono` crate uses `%f`.
|
||||
fmt.replace("%#z", "%z")
|
||||
.replace("%N", "%f")
|
||||
.replace("%Z", timezone_abbreviation().as_ref())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{custom_time_format, timezone_abbreviation};
|
||||
|
||||
#[test]
|
||||
fn test_custom_time_format() {
|
||||
assert_eq!(custom_time_format("%Y-%m-%d %H-%M-%S"), "%Y-%m-%d %H-%M-%S");
|
||||
assert_eq!(custom_time_format("%d-%m-%Y %H-%M-%S"), "%d-%m-%Y %H-%M-%S");
|
||||
assert_eq!(custom_time_format("%Y-%m-%d %H-%M-%S"), "%Y-%m-%d %H-%M-%S");
|
||||
assert_eq!(
|
||||
custom_time_format("%Y-%m-%d %H-%M-%S.%N"),
|
||||
"%Y-%m-%d %H-%M-%S.%f"
|
||||
);
|
||||
assert_eq!(custom_time_format("%Z"), timezone_abbreviation());
|
||||
}
|
||||
}
|
||||
|
|
@ -41,8 +41,6 @@ pub use crate::features::buf_copy;
|
|||
pub use crate::features::checksum;
|
||||
#[cfg(feature = "colors")]
|
||||
pub use crate::features::colors;
|
||||
#[cfg(feature = "custom-tz-fmt")]
|
||||
pub use crate::features::custom_tz_fmt;
|
||||
#[cfg(feature = "encoding")]
|
||||
pub use crate::features::encoding;
|
||||
#[cfg(feature = "extendedbigdecimal")]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue