How to format a duration

For example, to provide a custom format for a duration,

package main

import (
    "fmt"
    "time"
)

func fmtDuration(d time.Duration) string {
    d = d.Round(time.Minute)
    h := d / time.Hour
    d -= h * time.Hour
    m := d / time.Minute
    return fmt.Sprintf("%02d:%02d", h, m)
}

func main() {
    modTime := time.Now().Round(0).Add(-(3600 + 60 + 45) * time.Second)
    since := time.Since(modTime)
    fmt.Println(since)
    durStr := fmtDuration(since)
    fmt.Println(durStr)
}

Playground: https://play.golang.org/p/HT4bFfoA5r

Output:

1h1m45s
01:02

If you want to sort on a duration then use the Go sort package. I would sort on ModTime to defer the calculation of the duration, Since(ModTime), to be accurate at the time it is printed. For example,

package main

import (
    "fmt"
    "os"
    "path/filepath"
    "sort"
    "strings"
    "time"
)

func isVideo(path string) bool {
    videos := []string{".mp4", ".m4v", ".h264"}
    ext := strings.ToLower(filepath.Ext(path))
    for _, video := range videos {
        if ext == video {
            return true
        }
    }
    return false
}

type modTimeInfo struct {
    path    string
    modTime time.Time
}

func walkModTime(root string) ([]modTimeInfo, error) {
    var infos []modTimeInfo
    err := filepath.Walk(
        root,
        func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }
            if info.Mode().IsRegular() {
                path = filepath.Clean(path)
                if !isVideo(path) {
                    return nil
                }
                sep := string(filepath.Separator)
                dir := sep + `Videos` + sep
                path = strings.Replace(path, dir, sep, 1)
                infos = append(infos, modTimeInfo{
                    path:    path,
                    modTime: info.ModTime()},
                )
            }
            return nil
        },
    )
    if err != nil {
        return nil, err
    }
    return infos, nil
}

func sortModTime(infos []modTimeInfo) {
    sort.SliceStable(
        infos,
        func(i, j int) bool {
            return infos[i].modTime.Before(infos[j].modTime)
        },
    )
}

func fmtAge(d time.Duration) string {
    d = d.Round(time.Minute)
    h := d / time.Hour
    d -= h * time.Hour
    m := d / time.Minute
    return fmt.Sprintf("%02d:%02d", h, m)
}

func main() {
    root := `/home/peter/Videos` // Testing ...
    infos, err := walkModTime(root)
    if err != nil {
        fmt.Println(err)
        return
    }
    sortModTime(infos)
    now := time.Now()
    for _, info := range infos {
        age := fmtAge(now.Sub(info.modTime))
        fmt.Println("Age (H:M):", age, "File:", info.path)
    }
}

Playground: https://play.golang.org/p/j2TUmJdAi4


FYI, if you just want to quickly display a duration, the built-in formatting works well:

fmt.Sprintf("duration: %s", d)

will display something like this:

duration: 7h3m45s

Another way to format the duration if you don't care about the day, month or year

package main

import (
    "fmt"
    "time"
)

type Timespan time.Duration

func (t Timespan) Format(format string) string {
    z := time.Unix(0, 0).UTC()
    return z.Add(time.Duration(t)).Format(format)
}

func main() {
    dur := 7777 * time.Second
    fmt.Println(Timespan(dur).Format("15:04:05")) // 02:09:37
}

https://play.golang.org/p/XM-884oYMvE

Tags:

Go