du/ls: Move common time formats to constants in uucore/time

Along the way:
 - Fix the full-iso format, that was supposed to use %N, not %f
   (%f padded with chrono, but not with jiff, and %N is more correct
   and what GNU says in their manual)
 - The Hashmap thing in parse_time_style was too smart, to a point
   that it became too unflexible (it would have been even worse
   when we added locale support).

I was hoping the share more of the code, but that seems difficult.
This commit is contained in:
Nicolas Boichat
2025-07-30 17:27:22 +08:00
parent 1272bfc222
commit b99615c1e3
3 changed files with 29 additions and 24 deletions
+6 -6
View File
@@ -28,7 +28,7 @@ use uucore::translate;
use uucore::parser::parse_glob;
use uucore::parser::parse_size::{ParseSizeError, parse_size_u64};
use uucore::parser::shortcut_value_parser::ShortcutValueParser;
use uucore::time::{FormatSystemTimeFallback, format_system_time};
use uucore::time::{FormatSystemTimeFallback, format, format_system_time};
use uucore::{format_usage, show, show_error, show_warning};
#[cfg(windows)]
use windows_sys::Win32::Foundation::HANDLE;
@@ -668,7 +668,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let time_format = if time.is_some() {
parse_time_style(matches.get_one::<String>("time-style").map(|s| s.as_str()))?.to_string()
} else {
"%Y-%m-%d %H:%M".to_string()
format::LONG_ISO.to_string()
};
let stat_printer = StatPrinter {
@@ -758,15 +758,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
fn parse_time_style(s: Option<&str>) -> UResult<&str> {
match s {
Some(s) => match s {
"full-iso" => Ok("%Y-%m-%d %H:%M:%S.%f %z"),
"long-iso" => Ok("%Y-%m-%d %H:%M"),
"iso" => Ok("%Y-%m-%d"),
"full-iso" => Ok(format::FULL_ISO),
"long-iso" => Ok(format::LONG_ISO),
"iso" => Ok(format::ISO),
_ => match s.chars().next().unwrap() {
'+' => Ok(&s[1..]),
_ => Err(DuError::InvalidTimeStyleArg(s.into()).into()),
},
},
None => Ok("%Y-%m-%d %H:%M"),
None => Ok(format::LONG_ISO),
}
}
+17 -18
View File
@@ -60,7 +60,7 @@ use uucore::line_ending::LineEnding;
use uucore::translate;
use uucore::quoting_style::{QuotingStyle, locale_aware_escape_dir_name, locale_aware_escape_name};
use uucore::time::{FormatSystemTimeFallback, format_system_time};
use uucore::time::{FormatSystemTimeFallback, format, format_system_time};
use uucore::{
display::Quotable,
error::{UError, UResult, set_exit_code},
@@ -251,16 +251,8 @@ enum Files {
}
fn parse_time_style(options: &clap::ArgMatches) -> Result<(String, Option<String>), LsError> {
const TIME_STYLES: [(&str, (&str, Option<&str>)); 4] = [
("full-iso", ("%Y-%m-%d %H:%M:%S.%f %z", None)),
("long-iso", ("%Y-%m-%d %H:%M", None)),
("iso", ("%m-%d %H:%M", Some("%Y-%m-%d "))),
// TODO: Using correct locale string is not implemented.
("locale", ("%b %e %H:%M", Some("%b %e %Y"))),
];
// A map from a time-style parameter to a length-2 tuple of formats:
// the first one is used for recent dates, the second one for older ones (optional).
let time_styles = HashMap::from(TIME_STYLES);
// TODO: Using correct locale string is not implemented.
const LOCALE_FORMAT: (&str, Option<&str>) = ("%b %e %H:%M", Some("%b %e %Y"));
// Convert time_styles references to owned String/option.
fn ok((recent, older): (&str, Option<&str>)) -> Result<(String, Option<String>), LsError> {
@@ -278,7 +270,7 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<(String, Option<String
&& options.indices_of(options::FULL_TIME).unwrap().next_back()
> options.indices_of(options::TIME_STYLE).unwrap().next_back()
{
ok(time_styles["full-iso"])
ok((format::FULL_ISO, None))
} else {
let field = if let Some(field) = field.strip_prefix("posix-") {
// See GNU documentation, set format to "locale" if LC_TIME="POSIX",
@@ -288,16 +280,23 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<(String, Option<String
if std::env::var("LC_TIME").unwrap_or_default() == "POSIX"
|| std::env::var("LC_ALL").unwrap_or_default() == "POSIX"
{
return ok(time_styles["locale"]);
return ok(LOCALE_FORMAT);
}
field
} else {
&field
};
match time_styles.get(field) {
Some(formats) => ok(*formats),
None => match field.chars().next().unwrap() {
match field {
"full-iso" => ok((format::FULL_ISO, None)),
"long-iso" => ok((format::LONG_ISO, None)),
// ISO older format needs extra padding.
"iso" => Ok((
"%m-%d %H:%M".to_string(),
Some(format::ISO.to_string() + " "),
)),
"locale" => ok(LOCALE_FORMAT),
_ => match field.chars().next().unwrap() {
'+' => {
// recent/older formats are (optionally) separated by a newline
let mut it = field[1..].split('\n');
@@ -313,9 +312,9 @@ fn parse_time_style(options: &clap::ArgMatches) -> Result<(String, Option<String
}
}
} else if options.get_flag(options::FULL_TIME) {
ok(time_styles["full-iso"])
ok((format::FULL_ISO, None))
} else {
ok(time_styles["locale"])
ok(LOCALE_FORMAT)
}
}
+6
View File
@@ -36,6 +36,12 @@ pub fn system_time_to_sec(time: SystemTime) -> (i64, u32) {
}
}
pub mod format {
pub static FULL_ISO: &str = "%Y-%m-%d %H:%M:%S.%N %z";
pub static LONG_ISO: &str = "%Y-%m-%d %H:%M";
pub static ISO: &str = "%Y-%m-%d";
}
/// Sets how `format_system_time` behaves if the time cannot be converted.
pub enum FormatSystemTimeFallback {
Integer, // Just print seconds since epoch (`ls`)