@@ -43,12 +43,14 @@ const AUTHOR: usize = 7;
4343const AUTHOR_EMAIL : usize = 8 ;
4444const AUTHOR_DATE : usize = 9 ;
4545const AUTHOR_DATE_SHORT : usize = 10 ;
46- const COMMITTER : usize = 11 ;
47- const COMMITTER_EMAIL : usize = 12 ;
48- const COMMITTER_DATE : usize = 13 ;
49- const COMMITTER_DATE_SHORT : usize = 14 ;
50- const BODY : usize = 15 ;
51- const BODY_RAW : usize = 16 ;
46+ const AUTHOR_DATE_RELATIVE : usize = 11 ;
47+ const COMMITTER : usize = 12 ;
48+ const COMMITTER_EMAIL : usize = 13 ;
49+ const COMMITTER_DATE : usize = 14 ;
50+ const COMMITTER_DATE_SHORT : usize = 15 ;
51+ const COMMITTER_DATE_RELATIVE : usize = 16 ;
52+ const BODY : usize = 17 ;
53+ const BODY_RAW : usize = 18 ;
5254
5355const MODE_SPACE : usize = 1 ;
5456const MODE_PLUS : usize = 2 ;
@@ -57,8 +59,8 @@ const MODE_MINUS: usize = 3;
5759lazy_static ! {
5860 pub static ref PLACEHOLDERS : Vec <[ String ; 4 ] > = {
5961 let base = vec![
60- "n" , "H" , "h" , "P" , "p" , "d" , "s" , "an" , "ae" , "ad" , "as" , "cn " , "ce " , "cd " , "cs" , "b ",
61- "B" ,
62+ "n" , "H" , "h" , "P" , "p" , "d" , "s" , "an" , "ae" , "ad" , "as" , "ar " , "cn " , "ce " , "cd " ,
63+ "cs" , "cr" , "b" , " B",
6264 ] ;
6365 base. iter( )
6466 . map( |b| {
@@ -260,6 +262,14 @@ pub fn format_commit(
260262 }
261263 write ! ( out, "{}" , format_date( commit. author( ) . when( ) , "%F" ) )
262264 }
265+ AUTHOR_DATE_RELATIVE => {
266+ match mode {
267+ MODE_SPACE => write ! ( out, " " ) . unwrap ( ) ,
268+ MODE_PLUS => add_line ( & mut lines, & mut out, wrapping) ,
269+ _ => { }
270+ }
271+ write ! ( out, "{}" , format_relative_time( commit. author( ) . when( ) ) )
272+ }
263273 COMMITTER => {
264274 match mode {
265275 MODE_SPACE => write ! ( out, " " ) . unwrap ( ) ,
@@ -296,6 +306,14 @@ pub fn format_commit(
296306 }
297307 write ! ( out, "{}" , format_date( commit. committer( ) . when( ) , "%F" ) )
298308 }
309+ COMMITTER_DATE_RELATIVE => {
310+ match mode {
311+ MODE_SPACE => write ! ( out, " " ) . unwrap ( ) ,
312+ MODE_PLUS => add_line ( & mut lines, & mut out, wrapping) ,
313+ _ => { }
314+ }
315+ write ! ( out, "{}" , format_relative_time( commit. committer( ) . when( ) ) )
316+ }
299317 BODY => {
300318 let message = commit
301319 . message ( )
@@ -517,6 +535,38 @@ pub fn format_date(time: Time, format: &str) -> String {
517535 format ! ( "{}" , date. format( format) )
518536}
519537
538+ /// Format a time as a relative time string (e.g., "21 hours ago", "4 days ago")
539+ pub fn format_relative_time ( time : Time ) -> String {
540+ let commit_time =
541+ Local :: from_offset ( & FixedOffset :: east ( time. offset_minutes ( ) ) ) . timestamp ( time. seconds ( ) , 0 ) ;
542+ let now = Local :: now ( ) ;
543+ let duration = now. signed_duration_since ( commit_time) ;
544+
545+ let seconds = duration. num_seconds ( ) ;
546+ let minutes = duration. num_minutes ( ) ;
547+ let hours = duration. num_hours ( ) ;
548+ let days = duration. num_hours ( ) / 24 ;
549+ let weeks = days / 7 ;
550+ let months = days / 30 ;
551+ let years = days / 365 ;
552+
553+ if seconds < 60 {
554+ format ! ( "{} seconds ago" , seconds)
555+ } else if minutes < 60 {
556+ format ! ( "{} minutes ago" , minutes)
557+ } else if hours < 24 {
558+ format ! ( "{} hours ago" , hours)
559+ } else if days < 7 {
560+ format ! ( "{} days ago" , days)
561+ } else if weeks < 4 {
562+ format ! ( "{} weeks ago" , weeks)
563+ } else if months < 12 {
564+ format ! ( "{} months ago" , months)
565+ } else {
566+ format ! ( "{} years ago" , years)
567+ }
568+ }
569+
520570fn append_wrapped ( vec : & mut Vec < String > , str : String , wrapping : & Option < Options > ) {
521571 if str. is_empty ( ) {
522572 vec. push ( str) ;
0 commit comments