From 07c44e77973fc8b87da5395cc37e6d8e0953f168 Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Thu, 11 Dec 2025 19:00:58 +0100 Subject: [PATCH 1/2] Add 'iso' option to --date argument Previously, the --date flag did not support ISO format. It was only affected in the TIME_STYLE environment variable. --- doc/lsd.md | 4 +++- doc/samples/config-sample.yaml | 2 +- src/app.rs | 6 +++--- src/flags/date.rs | 8 ++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/doc/lsd.md b/doc/lsd.md index 78a120465..5e1051cd1 100644 --- a/doc/lsd.md +++ b/doc/lsd.md @@ -99,7 +99,9 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich : When to use terminal colours [default: auto] [possible values: always, auto, never] `--date ...` -: How to display date [possible values: date, locale, relative, +date-time-format] [default: date] +: How to display date [possible values: date, iso, locale, relative, +date-time-format] [default: date] + + The `iso` format displays dates in ISO format (MM-DD HH:MM for recent files, YYYY-MM-DD for older files). `--depth ...` : Stop recursing into directories after reaching specified depth diff --git a/doc/samples/config-sample.yaml b/doc/samples/config-sample.yaml index 3da640e5d..93974d4cb 100644 --- a/doc/samples/config-sample.yaml +++ b/doc/samples/config-sample.yaml @@ -35,7 +35,7 @@ color: # This specifies the date format for the date column. The freeform format # accepts a strftime like string. # When "classic" is set, this is set to "date". -# Possible values: date, locale, relative, '+' +# Possible values: date, iso, locale, relative, '+' # `date_format` will be a `strftime` formatted value. e.g. `date: '+%d %b %y %X'` will give you a date like this: 17 Jun 21 20:14:55 date: date diff --git a/src/app.rs b/src/app.rs index c8d80c7cb..e11c20a57 100644 --- a/src/app.rs +++ b/src/app.rs @@ -84,7 +84,7 @@ pub struct Cli { #[arg(long)] pub total_size: bool, - /// How to display date [default: date] [possible values: date, locale, relative, +date-time-format] + /// How to display date [default: date] [possible values: date, iso, locale, relative, +date-time-format] #[arg(long, value_parser = validate_date_argument)] pub date: Option, @@ -205,10 +205,10 @@ pub struct Cli { fn validate_date_argument(arg: &str) -> Result { if arg.starts_with('+') { validate_time_format(arg) - } else if arg == "date" || arg == "relative" || arg == "locale" { + } else if arg == "date" || arg == "relative" || arg == "locale" || arg == "iso" { Result::Ok(arg.to_owned()) } else { - Result::Err("possible values: date, locale, relative, +date-time-format".to_owned()) + Result::Err("possible values: date, iso, locale, relative, +date-time-format".to_owned()) } } diff --git a/src/flags/date.rs b/src/flags/date.rs index a316cafe9..b15b701e8 100644 --- a/src/flags/date.rs +++ b/src/flags/date.rs @@ -36,6 +36,7 @@ impl DateFlag { "date" => Some(Self::Date), "locale" => Some(Self::Locale), "relative" => Some(Self::Relative), + "iso" => Some(Self::Iso), _ if value.starts_with('+') => Self::from_format_string(value), _ => { print_error!("Not a valid date value: {}.", value); @@ -131,6 +132,13 @@ mod test { assert_eq!(Some(DateFlag::Relative), DateFlag::from_cli(&cli)); } + #[test] + fn test_from_cli_iso() { + let argv = ["lsd", "--date", "iso"]; + let cli = Cli::try_parse_from(argv).unwrap(); + assert_eq!(Some(DateFlag::Iso), DateFlag::from_cli(&cli)); + } + #[test] fn test_from_cli_format() { let argv = ["lsd", "--date", "+%F"]; From 487d959ffa72ee86d7052924324c6e6248ddecbd Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Thu, 11 Dec 2025 23:56:07 +0100 Subject: [PATCH 2/2] Support dual-line date formats for recent and old files Allow DateFlag::Formatted to accept two formats separated by a newline. Recent files use the second format, older files use the first. Add tests to verify correct formatting based on file age. This corrctly parses Gnu coreutils time styles --- doc/lsd.md | 2 + doc/samples/config-sample.yaml | 1 + src/meta/date.rs | 73 +++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/doc/lsd.md b/doc/lsd.md index 5e1051cd1..1a33f5f3a 100644 --- a/doc/lsd.md +++ b/doc/lsd.md @@ -103,6 +103,8 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich The `iso` format displays dates in ISO format (MM-DD HH:MM for recent files, YYYY-MM-DD for older files). + For custom formats using +date-time-format, you can specify dual-line formats by separating two format strings with a newline (e.g., '+%F\n%H:%M'). The first format is used for old files (>6 months), and the second for recent files. + `--depth ...` : Stop recursing into directories after reaching specified depth diff --git a/doc/samples/config-sample.yaml b/doc/samples/config-sample.yaml index 93974d4cb..b922f6511 100644 --- a/doc/samples/config-sample.yaml +++ b/doc/samples/config-sample.yaml @@ -37,6 +37,7 @@ color: # When "classic" is set, this is set to "date". # Possible values: date, iso, locale, relative, '+' # `date_format` will be a `strftime` formatted value. e.g. `date: '+%d %b %y %X'` will give you a date like this: 17 Jun 21 20:14:55 +# For dual-line date formats (recent vs old files), use a newline to separate formats. e.g. `date: '+%F\n%H:%M'` uses '%F' for old files and '%H:%M' for recent files. date: date # == Dereference == diff --git a/src/meta/date.rs b/src/meta/date.rs index 007023c35..2c7b2d944 100644 --- a/src/meta/date.rs +++ b/src/meta/date.rs @@ -61,7 +61,17 @@ impl Date { val.format("%F").to_string() } } - DateFlag::Formatted(format) => val.format_localized(format, locale).to_string(), + DateFlag::Formatted(format) => { + let vec: Vec<&str> = format.split('\n').collect(); + + if vec.len() == 1 { + val.format_localized(format, locale).to_string() + } else if *val > Local::now() - Duration::seconds(15_778_476) { + val.format_localized(vec[1], locale).to_string() + } else { + val.format_localized(vec[0], locale).to_string() + } + } } } else { String::from('-') @@ -347,6 +357,67 @@ mod test { fs::remove_file(file_path).unwrap(); } + #[test] + fn test_recent_format_now() { + let mut file_path = env::temp_dir(); + file_path.push("test_recent_format_now.tmp"); + + let creation_date = Local::now(); + let success = cross_platform_touch(&file_path, &creation_date) + .unwrap() + .success(); + assert!(success, "failed to exec touch"); + + let colors = Colors::new(ThemeOption::Default); + let date = Date::from(&file_path.metadata().unwrap()); + + let flags = Flags { + date: DateFlag::Formatted(String::from("%F\n%H:%M")), + ..Default::default() + }; + + assert_eq!( + creation_date + .format("%H:%M") + .to_string() + .with(Color::AnsiValue(40)), + date.render(&colors, &flags) + ); + + fs::remove_file(file_path).unwrap(); + } + + #[test] + fn test_recent_format_year_old() { + let mut file_path = env::temp_dir(); + file_path.push("test_recent_format_year_old.tmp"); + + #[allow(deprecated)] + let creation_date = Local::now() - Duration::days(400); + let success = cross_platform_touch(&file_path, &creation_date) + .unwrap() + .success(); + assert!(success, "failed to exec touch"); + + let colors = Colors::new(ThemeOption::Default); + let date = Date::from(&file_path.metadata().unwrap()); + + let flags = Flags { + date: DateFlag::Formatted(String::from("%F\n%H:%M")), + ..Default::default() + }; + + assert_eq!( + creation_date + .format("%F") + .to_string() + .with(Color::AnsiValue(36)), + date.render(&colors, &flags) + ); + + fs::remove_file(file_path).unwrap(); + } + #[test] #[cfg(all(not(windows), target_arch = "x86_64"))] fn test_bad_date() {