Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: massively improve durationRound function, add support for durations #364

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 40 additions & 27 deletions date.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand Down
24 changes: 24 additions & 0 deletions date_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
}
}
19 changes: 12 additions & 7 deletions docs/date.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down