diff options
Diffstat (limited to 'libgo/go/time')
-rw-r--r-- | libgo/go/time/format.go | 150 | ||||
-rw-r--r-- | libgo/go/time/sleep.go | 4 | ||||
-rw-r--r-- | libgo/go/time/tick.go | 2 | ||||
-rw-r--r-- | libgo/go/time/time_test.go | 77 |
4 files changed, 210 insertions, 23 deletions
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index 082a51a..bd02b48 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -283,25 +283,16 @@ var atoiError = errors.New("time: invalid number") // Duplicates functionality in strconv, but avoids dependency. func atoi(s string) (x int, err error) { - i := 0 - if len(s) > 0 && s[0] == '-' { - i++ + neg := false + if s != "" && s[0] == '-' { + neg = true + s = s[1:] } - if i >= len(s) { + x, rem, err := leadingInt(s) + if err != nil || rem != "" { return 0, atoiError } - for ; i < len(s); i++ { - c := s[i] - if c < '0' || c > '9' { - return 0, atoiError - } - if x >= (1<<31-10)/10 { - // will overflow - return 0, atoiError - } - x = x*10 + int(c) - '0' - } - if s[0] == '-' { + if neg { x = -x } return x, nil @@ -344,10 +335,6 @@ func (b *buffer) WriteString(s string) { *b = append(*b, s...) } -func (b *buffer) WriteByte(c byte) { - *b = append(*b, c) -} - func (b *buffer) String() string { return string([]byte(*b)) } @@ -893,3 +880,126 @@ func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, } return } + +var errLeadingInt = errors.New("time: bad [0-9]*") // never printed + +// leadingInt consumes the leading [0-9]* from s. +func leadingInt(s string) (x int, rem string, err error) { + i := 0 + for ; i < len(s); i++ { + c := s[i] + if c < '0' || c > '9' { + break + } + if x >= (1<<31-10)/10 { + // overflow + return 0, "", errLeadingInt + } + x = x*10 + int(c) - '0' + } + return x, s[i:], nil +} + +var unitMap = map[string]float64{ + "ns": float64(Nanosecond), + "us": float64(Microsecond), + "µs": float64(Microsecond), // U+00B5 = micro symbol + "μs": float64(Microsecond), // U+03BC = Greek letter mu + "ms": float64(Millisecond), + "s": float64(Second), + "m": float64(Minute), + "h": float64(Hour), +} + +// ParseDuration parses a duration string. +// A duration string is a possibly signed sequence of +// decimal numbers, each with optional fraction and a unit suffix, +// such as "300ms", "-1.5h" or "2h45m". +// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". +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 int + 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 + for n := pl - len(s); n > 0; n-- { + scale *= 10 + } + g += float64(x) / float64(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 +} diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go index b4680db..27820b0 100644 --- a/libgo/go/time/sleep.go +++ b/libgo/go/time/sleep.go @@ -41,7 +41,7 @@ func (t *Timer) Stop() (ok bool) { } // NewTimer creates a new Timer that will send -// the current time on its channel after at least ns nanoseconds. +// the current time on its channel after at least duration d. func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ @@ -70,7 +70,7 @@ func sendTime(now int64, c interface{}) { // After waits for the duration to elapse and then sends the current time // on the returned channel. -// It is equivalent to NewTimer(ns).C. +// It is equivalent to NewTimer(d).C. func After(d Duration) <-chan Time { return NewTimer(d).C } diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go index 4440c22..8c6b9bc 100644 --- a/libgo/go/time/tick.go +++ b/libgo/go/time/tick.go @@ -14,7 +14,7 @@ type Ticker struct { } // NewTicker returns a new Ticker containing a channel that will send the -// time, in nanoseconds, with a period specified by the duration argument. +// time with a period specified by the duration argument. // It adjusts the intervals or drops ticks to make up for slow receivers. // The duration d must be greater than zero; if not, NewTicker will panic. func NewTicker(d Duration) *Ticker { diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index 484ae42..cdc1c39 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/gob" "encoding/json" + "math/rand" "strconv" "strings" "testing" @@ -816,6 +817,82 @@ func TestNotJSONEncodableTime(t *testing.T) { } } +var parseDurationTests = []struct { + in string + ok bool + want Duration +}{ + // simple + {"0", true, 0}, + {"5s", true, 5 * Second}, + {"30s", true, 30 * Second}, + {"1478s", true, 1478 * Second}, + // sign + {"-5s", true, -5 * Second}, + {"+5s", true, 5 * Second}, + {"-0", true, 0}, + {"+0", true, 0}, + // decimal + {"5.0s", true, 5 * Second}, + {"5.6s", true, 5*Second + 600*Millisecond}, + {"5.s", true, 5 * Second}, + {".5s", true, 500 * Millisecond}, + {"1.0s", true, 1 * Second}, + {"1.00s", true, 1 * Second}, + {"1.004s", true, 1*Second + 4*Millisecond}, + {"1.0040s", true, 1*Second + 4*Millisecond}, + {"100.00100s", true, 100*Second + 1*Millisecond}, + // different units + {"10ns", true, 10 * Nanosecond}, + {"11us", true, 11 * Microsecond}, + {"12µs", true, 12 * Microsecond}, // U+00B5 + {"12μs", true, 12 * Microsecond}, // U+03BC + {"13ms", true, 13 * Millisecond}, + {"14s", true, 14 * Second}, + {"15m", true, 15 * Minute}, + {"16h", true, 16 * Hour}, + // composite durations + {"3h30m", true, 3*Hour + 30*Minute}, + {"10.5s4m", true, 4*Minute + 10*Second + 500*Millisecond}, + {"-2m3.4s", true, -(2*Minute + 3*Second + 400*Millisecond)}, + {"1h2m3s4ms5us6ns", true, 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond}, + {"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond}, + + // errors + {"", false, 0}, + {"3", false, 0}, + {"-", false, 0}, + {"s", false, 0}, + {".", false, 0}, + {"-.", false, 0}, + {".s", false, 0}, + {"+.s", false, 0}, +} + +func TestParseDuration(t *testing.T) { + for _, tc := range parseDurationTests { + d, err := ParseDuration(tc.in) + if tc.ok && (err != nil || d != tc.want) { + t.Errorf("ParseDuration(%q) = %v, %v, want %v, nil", tc.in, d, err, tc.want) + } else if !tc.ok && err == nil { + t.Errorf("ParseDuration(%q) = _, nil, want _, non-nil", tc.in) + } + } +} + +func TestParseDurationRoundTrip(t *testing.T) { + for i := 0; i < 100; i++ { + // Resolutions finer than milliseconds will result in + // imprecise round-trips. + d0 := Duration(rand.Int31()) * Millisecond + s := d0.String() + d1, err := ParseDuration(s) + if err != nil || d0 != d1 { + t.Errorf("round-trip failed: %d => %q => %d, %v", d0, s, d1, err) + } + } +} + func BenchmarkNow(b *testing.B) { for i := 0; i < b.N; i++ { Now() |