55
66// spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly
77
8- use clap:: {
9- builder:: { NonEmptyStringValueParser , PossibleValue , ValueParser } ,
10- crate_version, Arg , ArgAction , Command ,
11- } ;
12- use glob:: { MatchOptions , Pattern } ;
13- use lscolors:: LsColors ;
14-
15- use ansi_width:: ansi_width;
16- use std:: { cell:: OnceCell , num:: IntErrorKind } ;
17- use std:: { collections:: HashSet , io:: IsTerminal } ;
18-
198#[ cfg( windows) ]
209use std:: os:: windows:: fs:: MetadataExt ;
10+ use std:: { cell:: OnceCell , num:: IntErrorKind } ;
2111use std:: {
2212 cmp:: Reverse ,
2313 error:: Error ,
@@ -34,7 +24,20 @@ use std::{
3424 os:: unix:: fs:: { FileTypeExt , MetadataExt } ,
3525 time:: Duration ,
3626} ;
27+ use std:: { collections:: HashSet , io:: IsTerminal } ;
28+
29+ use ansi_width:: ansi_width;
30+ use chrono:: { DateTime , Local , TimeDelta , TimeZone , Utc } ;
31+ use chrono_tz:: { OffsetName , Tz } ;
32+ use clap:: {
33+ builder:: { NonEmptyStringValueParser , PossibleValue , ValueParser } ,
34+ crate_version, Arg , ArgAction , Command ,
35+ } ;
36+ use glob:: { MatchOptions , Pattern } ;
37+ use iana_time_zone:: get_timezone;
38+ use lscolors:: LsColors ;
3739use term_grid:: { Direction , Filling , Grid , GridOptions } ;
40+
3841use uucore:: error:: USimpleError ;
3942use uucore:: format:: human:: { human_readable, SizeFormat } ;
4043#[ cfg( all( unix, not( any( target_os = "android" , target_os = "macos" ) ) ) ) ]
@@ -67,10 +70,12 @@ use uucore::{
6770 version_cmp:: version_cmp,
6871} ;
6972use uucore:: { help_about, help_section, help_usage, parse_glob, show, show_error, show_warning} ;
73+
7074mod dired;
7175use dired:: { is_dired_arg_present, DiredOutput } ;
7276mod colors;
7377use colors:: { color_name, StyleManager } ;
78+
7479#[ cfg( not( feature = "selinux" ) ) ]
7580static CONTEXT_HELP_TEXT : & str = "print any security context of each file (not enabled)" ;
7681#[ cfg( feature = "selinux" ) ]
@@ -334,6 +339,58 @@ enum TimeStyle {
334339 Format ( String ) ,
335340}
336341
342+ /// Whether the given date is considered recent (i.e., in the last 6 months).
343+ fn is_recent ( time : DateTime < Local > ) -> bool {
344+ // According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
345+ time + TimeDelta :: try_seconds ( 31_556_952 / 2 ) . unwrap ( ) > Local :: now ( )
346+ }
347+
348+ /// Get the alphabetic abbreviation of the current timezone.
349+ ///
350+ /// For example, "UTC" or "CET" or "PDT".
351+ fn timezone_abbrev ( ) -> String {
352+ let tz = match std:: env:: var ( "TZ" ) {
353+ // TODO Support other time zones...
354+ Ok ( s) if s == "UTC0" || s. is_empty ( ) => Tz :: Etc__UTC ,
355+ _ => match get_timezone ( ) {
356+ Ok ( tz_str) => tz_str. parse ( ) . unwrap ( ) ,
357+ Err ( _) => Tz :: Etc__UTC ,
358+ } ,
359+ } ;
360+ let offset = tz. offset_from_utc_date ( & Utc :: now ( ) . date_naive ( ) ) ;
361+ offset. abbreviation ( ) . unwrap_or ( "UTC" ) . to_string ( )
362+ }
363+
364+ /// Format the given time according to a custom format string.
365+ fn custom_time_format ( fmt : & str , time : DateTime < Local > ) -> String {
366+ // TODO Refactor the common code from `ls` and `date` for rendering dates.
367+ // TODO - Revisit when chrono 0.5 is released. https://github.com/chronotope/chrono/issues/970
368+ // GNU `date` uses `%N` for nano seconds, however the `chrono` crate uses `%f`.
369+ let fmt = fmt. replace ( "%N" , "%f" ) . replace ( "%Z" , & timezone_abbrev ( ) ) ;
370+ time. format ( & fmt) . to_string ( )
371+ }
372+
373+ impl TimeStyle {
374+ /// Format the given time according to this time format style.
375+ fn format ( & self , time : DateTime < Local > ) -> String {
376+ let recent = is_recent ( time) ;
377+ match ( self , recent) {
378+ ( Self :: FullIso , _) => time. format ( "%Y-%m-%d %H:%M:%S.%f %z" ) . to_string ( ) ,
379+ ( Self :: LongIso , _) => time. format ( "%Y-%m-%d %H:%M" ) . to_string ( ) ,
380+ ( Self :: Iso , true ) => time. format ( "%m-%d %H:%M" ) . to_string ( ) ,
381+ ( Self :: Iso , false ) => time. format ( "%Y-%m-%d " ) . to_string ( ) ,
382+ // spell-checker:ignore (word) datetime
383+ //In this version of chrono translating can be done
384+ //The function is chrono::datetime::DateTime::format_localized
385+ //However it's currently still hard to get the current pure-rust-locale
386+ //So it's not yet implemented
387+ ( Self :: Locale , true ) => time. format ( "%b %e %H:%M" ) . to_string ( ) ,
388+ ( Self :: Locale , false ) => time. format ( "%b %e %Y" ) . to_string ( ) ,
389+ ( Self :: Format ( e) , _) => custom_time_format ( e, time) ,
390+ }
391+ }
392+ }
393+
337394fn parse_time_style ( options : & clap:: ArgMatches ) -> Result < TimeStyle , LsError > {
338395 let possible_time_styles = vec ! [
339396 "full-iso" . to_string( ) ,
@@ -3115,31 +3172,7 @@ fn get_time(md: &Metadata, config: &Config) -> Option<chrono::DateTime<chrono::L
31153172
31163173fn display_date ( metadata : & Metadata , config : & Config ) -> String {
31173174 match get_time ( metadata, config) {
3118- Some ( time) => {
3119- //Date is recent if from past 6 months
3120- //According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
3121- let recent = time + chrono:: TimeDelta :: try_seconds ( 31_556_952 / 2 ) . unwrap ( )
3122- > chrono:: Local :: now ( ) ;
3123-
3124- match & config. time_style {
3125- TimeStyle :: FullIso => time. format ( "%Y-%m-%d %H:%M:%S.%f %z" ) ,
3126- TimeStyle :: LongIso => time. format ( "%Y-%m-%d %H:%M" ) ,
3127- TimeStyle :: Iso => time. format ( if recent { "%m-%d %H:%M" } else { "%Y-%m-%d " } ) ,
3128- TimeStyle :: Locale => {
3129- let fmt = if recent { "%b %e %H:%M" } else { "%b %e %Y" } ;
3130-
3131- // spell-checker:ignore (word) datetime
3132- //In this version of chrono translating can be done
3133- //The function is chrono::datetime::DateTime::format_localized
3134- //However it's currently still hard to get the current pure-rust-locale
3135- //So it's not yet implemented
3136-
3137- time. format ( fmt)
3138- }
3139- TimeStyle :: Format ( e) => time. format ( e) ,
3140- }
3141- . to_string ( )
3142- }
3175+ Some ( time) => config. time_style . format ( time) ,
31433176 None => "???" . into ( ) ,
31443177 }
31453178}
0 commit comments