first commit
This commit is contained in:
340
pkg/duration_fmt/fmt.go
Normal file
340
pkg/duration_fmt/fmt.go
Normal file
@ -0,0 +1,340 @@
|
||||
package duration_fmt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
//units, _ = DefaultUnitsCoder.Decode("year,week,day,hour,minute,second,millisecond,microsecond")
|
||||
units, _ = DefaultUnitsCoder.Decode("年,星期,天,小时,分钟,秒,毫秒,微秒")
|
||||
unitsShort = []string{"y", "w", "d", "h", "m", "s", "ms", "µs"}
|
||||
)
|
||||
|
||||
// Durafmt holds the parsed duration and the original input duration.
|
||||
type Durafmt struct {
|
||||
duration time.Duration
|
||||
input string // Used as reference.
|
||||
limitN int // Non-zero to limit only first N elements to output.
|
||||
limitUnit string // Non-empty to limit max unit
|
||||
}
|
||||
|
||||
// LimitToUnit sets the output format, you will not have unit bigger than the UNIT specified. UNIT = "" means no restriction.
|
||||
func (d *Durafmt) LimitToUnit(unit string) *Durafmt {
|
||||
d.limitUnit = unit
|
||||
return d
|
||||
}
|
||||
|
||||
// LimitFirstN sets the output format, outputing only first N elements. n == 0 means no limit.
|
||||
func (d *Durafmt) LimitFirstN(n int) *Durafmt {
|
||||
d.limitN = n
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Durafmt) Duration() time.Duration {
|
||||
return d.duration
|
||||
}
|
||||
|
||||
// Truncate sets precision
|
||||
func (d *Durafmt) Truncate(unit time.Duration) *Durafmt {
|
||||
d.duration = d.duration.Truncate(unit)
|
||||
return d
|
||||
}
|
||||
|
||||
// Parse creates a new *Durafmt struct, returns error if input is invalid.
|
||||
func Parse(dinput time.Duration) *Durafmt {
|
||||
input := dinput.String()
|
||||
return &Durafmt{dinput, input, 0, ""}
|
||||
}
|
||||
|
||||
// ParseShort creates a new *Durafmt struct, short form, returns error if input is invalid.
|
||||
// It's shortcut for `Parse(dur).LimitFirstN(1)`
|
||||
func ParseShort(dinput time.Duration) *Durafmt {
|
||||
input := dinput.String()
|
||||
return &Durafmt{dinput, input, 1, ""}
|
||||
}
|
||||
|
||||
// ParseString creates a new *Durafmt struct from a string.
|
||||
// returns an error if input is invalid.
|
||||
func ParseString(input string) (*Durafmt, error) {
|
||||
if input == "0" || input == "-0" {
|
||||
return nil, errors.New("durafmt: missing unit in duration " + input)
|
||||
}
|
||||
duration, err := time.ParseDuration(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Durafmt{duration, input, 0, ""}, nil
|
||||
}
|
||||
|
||||
// ParseStringShort creates a new *Durafmt struct from a string, short form
|
||||
// returns an error if input is invalid.
|
||||
// It's shortcut for `ParseString(durStr)` and then calling `LimitFirstN(1)`
|
||||
func ParseStringShort(input string) (*Durafmt, error) {
|
||||
if input == "0" || input == "-0" {
|
||||
return nil, errors.New("durafmt: missing unit in duration " + input)
|
||||
}
|
||||
duration, err := time.ParseDuration(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Durafmt{duration, input, 1, ""}, nil
|
||||
}
|
||||
|
||||
// String parses d *Durafmt into a human readable duration with default units.
|
||||
func (d *Durafmt) String() string {
|
||||
return d.Format(units)
|
||||
}
|
||||
|
||||
// Format parses d *Durafmt into a human readable duration with units.
|
||||
func (d *Durafmt) Format(units Units) string {
|
||||
var duration string
|
||||
|
||||
// Check for minus durations.
|
||||
if string(d.input[0]) == "-" {
|
||||
duration += "-"
|
||||
d.duration = -d.duration
|
||||
}
|
||||
|
||||
var microseconds int64
|
||||
var milliseconds int64
|
||||
var seconds int64
|
||||
var minutes int64
|
||||
var hours int64
|
||||
var days int64
|
||||
var weeks int64
|
||||
var years int64
|
||||
var shouldConvert = false
|
||||
|
||||
remainingSecondsToConvert := int64(d.duration / time.Microsecond)
|
||||
|
||||
// Convert duration.
|
||||
if d.limitUnit == "" {
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "years" || shouldConvert {
|
||||
years = remainingSecondsToConvert / (365 * 24 * 3600 * 1000000)
|
||||
remainingSecondsToConvert -= years * 365 * 24 * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "weeks" || shouldConvert {
|
||||
weeks = remainingSecondsToConvert / (7 * 24 * 3600 * 1000000)
|
||||
remainingSecondsToConvert -= weeks * 7 * 24 * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "days" || shouldConvert {
|
||||
days = remainingSecondsToConvert / (24 * 3600 * 1000000)
|
||||
remainingSecondsToConvert -= days * 24 * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "hours" || shouldConvert {
|
||||
hours = remainingSecondsToConvert / (3600 * 1000000)
|
||||
remainingSecondsToConvert -= hours * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "minutes" || shouldConvert {
|
||||
minutes = remainingSecondsToConvert / (60 * 1000000)
|
||||
remainingSecondsToConvert -= minutes * 60 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "seconds" || shouldConvert {
|
||||
seconds = remainingSecondsToConvert / 1000000
|
||||
remainingSecondsToConvert -= seconds * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "milliseconds" || shouldConvert {
|
||||
milliseconds = remainingSecondsToConvert / 1000
|
||||
remainingSecondsToConvert -= milliseconds * 1000
|
||||
}
|
||||
|
||||
microseconds = remainingSecondsToConvert
|
||||
|
||||
// Create a map of the converted duration time.
|
||||
durationMap := []int64{
|
||||
microseconds,
|
||||
milliseconds,
|
||||
seconds,
|
||||
minutes,
|
||||
hours,
|
||||
days,
|
||||
weeks,
|
||||
years,
|
||||
}
|
||||
|
||||
// Construct duration string.
|
||||
for i, u := range units.Units() {
|
||||
v := durationMap[7-i]
|
||||
strval := strconv.FormatInt(v, 10)
|
||||
switch {
|
||||
// add to the duration string if v > 1.
|
||||
case v > 1:
|
||||
duration += strval + " " + u.Plural + " "
|
||||
// remove the plural 's', if v is 1.
|
||||
case v == 1:
|
||||
duration += strval + " " + u.Singular + " "
|
||||
// omit any value with 0s or 0.
|
||||
case d.duration.String() == "0" || d.duration.String() == "0s":
|
||||
pattern := fmt.Sprintf("^-?0%s$", unitsShort[i])
|
||||
isMatch, err := regexp.MatchString(pattern, d.input)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if isMatch {
|
||||
duration += strval + " " + u.Plural
|
||||
}
|
||||
|
||||
// omit any value with 0.
|
||||
case v == 0:
|
||||
continue
|
||||
}
|
||||
}
|
||||
// trim any remaining spaces.
|
||||
duration = strings.TrimSpace(duration)
|
||||
|
||||
// if more than 2 spaces present return the first 2 strings
|
||||
// if short version is requested
|
||||
if d.limitN > 0 {
|
||||
parts := strings.Split(duration, " ")
|
||||
if len(parts) > d.limitN*2 {
|
||||
duration = strings.Join(parts[:d.limitN*2], " ")
|
||||
}
|
||||
}
|
||||
|
||||
return duration
|
||||
}
|
||||
|
||||
func (d *Durafmt) InternationalString() string {
|
||||
var duration string
|
||||
|
||||
// Check for minus durations.
|
||||
if string(d.input[0]) == "-" {
|
||||
duration += "-"
|
||||
d.duration = -d.duration
|
||||
}
|
||||
|
||||
var microseconds int64
|
||||
var milliseconds int64
|
||||
var seconds int64
|
||||
var minutes int64
|
||||
var hours int64
|
||||
var days int64
|
||||
var weeks int64
|
||||
var years int64
|
||||
var shouldConvert = false
|
||||
|
||||
remainingSecondsToConvert := int64(d.duration / time.Microsecond)
|
||||
|
||||
// Convert duration.
|
||||
if d.limitUnit == "" {
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "years" || shouldConvert {
|
||||
years = remainingSecondsToConvert / (365 * 24 * 3600 * 1000000)
|
||||
remainingSecondsToConvert -= years * 365 * 24 * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "weeks" || shouldConvert {
|
||||
weeks = remainingSecondsToConvert / (7 * 24 * 3600 * 1000000)
|
||||
remainingSecondsToConvert -= weeks * 7 * 24 * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "days" || shouldConvert {
|
||||
days = remainingSecondsToConvert / (24 * 3600 * 1000000)
|
||||
remainingSecondsToConvert -= days * 24 * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "hours" || shouldConvert {
|
||||
hours = remainingSecondsToConvert / (3600 * 1000000)
|
||||
remainingSecondsToConvert -= hours * 3600 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "minutes" || shouldConvert {
|
||||
minutes = remainingSecondsToConvert / (60 * 1000000)
|
||||
remainingSecondsToConvert -= minutes * 60 * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "seconds" || shouldConvert {
|
||||
seconds = remainingSecondsToConvert / 1000000
|
||||
remainingSecondsToConvert -= seconds * 1000000
|
||||
shouldConvert = true
|
||||
}
|
||||
|
||||
if d.limitUnit == "milliseconds" || shouldConvert {
|
||||
milliseconds = remainingSecondsToConvert / 1000
|
||||
remainingSecondsToConvert -= milliseconds * 1000
|
||||
}
|
||||
|
||||
microseconds = remainingSecondsToConvert
|
||||
|
||||
// Create a map of the converted duration time.
|
||||
durationMap := map[string]int64{
|
||||
"µs": microseconds,
|
||||
"ms": milliseconds,
|
||||
"s": seconds,
|
||||
"m": minutes,
|
||||
"h": hours,
|
||||
"d": days,
|
||||
"w": weeks,
|
||||
"y": years,
|
||||
}
|
||||
|
||||
// Construct duration string.
|
||||
for i := range units.Units() {
|
||||
u := unitsShort[i]
|
||||
v := durationMap[u]
|
||||
strval := strconv.FormatInt(v, 10)
|
||||
switch {
|
||||
// add to the duration string if v > 0.
|
||||
case v > 0:
|
||||
duration += strval + " " + u + " "
|
||||
// omit any value with 0.
|
||||
case d.duration.String() == "0":
|
||||
pattern := fmt.Sprintf("^-?0%s$", unitsShort[i])
|
||||
isMatch, err := regexp.MatchString(pattern, d.input)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if isMatch {
|
||||
duration += strval + " " + u
|
||||
}
|
||||
|
||||
// omit any value with 0.
|
||||
case v == 0:
|
||||
continue
|
||||
}
|
||||
}
|
||||
// trim any remaining spaces.
|
||||
duration = strings.TrimSpace(duration)
|
||||
|
||||
// if more than 2 spaces present return the first 2 strings
|
||||
// if short version is requested
|
||||
if d.limitN > 0 {
|
||||
parts := strings.Split(duration, " ")
|
||||
if len(parts) > d.limitN*2 {
|
||||
duration = strings.Join(parts[:d.limitN*2], " ")
|
||||
}
|
||||
}
|
||||
|
||||
return duration
|
||||
}
|
||||
|
||||
func (d *Durafmt) TrimSpace() string {
|
||||
return strings.Replace(d.String(), " ", "", -1)
|
||||
}
|
107
pkg/duration_fmt/units.go
Normal file
107
pkg/duration_fmt/units.go
Normal file
@ -0,0 +1,107 @@
|
||||
package duration_fmt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DefaultUnitsCoder default units coder using `":"` as PluralSep and `","` as UnitsSep
|
||||
var DefaultUnitsCoder = UnitsCoder{":", ","}
|
||||
|
||||
// Unit the pair of singular and plural units
|
||||
type Unit struct {
|
||||
Singular, Plural string
|
||||
}
|
||||
|
||||
// Units duration units
|
||||
type Units struct {
|
||||
Year, Week, Day, Hour, Minute,
|
||||
Second, Millisecond, Microsecond Unit
|
||||
}
|
||||
|
||||
// Units return a slice of units
|
||||
func (u Units) Units() []Unit {
|
||||
return []Unit{u.Year, u.Week, u.Day, u.Hour, u.Minute,
|
||||
u.Second, u.Millisecond, u.Microsecond}
|
||||
}
|
||||
|
||||
// UnitsCoder the units encoder and decoder
|
||||
type UnitsCoder struct {
|
||||
// PluralSep char to sep singular and plural pair.
|
||||
// Example with char `":"`: `"year:year"` (english) or `"mês:meses"` (portuguese)
|
||||
PluralSep,
|
||||
// UnitsSep char to sep units (singular and plural pairs).
|
||||
// Example with char `","`: `"year:year,week:weeks"` (english) or `"mês:meses,semana:semanas"` (portuguese)
|
||||
UnitsSep string
|
||||
}
|
||||
|
||||
// Encode encodes input Units to string
|
||||
// Examples with `UnitsCoder{PluralSep: ":", UnitsSep = ","}`
|
||||
// - singular and plural pair units: `"year:wers,week:weeks,day:days,hour:hours,minute:minutes,second:seconds,millisecond:millliseconds,microsecond:microsseconds"`
|
||||
func (coder UnitsCoder) Encode(units Units) string {
|
||||
var pairs = make([]string, 8)
|
||||
for i, u := range units.Units() {
|
||||
pairs[i] = u.Singular + coder.PluralSep + u.Plural
|
||||
}
|
||||
return strings.Join(pairs, coder.UnitsSep)
|
||||
}
|
||||
|
||||
// Decode decodes input string to Units.
|
||||
// The input must follow the following formats:
|
||||
// - Unit format (singular and plural pair)
|
||||
// - must singular (the plural receives 's' character as suffix)
|
||||
// - singular and plural: separated by `PluralSep` char
|
||||
// Example with char `":"`: `"year:year"` (english) or `"mês:meses"` (portuguese)
|
||||
// - Units format (pairs of Year, Week, Day, Hour, Minute,
|
||||
// Second, Millisecond and Microsecond units) separated by `UnitsSep` char
|
||||
// - Examples with `UnitsCoder{PluralSep: ":", UnitsSep = ","}`
|
||||
// - must singular units: `"year,week,day,hour,minute,second,millisecond,microsecond"`
|
||||
// - mixed units: `"year,week:weeks,day,hour,minute:minutes,second,millisecond,microsecond"`
|
||||
// - singular and plural pair units: `"year:wers,week:weeks,day:days,hour:hours,minute:minutes,second:seconds,millisecond:millliseconds,microsecond:microsseconds"`
|
||||
func (coder UnitsCoder) Decode(s string) (units Units, err error) {
|
||||
parts := strings.Split(s, coder.UnitsSep)
|
||||
if len(parts) != 8 {
|
||||
err = fmt.Errorf("bad parts length")
|
||||
return units, err
|
||||
}
|
||||
|
||||
var parse = func(name, part string, u *Unit) bool {
|
||||
ps := strings.Split(part, coder.PluralSep)
|
||||
switch len(ps) {
|
||||
case 1:
|
||||
u.Singular, u.Plural = ps[0], ps[0]
|
||||
case 2:
|
||||
u.Singular, u.Plural = ps[0], ps[1]
|
||||
default:
|
||||
err = fmt.Errorf("bad unit %q pair length", name)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if !parse("Year", parts[0], &units.Year) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Week", parts[1], &units.Week) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Day", parts[2], &units.Day) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Hour", parts[3], &units.Hour) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Minute", parts[4], &units.Minute) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Second", parts[5], &units.Second) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Millisecond", parts[6], &units.Millisecond) {
|
||||
return units, err
|
||||
}
|
||||
if !parse("Microsecond", parts[7], &units.Microsecond) {
|
||||
return units, err
|
||||
}
|
||||
return units, err
|
||||
}
|
Reference in New Issue
Block a user