From: Rob Pike Date: Thu, 15 Aug 2013 00:10:49 +0000 (+1000) Subject: time: handle GMT possibly with offset X-Git-Tag: go1.2rc2~561 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=727b2b6f7dbec2bed608e3c97129c3bb7bab0547;p=gostls13.git time: handle GMT possibly with offset 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 --- diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go index c4ea5fca65..ae13811d41 100644 --- a/src/pkg/time/format.go +++ b/src/pkg/time/format.go @@ -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 diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go index bef5fbec3e..f059380d04 100644 --- a/src/pkg/time/time_test.go +++ b/src/pkg/time/time_test.go @@ -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.