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)
-}