1// This code includes software originally developed by Dustin Sallings.
2//
3// Copyright (c) 2005-2008 Dustin Sallings <dustin@spy.net>
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22//
23//<http://www.opensource.org/licenses/mit-license.php>
24
25package humanize
26
27import (
28 "fmt"
29 "math"
30 "sort"
31 "time"
32)
33
34// Seconds-based time units
35const (
36 Day = 24 * time.Hour
37 Week = 7 * Day
38 Month = 30 * Day
39 Year = 12 * Month
40 LongTime = 37 * Year
41)
42
43// Time formats a time into a relative string.
44//
45// Time(someT) -> "3 weeks ago"
46func Time(then time.Time) string {
47 return RelTime(then, time.Now(), "ago", "from now")
48}
49
50// A RelTimeMagnitude struct contains a relative time point at which
51// the relative format of time will switch to a new format string. A
52// slice of these in ascending order by their "D" field is passed to
53// CustomRelTime to format durations.
54//
55// The Format field is a string that may contain a "%s" which will be
56// replaced with the appropriate signed label (e.g. "ago" or "from
57// now") and a "%d" that will be replaced by the quantity.
58//
59// The DivBy field is the amount of time the time difference must be
60// divided by in order to display correctly.
61//
62// e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
63// DivBy should be time.Minute so whatever the duration is will be
64// expressed in minutes.
65type RelTimeMagnitude struct {
66 D time.Duration
67 Format string
68 DivBy time.Duration
69}
70
71var defaultMagnitudes = []RelTimeMagnitude{
72 {time.Second, "now", time.Second},
73 {2 * time.Second, "1 second %s", 1},
74 {time.Minute, "%d seconds %s", time.Second},
75 {2 * time.Minute, "1 minute %s", 1},
76 {time.Hour, "%d minutes %s", time.Minute},
77 {2 * time.Hour, "1 hour %s", 1},
78 {Day, "%d hours %s", time.Hour},
79 {2 * Day, "1 day %s", 1},
80 {Week, "%d days %s", Day},
81 {2 * Week, "1 week %s", 1},
82 {Month, "%d weeks %s", Week},
83 {2 * Month, "1 month %s", 1},
84 {Year, "%d months %s", Month},
85 {18 * Month, "1 year %s", 1},
86 {2 * Year, "2 years %s", 1},
87 {LongTime, "%d years %s", Year},
88 {math.MaxInt64, "a long while %s", 1},
89}
90
91// RelTime formats a time into a relative string.
92//
93// It takes two times and two labels. In addition to the generic time
94// delta string (e.g. 5 minutes), the labels are used applied so that
95// the label corresponding to the smaller time is applied.
96//
97// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
98func RelTime(a, b time.Time, albl, blbl string) string {
99 return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
100}
101
102// CustomRelTime formats a time into a relative string.
103//
104// It takes two times two labels and a table of relative time formats.
105// In addition to the generic time delta string (e.g. 5 minutes), the
106// labels are used applied so that the label corresponding to the
107// smaller time is applied.
108func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
109 lbl := albl
110 diff := b.Sub(a)
111
112 if a.After(b) {
113 lbl = blbl
114 diff = a.Sub(b)
115 }
116
117 n := sort.Search(len(magnitudes), func(i int) bool {
118 return magnitudes[i].D > diff
119 })
120
121 if n >= len(magnitudes) {
122 n = len(magnitudes) - 1
123 }
124 mag := magnitudes[n]
125 args := []interface{}{}
126 escaped := false
127 for _, ch := range mag.Format {
128 if escaped {
129 switch ch {
130 case 's':
131 args = append(args, lbl)
132 case 'd':
133 args = append(args, diff/mag.DivBy)
134 }
135 escaped = false
136 } else {
137 escaped = ch == '%'
138 }
139 }
140 return fmt.Sprintf(mag.Format, args...)
141}