Implement CustomRelTime
for #25
diff --git a/times.go b/times.go
index 7aa1642..81e443a 100644
--- a/times.go
+++ b/times.go
@@ -9,9 +9,7 @@
// Seconds-based time units
const (
- Minute = 60
- Hour = 60 * Minute
- Day = 24 * Hour
+ Day = 24 * time.Hour
Week = 7 * Day
Month = 30 * Day
Year = 12 * Month
@@ -25,46 +23,45 @@
return RelTime(then, time.Now(), "ago", "from now")
}
-// Magnitude stores one magnitude and the output format for this magnitude of relative time.
-type Magnitude struct {
- d time.Duration
- format string
- divby time.Duration
+// A RelTimeMagnitude struct contains a relative time point at which
+// the relative format of time will switch to a new format string. A
+// slice of these in ascending order by their "D" field is passed to
+// CustomRelTime to format durations.
+//
+// The Format field is a string that may contain a "%s" which will be
+// replaced with the appropriate signed label (e.g. "ago" or "from
+// now") and a "%d" that will be replaced by the quantity.
+//
+// The DivBy field is the amount of time the time difference must be
+// divided by in order to display correctly.
+//
+// e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
+// DivBy should be time.Minute so whatever the duration is will be
+// expressed in minutes.
+type RelTimeMagnitude struct {
+ D time.Duration
+ Format string
+ DivBy time.Duration
}
-// NewMagnitude returns a Magnitude object.
-//
-// d is the max number value of relative time for this magnitude.
-// divby is the divisor to turn input number value into expected unit.
-// format is the expected output format string.
-//
-// Also refer to RelTimeMagnitudes for examples.
-func NewMagnitude(d time.Duration, format string, divby time.Duration) Magnitude {
- return Magnitude{
- d: d,
- format: format,
- divby: divby,
- }
-}
-
-var defaultMagnitudes = []Magnitude{
- NewMagnitude(time.Second, "now", time.Second),
- NewMagnitude(2*time.Second, "1 second %s", time.Second),
- NewMagnitude(Minute*time.Second, "%d seconds %s", time.Second),
- NewMagnitude(2*Minute*time.Second, "1 minute %s", time.Second),
- NewMagnitude(Hour*time.Second, "%d minutes %s", Minute*time.Second),
- NewMagnitude(2*Hour*time.Second, "1 hour %s", time.Second),
- NewMagnitude(Day*time.Second, "%d hours %s", Hour*time.Second),
- NewMagnitude(2*Day*time.Second, "1 day %s", time.Second),
- NewMagnitude(Week*time.Second, "%d days %s", Day*time.Second),
- NewMagnitude(2*Week*time.Second, "1 week %s", time.Second),
- NewMagnitude(Month*time.Second, "%d weeks %s", Week*time.Second),
- NewMagnitude(2*Month*time.Second, "1 month %s", time.Second),
- NewMagnitude(Year*time.Second, "%d months %s", Month*time.Second),
- NewMagnitude(18*Month*time.Second, "1 year %s", time.Second),
- NewMagnitude(2*Year*time.Second, "2 years %s", time.Second),
- NewMagnitude(LongTime*time.Second, "%d years %s", Year*time.Second),
- NewMagnitude(math.MaxInt64, "a long while %s", time.Second),
+var defaultMagnitudes = []RelTimeMagnitude{
+ {time.Second, "now", time.Second},
+ {2 * time.Second, "1 second %s", 1},
+ {time.Minute, "%d seconds %s", time.Second},
+ {2 * time.Minute, "1 minute %s", 1},
+ {time.Hour, "%d minutes %s", time.Minute},
+ {2 * time.Hour, "1 hour %s", 1},
+ {Day, "%d hours %s", time.Hour},
+ {2 * Day, "1 day %s", 1},
+ {Week, "%d days %s", Day},
+ {2 * Week, "1 week %s", 1},
+ {Month, "%d weeks %s", Week},
+ {2 * Month, "1 month %s", 1},
+ {Year, "%d months %s", Month},
+ {18 * Month, "1 year %s", 1},
+ {2 * Year, "2 years %s", 1},
+ {LongTime, "%d years %s", Year},
+ {math.MaxInt64, "a long while %s", 1},
}
// RelTime formats a time into a relative string.
@@ -75,62 +72,45 @@
//
// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
func RelTime(a, b time.Time, albl, blbl string) string {
- return RelTimeMagnitudes(a, b, albl, blbl, defaultMagnitudes)
+ return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
}
-// RelTimeMagnitudes accepts a magnitudes parameter to allow custom defined units and output format.
+// CustomRelTime formats a time into a relative string.
//
-// example:
-// magitudes:
-// {
-// NewMagnitude(time.Second, "now", time.Second),
-// NewMagnitude(60*time.Second, "%d seconds %s", time.Second),
-// NewMagnitude(120*time.Second,"a minute %s", time.Second),
-// NewMagnitude(360*time.Second, "%d minutes %s", 60*time.Second),
-// }
-// albl: earlier
-// blbl: later
+// It takes two times two labels and a table of relative time formats.
+// In addition to the generic time delta string (e.g. 5 minutes), the
+// labels are used applied so that the label corresponding to the
+// smaller time is applied.
//
-// b - a output
-// -130*time.Second 2 minutes later
-// 0 now
-// 30*time.Second 30 seconds earlier
-// 80*time.Second a minute earlier
-// 340*time.Second 5 minutes earlier
-// 400*time.Second undefined
-func RelTimeMagnitudes(a, b time.Time, albl, blbl string, magnitudes []Magnitude) string {
+// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
+func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
lbl := albl
diff := b.Sub(a)
- after := a.After(b)
- if after {
+ if a.After(b) {
lbl = blbl
diff = a.Sub(b)
}
n := sort.Search(len(magnitudes), func(i int) bool {
- return magnitudes[i].d >= diff
+ return magnitudes[i].D >= diff
})
- if n >= len(magnitudes) {
- return "undefined"
- }
-
mag := magnitudes[n]
args := []interface{}{}
escaped := false
- for _, ch := range mag.format {
+ for _, ch := range mag.Format {
if escaped {
switch ch {
case 's':
args = append(args, lbl)
case 'd':
- args = append(args, diff/mag.divby)
+ args = append(args, diff/mag.DivBy)
}
escaped = false
} else {
escaped = ch == '%'
}
}
- return fmt.Sprintf(mag.format, args...)
+ return fmt.Sprintf(mag.Format, args...)
}
diff --git a/times_test.go b/times_test.go
index 314da59..bbbc741 100644
--- a/times_test.go
+++ b/times_test.go
@@ -7,59 +7,59 @@
)
func TestPast(t *testing.T) {
- now := time.Now().Unix()
+ now := time.Now()
testList{
- {"now", Time(time.Unix(now, 0)), "now"},
- {"1 second ago", Time(time.Unix(now-1, 0)), "1 second ago"},
- {"12 seconds ago", Time(time.Unix(now-12, 0)), "12 seconds ago"},
- {"30 seconds ago", Time(time.Unix(now-30, 0)), "30 seconds ago"},
- {"45 seconds ago", Time(time.Unix(now-45, 0)), "45 seconds ago"},
- {"1 minute ago", Time(time.Unix(now-63, 0)), "1 minute ago"},
- {"15 minutes ago", Time(time.Unix(now-15*Minute, 0)), "15 minutes ago"},
- {"1 hour ago", Time(time.Unix(now-63*Minute, 0)), "1 hour ago"},
- {"2 hours ago", Time(time.Unix(now-2*Hour, 0)), "2 hours ago"},
- {"21 hours ago", Time(time.Unix(now-21*Hour, 0)), "21 hours ago"},
- {"1 day ago", Time(time.Unix(now-26*Hour, 0)), "1 day ago"},
- {"2 days ago", Time(time.Unix(now-49*Hour, 0)), "2 days ago"},
- {"3 days ago", Time(time.Unix(now-3*Day, 0)), "3 days ago"},
- {"1 week ago (1)", Time(time.Unix(now-7*Day, 0)), "1 week ago"},
- {"1 week ago (2)", Time(time.Unix(now-12*Day, 0)), "1 week ago"},
- {"2 weeks ago", Time(time.Unix(now-15*Day, 0)), "2 weeks ago"},
- {"1 month ago", Time(time.Unix(now-39*Day, 0)), "1 month ago"},
- {"3 months ago", Time(time.Unix(now-99*Day, 0)), "3 months ago"},
- {"1 year ago (1)", Time(time.Unix(now-365*Day, 0)), "1 year ago"},
- {"1 year ago (1)", Time(time.Unix(now-400*Day, 0)), "1 year ago"},
- {"2 years ago (1)", Time(time.Unix(now-548*Day, 0)), "2 years ago"},
- {"2 years ago (2)", Time(time.Unix(now-725*Day, 0)), "2 years ago"},
- {"2 years ago (3)", Time(time.Unix(now-800*Day, 0)), "2 years ago"},
- {"3 years ago", Time(time.Unix(now-3*Year, 0)), "3 years ago"},
- {"long ago", Time(time.Unix(now-LongTime, 0)), "a long while ago"},
+ {"now", Time(now), "now"},
+ {"1 second ago", Time(now.Add(-1 * time.Second)), "1 second ago"},
+ {"12 seconds ago", Time(now.Add(-12 * time.Second)), "12 seconds ago"},
+ {"30 seconds ago", Time(now.Add(-30 * time.Second)), "30 seconds ago"},
+ {"45 seconds ago", Time(now.Add(-45 * time.Second)), "45 seconds ago"},
+ {"1 minute ago", Time(now.Add(-63 * time.Second)), "1 minute ago"},
+ {"15 minutes ago", Time(now.Add(-15 * time.Minute)), "15 minutes ago"},
+ {"1 hour ago", Time(now.Add(-63 * time.Minute)), "1 hour ago"},
+ {"2 hours ago", Time(now.Add(-2 * time.Hour)), "2 hours ago"},
+ {"21 hours ago", Time(now.Add(-21 * time.Hour)), "21 hours ago"},
+ {"1 day ago", Time(now.Add(-26 * time.Hour)), "1 day ago"},
+ {"2 days ago", Time(now.Add(-49 * time.Hour)), "2 days ago"},
+ {"3 days ago", Time(now.Add(-3 * Day)), "3 days ago"},
+ {"1 week ago (1)", Time(now.Add(-7 * Day)), "1 week ago"},
+ {"1 week ago (2)", Time(now.Add(-12 * Day)), "1 week ago"},
+ {"2 weeks ago", Time(now.Add(-15 * Day)), "2 weeks ago"},
+ {"1 month ago", Time(now.Add(-39 * Day)), "1 month ago"},
+ {"3 months ago", Time(now.Add(-99 * Day)), "3 months ago"},
+ {"1 year ago (1)", Time(now.Add(-365 * Day)), "1 year ago"},
+ {"1 year ago (1)", Time(now.Add(-400 * Day)), "1 year ago"},
+ {"2 years ago (1)", Time(now.Add(-548 * Day)), "2 years ago"},
+ {"2 years ago (2)", Time(now.Add(-725 * Day)), "2 years ago"},
+ {"2 years ago (3)", Time(now.Add(-800 * Day)), "2 years ago"},
+ {"3 years ago", Time(now.Add(-3 * Year)), "3 years ago"},
+ {"long ago", Time(now.Add(-LongTime)), "a long while ago"},
}.validate(t)
}
func TestFuture(t *testing.T) {
- now := time.Now().Unix()
- // add 1 second offset for test time to balance decimal fraction of time.Now()
- offset := int64(time.Second)
+ // Add a little time so that these things properly line up in
+ // the future.
+ now := time.Now().Add(time.Millisecond * 250)
testList{
- {"now", Time(time.Unix(now, 0)), "now"},
- {"1 second from now", Time(time.Unix(now+1, offset)), "1 second from now"},
- {"12 seconds from now", Time(time.Unix(now+12, offset)), "12 seconds from now"},
- {"30 seconds from now", Time(time.Unix(now+30, offset)), "30 seconds from now"},
- {"45 seconds from now", Time(time.Unix(now+45, offset)), "45 seconds from now"},
- {"15 minutes from now", Time(time.Unix(now+15*Minute, offset)), "15 minutes from now"},
- {"2 hours from now", Time(time.Unix(now+2*Hour, offset)), "2 hours from now"},
- {"21 hours from now", Time(time.Unix(now+21*Hour, offset)), "21 hours from now"},
- {"1 day from now", Time(time.Unix(now+26*Hour, offset)), "1 day from now"},
- {"2 days from now", Time(time.Unix(now+49*Hour, offset)), "2 days from now"},
- {"3 days from now", Time(time.Unix(now+3*Day, offset)), "3 days from now"},
- {"1 week from now (1)", Time(time.Unix(now+7*Day, offset)), "1 week from now"},
- {"1 week from now (2)", Time(time.Unix(now+12*Day, offset)), "1 week from now"},
- {"2 weeks from now", Time(time.Unix(now+15*Day, offset)), "2 weeks from now"},
- {"1 month from now", Time(time.Unix(now+30*Day, offset)), "1 month from now"},
- {"1 year from now", Time(time.Unix(now+365*Day, offset)), "1 year from now"},
- {"2 years from now", Time(time.Unix(now+2*Year, offset)), "2 years from now"},
- {"a while from now", Time(time.Unix(now+LongTime, offset)), "a long while from now"},
+ {"now", Time(now), "now"},
+ {"1 second from now", Time(now.Add(+1 * time.Second)), "1 second from now"},
+ {"12 seconds from now", Time(now.Add(+12 * time.Second)), "12 seconds from now"},
+ {"30 seconds from now", Time(now.Add(+30 * time.Second)), "30 seconds from now"},
+ {"45 seconds from now", Time(now.Add(+45 * time.Second)), "45 seconds from now"},
+ {"15 minutes from now", Time(now.Add(+15 * time.Minute)), "15 minutes from now"},
+ {"2 hours from now", Time(now.Add(+2 * time.Hour)), "2 hours from now"},
+ {"21 hours from now", Time(now.Add(+21 * time.Hour)), "21 hours from now"},
+ {"1 day from now", Time(now.Add(+26 * time.Hour)), "1 day from now"},
+ {"2 days from now", Time(now.Add(+49 * time.Hour)), "2 days from now"},
+ {"3 days from now", Time(now.Add(+3 * Day)), "3 days from now"},
+ {"1 week from now (1)", Time(now.Add(+7 * Day)), "1 week from now"},
+ {"1 week from now (2)", Time(now.Add(+12 * Day)), "1 week from now"},
+ {"2 weeks from now", Time(now.Add(+15 * Day)), "2 weeks from now"},
+ {"1 month from now", Time(now.Add(+30 * Day)), "1 month from now"},
+ {"1 year from now", Time(now.Add(+365 * Day)), "1 year from now"},
+ {"2 years from now", Time(now.Add(+2 * Year)), "2 years from now"},
+ {"a while from now", Time(now.Add(+LongTime)), "a long while from now"},
}.validate(t)
}
@@ -71,32 +71,3 @@
t.Errorf("Expected a long while from now, got %q", x)
}
}
-
-func TestRelTimeMagnitudes(t *testing.T) {
- magnitudes := []Magnitude{
- NewMagnitude(1*time.Second, "now", time.Second),
- NewMagnitude(2*time.Second, "1s %s", time.Second),
- NewMagnitude(Minute*time.Second, "s %s", time.Second),
- NewMagnitude(2*Minute*time.Second, "1m %s", time.Second),
- NewMagnitude(Hour*time.Second, "%dm %s", Minute*time.Second),
- NewMagnitude(2*Hour*time.Second, "1h %s", time.Second),
- NewMagnitude(Day*time.Second, "%dh %s", Hour*time.Second),
- NewMagnitude(2*Day*time.Second, "1D %s", time.Second),
- NewMagnitude(Month*time.Second, "%dD %s", Day*time.Second),
- NewMagnitude(2*Month*time.Second, "1M %s", time.Second),
- NewMagnitude(Year*time.Second, "%dM %s", Month*time.Second),
- NewMagnitude(18*Month*time.Second, "1Y %s", time.Second),
- NewMagnitude(2*Year*time.Second, "2Y %s", time.Second),
- }
- now := time.Now().Unix()
- timeNow := time.Unix(now, 0)
- testList{
- {"now", RelTimeMagnitudes(time.Unix(now, 0), timeNow, "ago", "later", magnitudes), "now"},
- {"1 second from now", RelTimeMagnitudes(time.Unix(now+1, 1), timeNow, "ago", "later", magnitudes), "1s later"},
- // Unit week has been removed from magnitudes
- {"1 week ago", RelTimeMagnitudes(time.Unix(now-12*Day, 0), timeNow, "ago", "", magnitudes), "12D ago"},
- {"3 months ago", RelTimeMagnitudes(time.Unix(now-99*Day, 0), timeNow, "ago", "later", magnitudes), "3M ago"},
- {"1 year ago", RelTimeMagnitudes(time.Unix(now-365*Day, 0), timeNow, "", "later", magnitudes), "1Y "},
- {"out of defined magnitudes", RelTimeMagnitudes(time.Unix(now+LongTime, 0), timeNow, "ago", "later", magnitudes), "undefined"},
- }.validate(t)
-}