@@ -27,6 +27,7 @@ use std::{
27
27
use std:: { collections:: HashSet , io:: IsTerminal } ;
28
28
29
29
use ansi_width:: ansi_width;
30
+ use chrono:: format:: { Item , StrftimeItems } ;
30
31
use chrono:: { DateTime , Local , TimeDelta } ;
31
32
use clap:: {
32
33
Arg , ArgAction , Command ,
@@ -273,32 +274,64 @@ enum TimeStyle {
273
274
Format ( String ) ,
274
275
}
275
276
276
- /// Whether the given date is considered recent (i.e., in the last 6 months).
277
- fn is_recent ( time : DateTime < Local > ) -> bool {
278
- // According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
279
- time + TimeDelta :: try_seconds ( 31_556_952 / 2 ) . unwrap ( ) > Local :: now ( )
280
- }
277
+ /// A struct/impl used to format a file DateTime, precomputing the format for performance reasons.
278
+ struct TimeStyler {
279
+ // default format, always specified.
280
+ default : Vec < Item < ' static > > ,
281
+ // format for a recent time, only specified it is is different from the default
282
+ recent : Option < Vec < Item < ' static > > > ,
283
+ // If `recent` is set, cache the threshold time when we switch from recent to default format.
284
+ recent_time_threshold : Option < DateTime < Local > > ,
285
+ }
286
+
287
+ impl TimeStyler {
288
+ /// Create a TimeStyler based on a TimeStyle specification.
289
+ fn new ( style : & TimeStyle ) -> TimeStyler {
290
+ let default: Vec < Item < ' static > > = match style {
291
+ TimeStyle :: FullIso => StrftimeItems :: new ( "%Y-%m-%d %H:%M:%S.%f %z" ) . parse ( ) ,
292
+ TimeStyle :: LongIso => StrftimeItems :: new ( "%Y-%m-%d %H:%M" ) . parse ( ) ,
293
+ TimeStyle :: Iso => StrftimeItems :: new ( "%Y-%m-%d " ) . parse ( ) ,
294
+ // In this version of chrono translating can be done
295
+ // The function is chrono::datetime::DateTime::format_localized
296
+ // However it's currently still hard to get the current pure-rust-locale
297
+ // So it's not yet implemented
298
+ TimeStyle :: Locale => StrftimeItems :: new ( "%b %e %Y" ) . parse ( ) ,
299
+ TimeStyle :: Format ( fmt) => {
300
+ // TODO (#7802): Replace with new_lenient
301
+ StrftimeItems :: new ( custom_tz_fmt:: custom_time_format ( fmt) . as_str ( ) ) . parse_to_owned ( )
302
+ }
303
+ }
304
+ . unwrap ( ) ;
305
+ let recent = match style {
306
+ TimeStyle :: Iso => Some ( StrftimeItems :: new ( "%m-%d %H:%M" ) ) ,
307
+ // See comment above about locale
308
+ TimeStyle :: Locale => Some ( StrftimeItems :: new ( "%b %e %H:%M" ) ) ,
309
+ _ => None ,
310
+ }
311
+ . map ( |x| x. collect ( ) ) ;
312
+ let recent_time_threshold = if recent. is_some ( ) {
313
+ // According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
314
+ Some ( Local :: now ( ) - TimeDelta :: try_seconds ( 31_556_952 / 2 ) . unwrap ( ) )
315
+ } else {
316
+ None
317
+ } ;
281
318
282
- impl TimeStyle {
283
- /// Format the given time according to this time format style.
319
+ TimeStyler {
320
+ default,
321
+ recent,
322
+ recent_time_threshold,
323
+ }
324
+ }
325
+
326
+ /// Format a DateTime, using `recent` format if available, and the DateTime
327
+ /// is recent enough.
284
328
fn format ( & self , time : DateTime < Local > ) -> String {
285
- let recent = is_recent ( time) ;
286
- match ( self , recent) {
287
- ( Self :: FullIso , _) => time. format ( "%Y-%m-%d %H:%M:%S.%f %z" ) . to_string ( ) ,
288
- ( Self :: LongIso , _) => time. format ( "%Y-%m-%d %H:%M" ) . to_string ( ) ,
289
- ( Self :: Iso , true ) => time. format ( "%m-%d %H:%M" ) . to_string ( ) ,
290
- ( Self :: Iso , false ) => time. format ( "%Y-%m-%d " ) . to_string ( ) ,
291
- // spell-checker:ignore (word) datetime
292
- //In this version of chrono translating can be done
293
- //The function is chrono::datetime::DateTime::format_localized
294
- //However it's currently still hard to get the current pure-rust-locale
295
- //So it's not yet implemented
296
- ( Self :: Locale , true ) => time. format ( "%b %e %H:%M" ) . to_string ( ) ,
297
- ( Self :: Locale , false ) => time. format ( "%b %e %Y" ) . to_string ( ) ,
298
- ( Self :: Format ( fmt) , _) => time
299
- . format ( custom_tz_fmt:: custom_time_format ( fmt) . as_str ( ) )
300
- . to_string ( ) ,
329
+ if self . recent . is_none ( ) || time <= self . recent_time_threshold . unwrap ( ) {
330
+ time. format_with_items ( self . default . iter ( ) )
331
+ } else {
332
+ time. format_with_items ( self . recent . as_ref ( ) . unwrap ( ) . iter ( ) )
301
333
}
334
+ . to_string ( )
302
335
}
303
336
}
304
337
@@ -2060,6 +2093,8 @@ struct ListState<'a> {
2060
2093
uid_cache : HashMap < u32 , String > ,
2061
2094
#[ cfg( unix) ]
2062
2095
gid_cache : HashMap < u32 , String > ,
2096
+
2097
+ time_styler : TimeStyler ,
2063
2098
}
2064
2099
2065
2100
#[ allow( clippy:: cognitive_complexity) ]
@@ -2076,6 +2111,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
2076
2111
uid_cache : HashMap :: new ( ) ,
2077
2112
#[ cfg( unix) ]
2078
2113
gid_cache : HashMap :: new ( ) ,
2114
+ time_styler : TimeStyler :: new ( & config. time_style ) ,
2079
2115
} ;
2080
2116
2081
2117
for loc in locs {
@@ -2876,7 +2912,7 @@ fn display_item_long(
2876
2912
} ;
2877
2913
2878
2914
output_display. extend ( b" " ) ;
2879
- output_display. extend ( display_date ( md, config) . as_bytes ( ) ) ;
2915
+ output_display. extend ( display_date ( md, config, state ) . as_bytes ( ) ) ;
2880
2916
output_display. extend ( b" " ) ;
2881
2917
2882
2918
let item_name = display_item_name (
@@ -3080,9 +3116,9 @@ fn get_time(md: &Metadata, config: &Config) -> Option<DateTime<Local>> {
3080
3116
Some ( time. into ( ) )
3081
3117
}
3082
3118
3083
- fn display_date ( metadata : & Metadata , config : & Config ) -> String {
3119
+ fn display_date ( metadata : & Metadata , config : & Config , state : & mut ListState ) -> String {
3084
3120
match get_time ( metadata, config) {
3085
- Some ( time) => config . time_style . format ( time) ,
3121
+ Some ( time) => state . time_styler . format ( time) ,
3086
3122
None => "???" . into ( ) ,
3087
3123
}
3088
3124
}
0 commit comments