@@ -7,6 +7,7 @@ pub use crate::flags::color::ThemeOption;
77use crate :: git:: GitStatus ;
88use crate :: print_output;
99use crate :: theme:: { Theme , color:: ColorTheme } ;
10+ use jiff:: { Span , SpanTotal , Timestamp , ToSpan , Unit } ;
1011
1112#[ allow( dead_code) ]
1213#[ derive( Hash , Debug , Eq , PartialEq , Clone ) ]
@@ -45,9 +46,8 @@ pub enum Elem {
4546 System ,
4647
4748 /// Last Time Modified
48- DayOld ,
49- HourOld ,
50- Older ,
49+ Date ( i64 ) ,
50+ InvalidDate ,
5151
5252 /// User / Group Name
5353 User ,
@@ -123,10 +123,6 @@ impl Elem {
123123 Elem :: Hidden => theme. attributes . hidden ,
124124 Elem :: System => theme. attributes . system ,
125125
126- Elem :: DayOld => theme. date . day_old ,
127- Elem :: HourOld => theme. date . hour_old ,
128- Elem :: Older => theme. date . older ,
129-
130126 Elem :: User => theme. user ,
131127 Elem :: Group => theme. group ,
132128 Elem :: NonFile => theme. size . none ,
@@ -169,15 +165,30 @@ impl Elem {
169165 Elem :: GitStatus {
170166 status : GitStatus :: Conflicted ,
171167 } => theme. git_status . conflicted ,
168+ Elem :: Date ( _) | Elem :: InvalidDate => {
169+ // These are handled in style_default, not here
170+ Color :: Blue
171+ }
172172 }
173173 }
174174}
175175
176176pub type ColoredString = StyledContent < String > ;
177177
178+ /// Unified timestamp-based date color entry
179+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
180+ struct TimestampColorEntry {
181+ timestamp : i64 ,
182+ color : Color ,
183+ }
184+
178185pub struct Colors {
179186 theme : Option < ColorTheme > ,
180187 lscolors : Option < LsColors > ,
188+ default_date_color : Color ,
189+ /// Sorted timestamp table: all entries (legacy, relative, absolute) converted to timestamps
190+ /// Sorted in ascending order (oldest first)
191+ timestamp_colors : Vec < TimestampColorEntry > ,
181192}
182193
183194impl Colors {
@@ -209,7 +220,72 @@ impl Colors {
209220 _ => None ,
210221 } ;
211222
212- Self { theme, lscolors }
223+ let ( default_date_color, timestamp_colors) = if let Some ( t) = & theme {
224+ let now = Timestamp :: now ( ) . as_second ( ) ;
225+ let mut timestamp_entries: Vec < TimestampColorEntry > = Vec :: new ( ) ;
226+
227+ // Convert legacy config to timestamp entries (relative to now)
228+ if let Some ( hour_old) = t. date . hour_old {
229+ timestamp_entries. push ( TimestampColorEntry {
230+ timestamp : now - 1 . hours ( ) . total ( Unit :: Second ) . unwrap ( ) as i64 ,
231+ color : hour_old,
232+ } ) ;
233+ }
234+
235+ if let Some ( day_old) = t. date . day_old {
236+ timestamp_entries. push ( TimestampColorEntry {
237+ timestamp : now
238+ - 1 . days ( )
239+ . total ( SpanTotal :: from ( Unit :: Second ) . days_are_24_hours ( ) )
240+ . unwrap ( ) as i64 ,
241+ color : day_old,
242+ } ) ;
243+ }
244+
245+ timestamp_entries. push ( TimestampColorEntry {
246+ timestamp : i64:: MIN ,
247+ color : t. date . older ,
248+ } ) ;
249+
250+ // Convert relative config to timestamp entries
251+ for relative in & t. date . relative {
252+ if let Ok ( span) = relative. threshold . parse :: < Span > ( ) {
253+ if let Ok ( total_seconds) =
254+ span. total ( SpanTotal :: from ( Unit :: Second ) . days_are_24_hours ( ) )
255+ {
256+ let timestamp = now - total_seconds as i64 ;
257+ timestamp_entries. push ( TimestampColorEntry {
258+ timestamp,
259+ color : relative. color ,
260+ } )
261+ }
262+ }
263+ }
264+
265+ // Convert absolute config to timestamp entries
266+ for absolute in & t. date . absolute {
267+ if let Ok ( threshold) = absolute. threshold . parse :: < Timestamp > ( ) {
268+ timestamp_entries. push ( TimestampColorEntry {
269+ timestamp : threshold. as_second ( ) ,
270+ color : absolute. color ,
271+ } ) ;
272+ }
273+ }
274+
275+ // Sort by timestamp (descending order - newest first)
276+ timestamp_entries. sort_by ( |a, b| b. timestamp . cmp ( & a. timestamp ) ) ;
277+
278+ ( t. date . older , timestamp_entries)
279+ } else {
280+ ( Color :: Blue , Vec :: new ( ) )
281+ } ;
282+
283+ Self {
284+ theme,
285+ lscolors,
286+ default_date_color,
287+ timestamp_colors,
288+ }
213289 }
214290
215291 pub fn colorize < S : Into < String > > ( & self , input : S , elem : & Elem ) -> ColoredString {
@@ -250,7 +326,30 @@ impl Colors {
250326
251327 fn style_default ( & self , elem : & Elem ) -> ContentStyle {
252328 if let Some ( t) = & self . theme {
253- let style_fg = ContentStyle :: default ( ) . with ( elem. get_color ( t) ) ;
329+ let color = match elem {
330+ Elem :: Date ( timestamp) => {
331+ let mut color = self . default_date_color ;
332+
333+ for entry in & self . timestamp_colors {
334+ if * timestamp >= entry. timestamp {
335+ color = entry. color ;
336+ break ;
337+ }
338+ }
339+
340+ color
341+ }
342+ Elem :: InvalidDate => {
343+ // For invalid dates, use the oldest color if available, otherwise default
344+ self . timestamp_colors
345+ . last ( )
346+ . map ( |e| e. color )
347+ . unwrap_or ( self . default_date_color )
348+ }
349+ _ => elem. get_color ( t) ,
350+ } ;
351+
352+ let style_fg = ContentStyle :: default ( ) . with ( color) ;
254353 if elem. has_suid ( ) {
255354 style_fg. on ( Color :: AnsiValue ( 124 ) ) // Red3
256355 } else {
@@ -439,9 +538,11 @@ mod elem {
439538 special : Color :: AnsiValue ( 44 ) , // DarkTurquoise
440539 } ,
441540 date : color:: Date {
442- hour_old : Color :: AnsiValue ( 40 ) , // Green3
443- day_old : Color :: AnsiValue ( 42 ) , // SpringGreen2
444- older : Color :: AnsiValue ( 36 ) , // DarkCyan
541+ hour_old : Some ( Color :: AnsiValue ( 40 ) ) , // Green3
542+ day_old : Some ( Color :: AnsiValue ( 42 ) ) , // SpringGreen2
543+ older : Color :: AnsiValue ( 36 ) , // DarkCyan
544+ relative : Vec :: new ( ) ,
545+ absolute : Vec :: new ( ) ,
445546 } ,
446547 size : color:: Size {
447548 none : Color :: AnsiValue ( 245 ) , // Grey
0 commit comments