Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
186 lines (166 sloc) 4.14 KB
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package opentsdb
import (
"errors"
"fmt"
"time"
)
const (
Millisecond Duration = Duration(time.Millisecond)
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
Day = Hour * 24
Week = Day * 7
Month = Day * 30
Year = Day * 365
)
// Duration extends time.Duration to support OpenTSDB time-format specifiers:
// http://opentsdb.net/docs/build/html/user_guide/query/dates.html#relative.
type Duration time.Duration
var unitMap = map[string]float64{
"ms": float64(time.Millisecond),
"s": float64(time.Second),
"m": float64(time.Minute),
"h": float64(time.Hour),
"d": float64(time.Hour) * 24,
"w": float64(time.Hour) * 24 * 7,
"n": float64(time.Hour) * 24 * 30,
"y": float64(time.Hour) * 24 * 365,
}
// ParseDuration is equivalent to time.ParseDuration, but supports time units specified at http://opentsdb.net/docs/build/html/user_guide/query/dates.html.
func ParseDuration(s string) (Duration, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
f := float64(0)
neg := false
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("time: invalid duration " + orig)
}
for s != "" {
g := float64(0) // this element of the sequence
var x int64
var err error
// The next character must be [0-9.]
if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
return 0, errors.New("time: invalid duration " + orig)
}
// Consume [0-9]*
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
g = float64(x)
pre := pl != len(s) // whether we consumed anything before a period
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
x, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + orig)
}
scale := 1.0
for n := pl - len(s); n > 0; n-- {
scale *= 10
}
g += float64(x) / scale
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("time: invalid duration " + orig)
}
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || ('0' <= c && c <= '9') {
break
}
}
if i == 0 {
return 0, errors.New("time: missing unit in duration " + orig)
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
}
f += g * unit
}
if neg {
f = -f
}
return Duration(f), nil
}
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x >= (1<<63-10)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
}
return x, s[i:], nil
}
func (d Duration) String() string {
return fmt.Sprintf("%dms", d/Millisecond)
}
func (d Duration) HumanString() string {
if d >= Year && d%Year == 0 {
return fmt.Sprintf("%dy", d/Year)
}
if d >= Week && d%Week == 0 {
return fmt.Sprintf("%dw", d/Week)
}
if d >= Day && d%Day == 0 {
return fmt.Sprintf("%dd", d/Day)
}
if d >= Hour && d%Hour == 0 {
return fmt.Sprintf("%dh", d/Hour)
}
if d >= Minute && d%Minute == 0 {
return fmt.Sprintf("%dm", d/Minute)
}
if d >= Second && d%Second == 0 {
return fmt.Sprintf("%ds", d/Second)
}
return fmt.Sprintf("%dms", d/Millisecond)
}
// Seconds returns the duration as a floating point number of seconds.
func (d Duration) Seconds() float64 {
return time.Duration(d).Seconds()
}
func (d *Duration) UnmarshalText(text []byte) error {
duration, err := ParseDuration(string(text))
if err != nil {
return err
}
*d = duration
return nil
}
You can’t perform that action at this time.