Skip to content

Commit eb2ceaf

Browse files
committed
Date format supports recent/non-recent format
1 parent ea56a7c commit eb2ceaf

File tree

4 files changed

+114
-37
lines changed

4 files changed

+114
-37
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Add `--system-protected` to include files with the Windows `system` flag set,
1313
on other platform the same as `--all` [#752](https://github.com/Peltoche/lsd/issues/752)
1414
- Add many icons from https://github.com/Peltoche/lsd/issues/764 [@TruncatedDinosour](https://ari-web.xyz/gh)
15+
- Add support to format dates for recent files differently. If `'+<date_format>'` contains a second format it is applied to files from the last six months. The formats are separated by new-line, similar to `TIME_STYLE` in GNU ls.
1516

1617
### Fixed
1718
- Do not quote filename when piping into another program from [TeamTamoad](https://github.com/TeamTamoad)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ color:
132132
# When "classic" is set, this is set to "date".
133133
# Possible values: date, relative, '+<date_format>'
134134
# `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
135+
# `'+<date_format>'` can contain a second format after a new-line. The second format is used format dates for files from the last six months.
136+
135137
date: date
136138

137139
# == Dereference ==

src/app.rs

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -363,49 +363,57 @@ fn validate_date_argument(arg: &str) -> Result<String, String> {
363363
}
364364

365365
pub fn validate_time_format(formatter: &str) -> Result<String, String> {
366-
let mut chars = formatter.chars();
367-
loop {
368-
match chars.next() {
369-
Some('%') => match chars.next() {
370-
Some('.') => match chars.next() {
371-
Some('f') => (),
372-
Some(n @ ('3' | '6' | '9')) => match chars.next() {
366+
let vec: Vec<&str> = formatter.split('\n').collect();
367+
368+
if vec.len() > 2 {
369+
return Err("invalid date format, cannot contain more than two entries".to_owned());
370+
}
371+
372+
for s in vec {
373+
let mut chars = s.chars();
374+
loop {
375+
match chars.next() {
376+
Some('%') => match chars.next() {
377+
Some('.') => match chars.next() {
373378
Some('f') => (),
374-
Some(c) => return Err(format!("invalid format specifier: %.{}{}", n, c)),
379+
Some(n @ ('3' | '6' | '9')) => match chars.next() {
380+
Some('f') => (),
381+
Some(c) => return Err(format!("invalid format specifier: %.{}{}", n, c)),
382+
None => return Err("missing format specifier".to_owned()),
383+
},
384+
Some(c) => return Err(format!("invalid format specifier: %.{}", c)),
385+
None => return Err("missing format specifier".to_owned()),
386+
},
387+
Some(n @ (':' | '#')) => match chars.next() {
388+
Some('z') => (),
389+
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
390+
None => return Err("missing format specifier".to_owned()),
391+
},
392+
Some(n @ ('-' | '_' | '0')) => match chars.next() {
393+
Some(
394+
'C' | 'd' | 'e' | 'f' | 'G' | 'g' | 'H' | 'I' | 'j' | 'k' | 'l' | 'M'
395+
| 'm' | 'S' | 's' | 'U' | 'u' | 'V' | 'W' | 'w' | 'Y' | 'y',
396+
) => (),
397+
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
375398
None => return Err("missing format specifier".to_owned()),
376399
},
377-
Some(c) => return Err(format!("invalid format specifier: %.{}", c)),
378-
None => return Err("missing format specifier".to_owned()),
379-
},
380-
Some(n @ (':' | '#')) => match chars.next() {
381-
Some('z') => (),
382-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
383-
None => return Err("missing format specifier".to_owned()),
384-
},
385-
Some(n @ ('-' | '_' | '0')) => match chars.next() {
386400
Some(
387-
'C' | 'd' | 'e' | 'f' | 'G' | 'g' | 'H' | 'I' | 'j' | 'k' | 'l' | 'M' | 'm'
388-
| 'S' | 's' | 'U' | 'u' | 'V' | 'W' | 'w' | 'Y' | 'y',
401+
'A' | 'a' | 'B' | 'b' | 'C' | 'c' | 'D' | 'd' | 'e' | 'F' | 'f' | 'G' | 'g'
402+
| 'H' | 'h' | 'I' | 'j' | 'k' | 'l' | 'M' | 'm' | 'n' | 'P' | 'p' | 'R'
403+
| 'r' | 'S' | 's' | 'T' | 't' | 'U' | 'u' | 'V' | 'v' | 'W' | 'w' | 'X'
404+
| 'x' | 'Y' | 'y' | 'Z' | 'z' | '+' | '%',
389405
) => (),
390-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
391-
None => return Err("missing format specifier".to_owned()),
392-
},
393-
Some(
394-
'A' | 'a' | 'B' | 'b' | 'C' | 'c' | 'D' | 'd' | 'e' | 'F' | 'f' | 'G' | 'g'
395-
| 'H' | 'h' | 'I' | 'j' | 'k' | 'l' | 'M' | 'm' | 'n' | 'P' | 'p' | 'R' | 'r'
396-
| 'S' | 's' | 'T' | 't' | 'U' | 'u' | 'V' | 'v' | 'W' | 'w' | 'X' | 'x' | 'Y'
397-
| 'y' | 'Z' | 'z' | '+' | '%',
398-
) => (),
399-
Some(n @ ('3' | '6' | '9')) => match chars.next() {
400-
Some('f') => (),
401-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
406+
Some(n @ ('3' | '6' | '9')) => match chars.next() {
407+
Some('f') => (),
408+
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
409+
None => return Err("missing format specifier".to_owned()),
410+
},
411+
Some(c) => return Err(format!("invalid format specifier: %{}", c)),
402412
None => return Err("missing format specifier".to_owned()),
403413
},
404-
Some(c) => return Err(format!("invalid format specifier: %{}", c)),
405-
None => return Err("missing format specifier".to_owned()),
406-
},
407-
None => break,
408-
_ => continue,
414+
None => break,
415+
_ => continue,
416+
}
409417
}
410418
}
411419
Ok(formatter.to_owned())

src/meta/date.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,17 @@ impl Date {
5555
val.format("%F").to_string()
5656
}
5757
}
58-
DateFlag::Formatted(format) => val.format(format).to_string(),
58+
DateFlag::Formatted(format) => {
59+
let vec: Vec<&str> = format.split('\n').collect();
60+
61+
if vec.len() == 1 || *val < Local::now() - Duration::seconds(15_778_476) {
62+
// non-recent or only one format
63+
val.format(vec[0]).to_string()
64+
} else {
65+
// recent
66+
val.format(vec[1]).to_string()
67+
}
68+
}
5969
}
6070
} else {
6171
String::from('-')
@@ -305,6 +315,62 @@ mod test {
305315
fs::remove_file(file_path).unwrap();
306316
}
307317

318+
#[test]
319+
fn test_recent_format_now() {
320+
let mut file_path = env::temp_dir();
321+
file_path.push("test_recent_format_now.tmp");
322+
323+
let creation_date = Local::now();
324+
let success = cross_platform_touch(&file_path, &creation_date)
325+
.unwrap()
326+
.success();
327+
assert_eq!(true, success, "failed to exec touch");
328+
329+
let colors = Colors::new(ThemeOption::Default);
330+
let date = Date::from(&file_path.metadata().unwrap());
331+
332+
let mut flags = Flags::default();
333+
flags.date = DateFlag::Formatted(String::from("%F\n%H:%M"));
334+
335+
assert_eq!(
336+
creation_date
337+
.format("%H:%M")
338+
.to_string()
339+
.with(Color::AnsiValue(40)),
340+
date.render(&colors, &flags)
341+
);
342+
343+
fs::remove_file(file_path).unwrap();
344+
}
345+
346+
#[test]
347+
fn test_recent_format_year_old() {
348+
let mut file_path = env::temp_dir();
349+
file_path.push("test_recent_format_year_old.tmp");
350+
351+
let creation_date = Local::now() - Duration::days(400);
352+
let success = cross_platform_touch(&file_path, &creation_date)
353+
.unwrap()
354+
.success();
355+
assert_eq!(true, success, "failed to exec touch");
356+
357+
let colors = Colors::new(ThemeOption::Default);
358+
let date = Date::from(&file_path.metadata().unwrap());
359+
360+
let mut flags = Flags::default();
361+
flags.date = DateFlag::Formatted(String::from("%F\n%H:%M"));
362+
363+
assert_eq!(
364+
creation_date
365+
.format("%F")
366+
.to_string()
367+
.with(Color::AnsiValue(36)),
368+
date.render(&colors, &flags)
369+
);
370+
371+
fs::remove_file(file_path).unwrap();
372+
}
373+
308374
#[test]
309375
#[cfg(all(not(windows), target_arch = "x86_64"))]
310376
fn test_bad_date() {

0 commit comments

Comments
 (0)