aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/time
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/time')
-rw-r--r--libgo/go/time/format.go150
-rw-r--r--libgo/go/time/sleep.go4
-rw-r--r--libgo/go/time/tick.go2
-rw-r--r--libgo/go/time/time_test.go77
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()