@@ -31,9 +31,6 @@ use clap::{
3131 builder:: { NonEmptyStringValueParser , PossibleValue , ValueParser } ,
3232} ;
3333use glob:: { MatchOptions , Pattern } ;
34- use jiff:: fmt:: StdIoWrite ;
35- use jiff:: fmt:: strtime:: BrokenDownTime ;
36- use jiff:: { Timestamp , Zoned } ;
3734use lscolors:: LsColors ;
3835use term_grid:: { DEFAULT_SEPARATOR_SIZE , Direction , Filling , Grid , GridOptions , SPACES_IN_TAB } ;
3936use thiserror:: Error ;
@@ -258,83 +255,54 @@ enum Time {
258255 Birth ,
259256}
260257
261- #[ derive( Debug ) ]
262- enum TimeStyle {
263- FullIso ,
264- LongIso ,
265- Iso ,
266- Locale ,
267- Format ( String ) ,
268- }
269-
270- /// Whether the given date is considered recent (i.e., in the last 6 months).
271- fn is_recent ( time : Timestamp , state : & mut ListState ) -> bool {
272- time > state. recent_time_threshold
273- }
274-
275- impl TimeStyle {
276- /// Format the given time according to this time format style.
277- fn format (
278- & self ,
279- date : Zoned ,
280- out : & mut Vec < u8 > ,
281- state : & mut ListState ,
282- ) -> Result < ( ) , jiff:: Error > {
283- let recent = is_recent ( date. timestamp ( ) , state) ;
284- let tm = BrokenDownTime :: from ( & date) ;
285- let mut out = StdIoWrite ( out) ;
286- let config = jiff:: fmt:: strtime:: Config :: new ( ) . lenient ( true ) ;
287-
288- let fmt = match ( self , recent) {
289- ( Self :: FullIso , _) => "%Y-%m-%d %H:%M:%S.%f %z" ,
290- ( Self :: LongIso , _) => "%Y-%m-%d %H:%M" ,
291- ( Self :: Iso , true ) => "%m-%d %H:%M" ,
292- ( Self :: Iso , false ) => "%Y-%m-%d " ,
293- // TODO: Using correct locale string is not implemented.
294- ( Self :: Locale , true ) => "%b %e %H:%M" ,
295- ( Self :: Locale , false ) => "%b %e %Y" ,
296- ( Self :: Format ( fmt) , _) => fmt,
297- } ;
258+ fn parse_time_style ( options : & clap:: ArgMatches ) -> Result < ( String , Option < String > ) , LsError > {
259+ const TIME_STYLES : [ ( & str , ( & str , Option < & str > ) ) ; 4 ] = [
260+ ( "full-iso" , ( "%Y-%m-%d %H:%M:%S.%f %z" , None ) ) ,
261+ ( "long-iso" , ( "%Y-%m-%d %H:%M" , None ) ) ,
262+ ( "iso" , ( "%m-%d %H:%M" , Some ( "%Y-%m-%d " ) ) ) ,
263+ // TODO: Using correct locale string is not implemented.
264+ ( "locale" , ( "%b %e %H:%M" , Some ( "%b %e %Y" ) ) ) ,
265+ ] ;
266+ // A map from a time-style parameter to a length-2 tuple of formats:
267+ // the first one is used for recent dates, the second one for older ones (optional).
268+ let time_styles = HashMap :: from ( TIME_STYLES ) ;
269+ let possible_time_styles = TIME_STYLES
270+ . iter ( )
271+ . map ( |( x, _) | * x)
272+ . chain ( iter:: once (
273+ "+FORMAT (e.g., +%H:%M) for a 'date'-style format" ,
274+ ) )
275+ . map ( |s| s. to_string ( ) ) ;
298276
299- tm. format_with_config ( & config, fmt, & mut out)
277+ // Convert time_styles references to owned String/option.
278+ fn ok ( ( recent, older) : ( & str , Option < & str > ) ) -> Result < ( String , Option < String > ) , LsError > {
279+ Ok ( ( recent. to_string ( ) , older. map ( String :: from) ) )
300280 }
301- }
302281
303- fn parse_time_style ( options : & clap:: ArgMatches ) -> Result < TimeStyle , LsError > {
304- let possible_time_styles = vec ! [
305- "full-iso" . to_string( ) ,
306- "long-iso" . to_string( ) ,
307- "iso" . to_string( ) ,
308- "locale" . to_string( ) ,
309- "+FORMAT (e.g., +%H:%M) for a 'date'-style format" . to_string( ) ,
310- ] ;
311282 if let Some ( field) = options. get_one :: < String > ( options:: TIME_STYLE ) {
312283 //If both FULL_TIME and TIME_STYLE are present
313284 //The one added last is dominant
314285 if options. get_flag ( options:: FULL_TIME )
315286 && options. indices_of ( options:: FULL_TIME ) . unwrap ( ) . next_back ( )
316287 > options. indices_of ( options:: TIME_STYLE ) . unwrap ( ) . next_back ( )
317288 {
318- Ok ( TimeStyle :: FullIso )
289+ ok ( time_styles [ "full-iso" ] )
319290 } else {
320- match field. as_str ( ) {
321- "full-iso" => Ok ( TimeStyle :: FullIso ) ,
322- "long-iso" => Ok ( TimeStyle :: LongIso ) ,
323- "iso" => Ok ( TimeStyle :: Iso ) ,
324- "locale" => Ok ( TimeStyle :: Locale ) ,
325- _ => match field. chars ( ) . next ( ) . unwrap ( ) {
326- '+' => Ok ( TimeStyle :: Format ( String :: from ( & field[ 1 ..] ) ) ) ,
291+ match time_styles. get ( field. as_str ( ) ) {
292+ Some ( formats) => ok ( * formats) ,
293+ None => match field. chars ( ) . next ( ) . unwrap ( ) {
294+ '+' => Ok ( ( field[ 1 ..] . to_string ( ) , None ) ) ,
327295 _ => Err ( LsError :: TimeStyleParseError (
328296 String :: from ( field) ,
329- possible_time_styles,
297+ possible_time_styles. collect ( ) ,
330298 ) ) ,
331299 } ,
332300 }
333301 }
334302 } else if options. get_flag ( options:: FULL_TIME ) {
335- Ok ( TimeStyle :: FullIso )
303+ ok ( time_styles [ "full-iso" ] )
336304 } else {
337- Ok ( TimeStyle :: Locale )
305+ ok ( time_styles [ "locale" ] )
338306 }
339307}
340308
@@ -377,7 +345,8 @@ pub struct Config {
377345 // Dir and vdir needs access to this field
378346 pub quoting_style : QuotingStyle ,
379347 indicator_style : IndicatorStyle ,
380- time_style : TimeStyle ,
348+ time_format_recent : String , // Time format for recent dates
349+ time_format_older : Option < String > , // Time format for older dates (optional, if not present, time_format_recent is used)
381350 context : bool ,
382351 selinux_supported : bool ,
383352 group_directories_first : bool ,
@@ -925,10 +894,10 @@ impl Config {
925894 let indicator_style = extract_indicator_style ( options) ;
926895 // Only parse the value to "--time-style" if it will become relevant.
927896 let dired = options. get_flag ( options:: DIRED ) ;
928- let time_style = if format == Format :: Long || dired {
897+ let ( time_format_recent , time_format_older ) = if format == Format :: Long || dired {
929898 parse_time_style ( options) ?
930899 } else {
931- TimeStyle :: Iso
900+ Default :: default ( )
932901 } ;
933902
934903 let mut ignore_patterns: Vec < Pattern > = Vec :: new ( ) ;
@@ -1114,7 +1083,8 @@ impl Config {
11141083 width,
11151084 quoting_style,
11161085 indicator_style,
1117- time_style,
1086+ time_format_recent,
1087+ time_format_older,
11181088 context,
11191089 selinux_supported : {
11201090 #[ cfg( feature = "selinux" ) ]
@@ -2002,7 +1972,7 @@ struct ListState<'a> {
20021972 uid_cache : HashMap < u32 , String > ,
20031973 #[ cfg( unix) ]
20041974 gid_cache : HashMap < u32 , String > ,
2005- recent_time_threshold : Timestamp ,
1975+ recent_time_threshold : SystemTime ,
20061976}
20071977
20081978#[ allow( clippy:: cognitive_complexity) ]
@@ -2020,7 +1990,7 @@ pub fn list(locs: Vec<&Path>, config: &Config) -> UResult<()> {
20201990 #[ cfg( unix) ]
20211991 gid_cache : HashMap :: new ( ) ,
20221992 // According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
2023- recent_time_threshold : Timestamp :: now ( ) - Duration :: new ( 31_556_952 / 2 , 0 ) ,
1993+ recent_time_threshold : SystemTime :: now ( ) - Duration :: new ( 31_556_952 / 2 , 0 ) ,
20241994 } ;
20251995
20261996 for loc in locs {
@@ -2993,7 +2963,7 @@ fn display_group(_metadata: &Metadata, _config: &Config, _state: &mut ListState)
29932963 "somegroup"
29942964}
29952965
2996- // The implementations for get_time are separated because some options, such
2966+ // The implementations for get_system_time are separated because some options, such
29972967// as ctime will not be available
29982968#[ cfg( unix) ]
29992969fn get_system_time ( md : & Metadata , config : & Config ) -> Option < SystemTime > {
@@ -3015,28 +2985,25 @@ fn get_system_time(md: &Metadata, config: &Config) -> Option<SystemTime> {
30152985 }
30162986}
30172987
3018- fn get_time ( md : & Metadata , config : & Config ) -> Option < Zoned > {
3019- let time = get_system_time ( md, config) ?;
3020- time. try_into ( ) . ok ( )
3021- }
3022-
30232988fn display_date (
30242989 metadata : & Metadata ,
30252990 config : & Config ,
30262991 state : & mut ListState ,
30272992 out : & mut Vec < u8 > ,
30282993) -> UResult < ( ) > {
3029- match get_time ( metadata, config) {
3030- // TODO: Some fancier error conversion might be nice.
3031- Some ( time) => config
3032- . time_style
3033- . format ( time, out, state)
3034- . map_err ( |x| USimpleError :: new ( 1 , x. to_string ( ) ) ) ,
3035- None => {
3036- out. extend ( b"???" ) ;
3037- Ok ( ( ) )
3038- }
3039- }
2994+ let Some ( time) = get_system_time ( metadata, config) else {
2995+ out. extend ( b"???" ) ;
2996+ return Ok ( ( ) ) ;
2997+ } ;
2998+
2999+ // Use "recent" format if the given date is considered recent (i.e., in the last 6 months),
3000+ // or if no "older" format is available.
3001+ let fmt = match & config. time_format_older {
3002+ Some ( time_format_older) if time <= state. recent_time_threshold => time_format_older,
3003+ _ => & config. time_format_recent ,
3004+ } ;
3005+
3006+ uucore:: time:: format_system_time ( out, time, fmt, false )
30403007}
30413008
30423009#[ allow( dead_code) ]
0 commit comments