times.go (3239B)
1 package humanize 2 3 import ( 4 "fmt" 5 "math" 6 "sort" 7 "time" 8 ) 9 10 // Seconds-based time units 11 const ( 12 Day = 24 * time.Hour 13 Week = 7 * Day 14 Month = 30 * Day 15 Year = 12 * Month 16 LongTime = 37 * Year 17 ) 18 19 // Time formats a time into a relative string. 20 // 21 // Time(someT) -> "3 weeks ago" 22 func Time(then time.Time) string { 23 return RelTime(then, time.Now(), "ago", "from now") 24 } 25 26 // A RelTimeMagnitude struct contains a relative time point at which 27 // the relative format of time will switch to a new format string. A 28 // slice of these in ascending order by their "D" field is passed to 29 // CustomRelTime to format durations. 30 // 31 // The Format field is a string that may contain a "%s" which will be 32 // replaced with the appropriate signed label (e.g. "ago" or "from 33 // now") and a "%d" that will be replaced by the quantity. 34 // 35 // The DivBy field is the amount of time the time difference must be 36 // divided by in order to display correctly. 37 // 38 // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" 39 // DivBy should be time.Minute so whatever the duration is will be 40 // expressed in minutes. 41 type RelTimeMagnitude struct { 42 D time.Duration 43 Format string 44 DivBy time.Duration 45 } 46 47 var defaultMagnitudes = []RelTimeMagnitude{ 48 {time.Second, "now", time.Second}, 49 {2 * time.Second, "1 second %s", 1}, 50 {time.Minute, "%d seconds %s", time.Second}, 51 {2 * time.Minute, "1 minute %s", 1}, 52 {time.Hour, "%d minutes %s", time.Minute}, 53 {2 * time.Hour, "1 hour %s", 1}, 54 {Day, "%d hours %s", time.Hour}, 55 {2 * Day, "1 day %s", 1}, 56 {Week, "%d days %s", Day}, 57 {2 * Week, "1 week %s", 1}, 58 {Month, "%d weeks %s", Week}, 59 {2 * Month, "1 month %s", 1}, 60 {Year, "%d months %s", Month}, 61 {18 * Month, "1 year %s", 1}, 62 {2 * Year, "2 years %s", 1}, 63 {LongTime, "%d years %s", Year}, 64 {math.MaxInt64, "a long while %s", 1}, 65 } 66 67 // RelTime formats a time into a relative string. 68 // 69 // It takes two times and two labels. In addition to the generic time 70 // delta string (e.g. 5 minutes), the labels are used applied so that 71 // the label corresponding to the smaller time is applied. 72 // 73 // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" 74 func RelTime(a, b time.Time, albl, blbl string) string { 75 return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) 76 } 77 78 // CustomRelTime formats a time into a relative string. 79 // 80 // It takes two times two labels and a table of relative time formats. 81 // In addition to the generic time delta string (e.g. 5 minutes), the 82 // labels are used applied so that the label corresponding to the 83 // smaller time is applied. 84 func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { 85 lbl := albl 86 diff := b.Sub(a) 87 88 if a.After(b) { 89 lbl = blbl 90 diff = a.Sub(b) 91 } 92 93 n := sort.Search(len(magnitudes), func(i int) bool { 94 return magnitudes[i].D > diff 95 }) 96 97 if n >= len(magnitudes) { 98 n = len(magnitudes) - 1 99 } 100 mag := magnitudes[n] 101 args := []interface{}{} 102 escaped := false 103 for _, ch := range mag.Format { 104 if escaped { 105 switch ch { 106 case 's': 107 args = append(args, lbl) 108 case 'd': 109 args = append(args, diff/mag.DivBy) 110 } 111 escaped = false 112 } else { 113 escaped = ch == '%' 114 } 115 } 116 return fmt.Sprintf(mag.Format, args...) 117 }