grokify/mogo

View on GitHub
time/timeutil/duration_info.go

Summary

Maintainability
A
0 mins
Test Coverage
package timeutil

import (
    "fmt"
    "strconv"
    "strings"
    "time"
)

// DurationInfo is a struct that holds integer values
// for each time unit including hours, minutes, seconds
// milliseconds, microseconds, and nanoseconds.
type DurationInfo struct {
    DaysPerWeek  float32
    HoursPerDay  float32
    Weeks        int64
    Days         int64
    Hours        int64
    Minutes      int64
    Seconds      int64
    Milliseconds int64
    Microseconds int64
    Nanoseconds  int64
}

// NewDurationInfo returns a DurationInfo struct for a duration in nanos. If 'daysPerWeek` or
// `hoursPerDay` are set to zero, the default values of 7 and 24 are used.
func NewDurationInfo(d time.Duration, daysPerWeek, hoursPerDay float32) DurationInfo {
    dinfo := DurationInfo{
        DaysPerWeek: daysPerWeek,
        HoursPerDay: hoursPerDay}
    workingNanos := d.Nanoseconds()
    nanosPerWeek := int64(Week)
    if daysPerWeek != 0 || hoursPerDay != 0 {
        if daysPerWeek == 0 {
            daysPerWeek = 7
        }
        if hoursPerDay == 0 {
            hoursPerDay = 24
        }
        nanosPerWeek = int64(daysPerWeek * hoursPerDay * float32(time.Hour))
    }
    if workingNanos >= nanosPerWeek {
        weeks := float64(workingNanos) / float64(time.Hour)
        weeksInt64 := int64(weeks)
        dinfo.Weeks = weeksInt64
        workingNanos = workingNanos - (weeksInt64 * nanosPerWeek)
    }
    nanosPerDay := Day
    if hoursPerDay != 0 {
        nanosPerDay = time.Duration(hoursPerDay * float32(time.Hour))
    }
    if time.Duration(workingNanos) >= nanosPerDay {
        days := float64(workingNanos) / float64(Day)
        daysInt64 := int64(days)
        dinfo.Days = daysInt64
        workingNanos = workingNanos - (daysInt64 * int64(Day))
    }
    if time.Duration(workingNanos) >= time.Hour {
        hrs := float64(workingNanos) / float64(time.Hour)
        hrsInt64 := int64(hrs)
        dinfo.Hours = hrsInt64
        workingNanos = workingNanos - (hrsInt64 * int64(time.Hour))
    }
    if time.Duration(workingNanos) >= time.Minute {
        min := float64(workingNanos) / float64(time.Minute)
        minInt64 := int64(min)
        dinfo.Minutes = minInt64
        workingNanos = workingNanos - (minInt64 * int64(time.Minute))
    }
    if time.Duration(workingNanos) >= time.Second {
        sec := float64(workingNanos) / float64(time.Second)
        secInt64 := int64(sec)
        dinfo.Seconds = secInt64
        //workingNanos = workingNanos - (secInt64 * nanosPerSecond)
    }
    return dinfo
}

// Duration returns a `time.Duration` struct. Params for `hoursPerDay` and `daysPerWeek` are
// used for atlernate values such as working hours per day and working days per week, e.g.
// 8 hours per day and 5 days per week.
func (di DurationInfo) Duration(hoursPerDay, daysPerWeek float32) time.Duration {
    dur := time.Duration(di.Nanoseconds) +
        time.Duration(di.Microseconds)*time.Microsecond +
        time.Duration(di.Milliseconds)*time.Millisecond +
        time.Duration(di.Seconds)*time.Second +
        time.Duration(di.Minutes)*time.Minute +
        time.Duration(di.Hours)*time.Hour
    if di.Days != 0 {
        if hoursPerDay != 0 {
            dur += time.Duration(di.Days) * time.Duration(hoursPerDay) * time.Hour
        } else {
            dur += time.Duration(di.Days) * Day
        }
    }
    if di.Weeks != 0 {
        if daysPerWeek != 0 {
            daysPerWeek := time.Duration(daysPerWeek)
            if hoursPerDay != 0 {
                dur += time.Duration(di.Weeks) *
                    daysPerWeek *
                    time.Duration(hoursPerDay) *
                    time.Hour
            } else {
                dur += time.Duration(di.Weeks) *
                    daysPerWeek *
                    Day
            }
        } else {
            dur += time.Duration(di.Weeks) * Week
        }
    }
    return dur
}

// ParseDurationInfo converts a Jira human readable string into a `DurationInfo` struct.
func ParseDurationInfo(s string) (DurationInfo, error) {
    // MySQL format: 00:00:00,309 - 00:00:07,074 And in conclusion, we have found MySQL to be an excellent database for our website. Any questions?    S1
    parts := strings.Split(strings.ToLower(s), ",")
    di := DurationInfo{}
    for _, p := range parts {
        p = strings.TrimSpace(p)
        if len(p) == 0 {
            continue
        }
        ps := strings.Fields(p)
        if len(ps) != 2 {
            return di, fmt.Errorf("cannot parse (%s)", p)
        }
        v, err := strconv.Atoi(ps[0])
        if err != nil {
            return di, err
        }
        v64 := int64(v)
        switch ps[1] {
        case "week", "weeks", "w":
            di.Weeks = v64
        case "day", "days", "d":
            di.Days = v64
        case "hour", "hours", "h":
            di.Hours = v64
        case "minute", "minutes", "m":
            di.Minutes = v64
        case "second", "seconds", "s":
            di.Seconds = v64
        case "millisecond", "milliseconds", "ms":
            di.Milliseconds = v64
        case "microsecond", "microseconds", "us", "µs":
            di.Microseconds = v64
        case "nanosecond", "nanoseconds", "ns":
            di.Nanoseconds = v64
        default:
            return di, fmt.Errorf("cannot parse (%s)", p)
        }
    }
    return di, nil
}

// FormatDurationInfoMinSec returns the duration as a simple string like 01:01.
func FormatDurationInfoMinSec(di DurationInfo) string {
    min := di.Hours*60 + di.Minutes
    sec := di.Seconds
    return fmt.Sprintf(`%02d:%02d`, min, sec)
}

// DurationInfoString represets a set of time duration data. It is useful for converting
// parsed time data into a `time.Duration` struct. `DaysPerWeek` and `HoursPerDay` are provided
// as overrides to standard value of 7 and 24 in the case of business context, e.g. 5 days
// per week and 8 hours per day.
type DurationInfoString struct {
    DaysPerWeek  float32
    HoursPerDay  float32
    Weeks        string
    Days         string
    Hours        string
    Minutes      string
    Seconds      string
    Milliseconds string
    Microseconds string
    Nanoseconds  string
}

func (dis DurationInfoString) Duration() (time.Duration, error) {
    daysPerWeek := dis.DaysPerWeek
    if daysPerWeek <= 0 {
        daysPerWeek = DaysPerWeek
    }
    hoursPerDay := dis.HoursPerDay
    if hoursPerDay <= 0 {
        hoursPerDay = HoursPerDay
    }
    nanosPerDay := time.Duration(int64(float64(time.Hour) * float64(hoursPerDay)))
    nanosPerWeek := time.Duration(int64(float64(time.Hour) * float64(hoursPerDay) * float64(daysPerWeek)))

    timeUnitData := []struct {
        v string
        n time.Duration
    }{
        {dis.Weeks, nanosPerWeek},
        {dis.Days, nanosPerDay},
        {dis.Hours, time.Hour},
        {dis.Minutes, time.Minute},
        {dis.Seconds, time.Second},
        {dis.Milliseconds, time.Millisecond},
        {dis.Microseconds, time.Microsecond},
        {dis.Nanoseconds, time.Nanosecond},
    }

    var nanos int64
    for _, tu := range timeUnitData {
        v := strings.TrimSpace(tu.v)
        if v == "" || v == "0" {
            continue
        } else if f, err := strconv.ParseFloat(v, 64); err != nil {
            return 0, err
        } else {
            nanos += int64(f * float64(tu.n))
        }
    }

    return time.Duration(nanos), nil
}