diff --git a/duration.go b/duration.go new file mode 100644 index 0000000..080257f --- /dev/null +++ b/duration.go @@ -0,0 +1,62 @@ +package humanize + +import ( + "strconv" + "strings" + "time" +) + +type format struct { + key string + unit time.Duration + name string +} + +// Duration produces a formated duration as a string +// it supports the following format flags: +// %y - year +// %m - month +// %w - week +// %H - hour +// %M - minute +// %S - second +func Duration(format string, d time.Duration) string { + if format == "" { + format = "%y %m %w %d %H %M %S" + } + if d < 0 { + format = "-" + format + d *= -1 + } + process := func(f string, unit time.Duration, name string) { + if strings.Contains(format, f) { + segment := int(d / unit) + format = strings.Replace(format, f, pluralize(segment, name), -1) + d %= unit + } + } + + process("%y", 365*Day, "year") + process("%m", Month, "month") + process("%w", Week, "week") + process("%d", Day, "day") + process("%H", time.Hour, "hour") + process("%M", time.Minute, "minute") + process("%S", time.Second, "second") + + // cleanup spaces + format = strings.Trim(format, " ") + return strings.Replace(format, " ", " ", -1) +} + +func pluralize(i int, s string) string { + if i == 0 { + return "" + } + s = strconv.Itoa(i) + " " + s + + if i > 1 { + s += "s" + } + return s +} diff --git a/duration_test.go b/duration_test.go new file mode 100644 index 0000000..b680006 --- /dev/null +++ b/duration_test.go @@ -0,0 +1,62 @@ +package humanize + +import ( + "testing" + "time" +) + +func TestDuration(t *testing.T) { + type test struct { + format string + d time.Duration + expected string + } + cases := map[string]test{ + "default": { + format: "%y %m %w %d %H %M %S", + d: 400*Day + 12*time.Hour + 17*time.Second, + expected: "1 year 1 month 5 days 12 hours 17 seconds", + }, + "negative": { + format: "%y %m %w %d %H %M %S", + d: -(400*Day + 12*time.Hour + 17*time.Second), + expected: "-1 year 1 month 5 days 12 hours 17 seconds", + }, + "years": { + format: "%y", + d: 800 * Day, + expected: "2 years", + }, + "zero": { + format: "%y", + d: 0, + expected: "", + }, + "neg years": { + format: "%y", + d: -800 * Day, + expected: "-2 years", + }, + "year + week": { + format: "%y %w", + d: 364 * Day, + expected: "52 weeks", + }, + "month + hours": { + format: "%w %H", + d: 15 * Day, + expected: "2 weeks 24 hours", + }, + "seconds only": { + format: "%S", + d: Day, + expected: "86400 seconds", + }, + } + for name, v := range cases { + result := Duration(v.format, v.d) + if result != v.expected { + t.Errorf("FAIL:%s\n\tExpected:%s\n\tActual:%s", name, v.expected, result) + } + } +}