diff --git a/date.go b/date.go index ed022dda..12808c38 100644 --- a/date.go +++ b/date.go @@ -94,47 +94,60 @@ func duration(sec interface{}) string { return (time.Duration(n) * time.Second).String() } +const ( + year = time.Hour * 24 * 365 + month = time.Hour * 24 * 30 + day = time.Hour * 24 +) + func durationRound(duration interface{}) string { var d time.Duration + switch duration := duration.(type) { default: - d = 0 + return "0s" case string: d, _ = time.ParseDuration(duration) + case int: + // We handle these cases similar to how `duration` does. + d = time.Duration(duration) * time.Second case int64: - d = time.Duration(duration) + // Considering the given value as seconds instead of nanoseconds might be a breaking + // change, but it is more consistent with the other cases and most likely closer to what + // the user expects. + d = time.Duration(duration) * time.Second + case float64: + d = time.Duration(duration) * time.Second case time.Time: d = time.Since(duration) + case time.Duration: + d = duration } - u := uint64(d) - neg := d < 0 - if neg { - u = -u + // Not sure if this actually makes much sense, but removing it would be a breaking change. + if d < 0 { + d = -d } - var ( - year = uint64(time.Hour) * 24 * 365 - month = uint64(time.Hour) * 24 * 30 - day = uint64(time.Hour) * 24 - hour = uint64(time.Hour) - minute = uint64(time.Minute) - second = uint64(time.Second) - ) - switch { - case u > year: - return strconv.FormatUint(u/year, 10) + "y" - case u > month: - return strconv.FormatUint(u/month, 10) + "mo" - case u > day: - return strconv.FormatUint(u/day, 10) + "d" - case u > hour: - return strconv.FormatUint(u/hour, 10) + "h" - case u > minute: - return strconv.FormatUint(u/minute, 10) + "m" - case u > second: - return strconv.FormatUint(u/second, 10) + "s" + if d > year { + return strconv.FormatInt(int64(d/year), 10) + "y" + } + if d > month { + return strconv.FormatInt(int64(d/month), 10) + "mo" } + if d > day { + return strconv.FormatInt(int64(d/day), 10) + "d" + } + if d > time.Hour { + return strconv.FormatInt(int64(d/time.Hour), 10) + "h" + } + if d > time.Minute { + return strconv.FormatInt(int64(d/time.Minute), 10) + "m" + } + if d > time.Second { + return strconv.FormatInt(int64(d/time.Second), 10) + "s" + } + return "0s" } diff --git a/date_test.go b/date_test.go index be7ec9d9..8cd85d77 100644 --- a/date_test.go +++ b/date_test.go @@ -104,6 +104,9 @@ func TestDuration(t *testing.T) { if err := runtv(tpl, "26h3m4s", map[string]interface{}{"Secs": "93784"}); err != nil { t.Error(err) } + if err := runtv(tpl, "0s", map[string]interface{}{"Secs": 90 * time.Second}); err != nil { + t.Error(err) + } } func TestDurationRound(t *testing.T) { @@ -117,4 +120,25 @@ func TestDurationRound(t *testing.T) { if err := runtv(tpl, "3mo", map[string]interface{}{"Time": "2400h5s"}); err != nil { t.Error(err) } + if err := runtv(tpl, "0s", map[string]interface{}{"Time": "unparseable"}); err != nil { + t.Error(err) + } + if err := runtv(tpl, "1m", map[string]interface{}{"Time": 90 * time.Second}); err != nil { + t.Error(err) + } + if err := runtv(tpl, "3y", map[string]interface{}{"Time": time.Date(2020, time.March, 5, 11, 11, 11, 0, time.UTC)}); err != nil { + t.Error(err) + } + if err := runtv(tpl, "25s", map[string]interface{}{"Time": 25.5}); err != nil { + t.Error(err) + } + if err := runtv(tpl, "0s", map[string]interface{}{"Time": byte(25)}); err != nil { + t.Error(err) + } + if err := runtv(tpl, "2m", map[string]interface{}{"Time": 150}); err != nil { + t.Error(err) + } + if err := runtv(tpl, "5s", map[string]interface{}{"Time": -5 * time.Second}); err != nil { + t.Error(err) + } } diff --git a/docs/date.md b/docs/date.md index 9f02651e..22270b44 100644 --- a/docs/date.md +++ b/docs/date.md @@ -51,27 +51,32 @@ dateInZone "2006-01-02" (now) "UTC" Formats a given amount of seconds as a `time.Duration`. -This returns 1m35s - ``` duration "95" + +# Yields 1m35s ``` ## durationRound -Rounds a given duration to the most significant unit. Strings and `time.Duration` -gets parsed as a duration, while a `time.Time` is calculated as the duration since. +Rounds a given duration to the most significant unit. +It always round down, e.g. `durationRound "2h10m5s"` yields `2h` - making it more a `floor` than a `round`. -This return 2h +`durationRound` accepts `time.Duration` as input; `string` gets parsed into `time.Duration`, `int`, `int64` and +`float64` are read as durations in seconds. +When `time.Time` is given the difference between `time.Now()` and the given time is used as duration. + ``` durationRound "2h10m5s" -``` -This returns 3mo +# Yields 2h +``` ``` durationRound "2400h10m5s" + +# Yields 3mo ``` ## unixEpoch