]> Cypherpunks repositories - gostls13.git/commitdiff
time: handle GMT possibly with offset
authorRob Pike <r@golang.org>
Thu, 15 Aug 2013 00:10:49 +0000 (10:10 +1000)
committerRob Pike <r@golang.org>
Thu, 15 Aug 2013 00:10:49 +0000 (10:10 +1000)
Update #3790
Handle time zones like GMT-8.
The more general time zone-matching problem is not yet resolved.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/12922043

src/pkg/time/format.go
src/pkg/time/time_test.go

index c4ea5fca651fedd620bd8d23cef668902fd1a298..ae13811d4128eca703af5ed7390632073e9d30ea 100644 (file)
@@ -353,8 +353,8 @@ var atoiError = errors.New("time: invalid number")
 // Duplicates functionality in strconv, but avoids dependency.
 func atoi(s string) (x int, err error) {
        neg := false
-       if s != "" && s[0] == '-' {
-               neg = true
+       if s != "" && (s[0] == '-' || s[0] == '+') {
+               neg = s[0] == '-'
                s = s[1:]
        }
        q, rem, err := leadingInt(s)
@@ -933,25 +933,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
                                value = value[3:]
                                break
                        }
-
-                       if len(value) >= 3 && value[2] == 'T' {
-                               p, value = value[0:3], value[3:]
-                       } else if len(value) >= 4 && value[3] == 'T' {
-                               p, value = value[0:4], value[4:]
-                       } else {
+                       n, ok := parseTimeZone(value)
+                       if !ok {
                                err = errBad
                                break
                        }
-                       for i := 0; i < len(p); i++ {
-                               if p[i] < 'A' || 'Z' < p[i] {
-                                       err = errBad
-                               }
-                       }
-                       if err != nil {
-                               break
-                       }
-                       // It's a valid format.
-                       zoneName = p
+                       zoneName, value = value[:n], value[n:]
 
                case stdFracSecond0:
                        // stdFracSecond0 requires the exact number of digits as specified in
@@ -1024,7 +1011,11 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
                }
 
                // Otherwise, create fake zone with unknown offset.
-               t.loc = FixedZone(zoneName, 0)
+               if len(zoneName) > 3 && zoneName[:3] == "GMT" {
+                       offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
+                       offset *= 3600
+               }
+               t.loc = FixedZone(zoneName, offset)
                return t, nil
        }
 
@@ -1032,6 +1023,57 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
        return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
 }
 
+// parseTimeZone parses a time zone string and returns its length.
+func parseTimeZone(value string) (length int, ok bool) {
+       if len(value) < 3 {
+               return 0, false
+       }
+       // GMT may have an offset.
+       if len(value) >= 3 && value[:3] == "GMT" {
+               length = parseGMT(value)
+               return length, true
+       }
+
+       if len(value) >= 3 && value[2] == 'T' {
+               length = 3
+       } else if len(value) >= 4 && value[3] == 'T' {
+               length = 4
+       } else {
+               return 0, false
+       }
+       for i := 0; i < length; i++ {
+               if value[i] < 'A' || 'Z' < value[i] {
+                       return 0, false
+               }
+       }
+       return length, true
+}
+
+// parseGMT parses a GMT time zone. The input string is known to start "GMT".
+// The function checks whether that is followed by a sign and a number in the
+// range -14 through 12 excluding zero.
+func parseGMT(value string) int {
+       value = value[3:]
+       if len(value) == 0 {
+               return 3
+       }
+       sign := value[0]
+       if sign != '-' && sign != '+' {
+               return 3
+       }
+       x, rem, err := leadingInt(value[1:])
+       if err != nil {
+               return 3
+       }
+       if sign == '-' {
+               x = -x
+       }
+       if x == 0 || x < -14 || 12 < x {
+               return 3
+       }
+       return 3 + len(value) - len(rem)
+}
+
 func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
        if value[0] != '.' {
                err = errBad
index bef5fbec3ea7e4d40b0aa783458ec34ece2a2268..f059380d04121f199f7a83834c29967f59d7546d 100644 (file)
@@ -510,6 +510,9 @@ var parseTests = []ParseTest{
        // Month and day names only match when not followed by a lower-case letter.
        {"Janet", "Hi Janet, the Month is January: Jan _2 15:04:05 2006", "Hi Janet, the Month is February: Feb  4 21:00:57 2010", false, true, 1, 0},
 
+       // GMT with offset.
+       {"GMT-8", UnixDate, "Fri Feb  5 05:00:57 GMT-8 2010", true, true, 1, 0},
+
        // Accept any number of fractional second digits (including none) for .999...
        // In Go 1, .999... was completely ignored in the format, meaning the first two
        // cases would succeed, but the next four would not. Go 1.1 accepts all six.