]> Cypherpunks repositories - gostls13.git/commitdiff
time: move RFC 3339 optimizations to separate file
authorJoe Tsai <joetsai@digital-static.net>
Tue, 13 Sep 2022 17:54:07 +0000 (10:54 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 15 Sep 2022 21:10:36 +0000 (21:10 +0000)
The optimizations were added in CL 425197 and CL 421877.
Move this functionality to a separate file to keep format.go smaller
and to document the justification for why this optimization exists.

Change-Id: I1e4e1ace19f9d596d8c0cf49ab6062f63a87b5bf
Reviewed-on: https://go-review.googlesource.com/c/go/+/430675
Run-TryBot: Joseph Tsai <joetsai@digital-static.net>
Auto-Submit: Jenny Rakoczy <jenny@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
Reviewed-by: Jenny Rakoczy <jenny@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Jenny Rakoczy <jenny@golang.org>
Auto-Submit: Joseph Tsai <joetsai@digital-static.net>

src/time/format.go
src/time/format_rfc3339.go [new file with mode: 0644]

index afb130cf94df3b7574b068c02a8aa983b2636120..8051687068bbdd35d2e8ff4f9289892ab0c9045d 100644 (file)
@@ -797,50 +797,6 @@ func (t Time) appendFormat(b []byte, layout string) []byte {
        return b
 }
 
-func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte {
-       _, offset, abs := t.locabs()
-
-       // Format date.
-       year, month, day, _ := absDate(abs, true)
-       b = appendInt(b, year, 4)
-       b = append(b, '-')
-       b = appendInt(b, int(month), 2)
-       b = append(b, '-')
-       b = appendInt(b, day, 2)
-
-       b = append(b, 'T')
-
-       // Format time.
-       hour, min, sec := absClock(abs)
-       b = appendInt(b, hour, 2)
-       b = append(b, ':')
-       b = appendInt(b, min, 2)
-       b = append(b, ':')
-       b = appendInt(b, sec, 2)
-
-       if nanos {
-               std := stdFracSecond(stdFracSecond9, 9, '.')
-               b = formatNano(b, uint(t.Nanosecond()), std)
-       }
-
-       if offset == 0 {
-               return append(b, 'Z')
-       }
-
-       // Format zone.
-       zone := offset / 60 // convert to minutes
-       if zone < 0 {
-               b = append(b, '-')
-               zone = -zone
-       } else {
-               b = append(b, '+')
-       }
-       b = appendInt(b, zone/60, 2)
-       b = append(b, ':')
-       b = appendInt(b, zone%60, 2)
-       return b
-}
-
 var errBad = errors.New("bad value for field") // placeholder not passed to user
 
 // ParseError describes a problem parsing a time string.
@@ -1043,79 +999,6 @@ func ParseInLocation(layout, value string, loc *Location) (Time, error) {
        return parse(layout, value, loc, loc)
 }
 
-func parseRFC3339(s string, local *Location) (Time, bool) {
-       // parseUint parses s as an unsigned decimal integer and
-       // verifies that it is within some range.
-       // If it is invalid or out-of-range,
-       // it sets ok to false and returns the min value.
-       ok := true
-       parseUint := func(s string, min, max int) (x int) {
-               for _, c := range []byte(s) {
-                       if c < '0' || '9' < c {
-                               ok = false
-                               return min
-                       }
-                       x = x*10 + int(c) - '0'
-               }
-               if x < min || max < x {
-                       ok = false
-                       return min
-               }
-               return x
-       }
-
-       // Parse the date and time.
-       if len(s) < len("2006-01-02T15:04:05") {
-               return Time{}, false
-       }
-       year := parseUint(s[0:4], 0, 9999)                       // e.g., 2006
-       month := parseUint(s[5:7], 1, 12)                        // e.g., 01
-       day := parseUint(s[8:10], 1, daysIn(Month(month), year)) // e.g., 02
-       hour := parseUint(s[11:13], 0, 23)                       // e.g., 15
-       min := parseUint(s[14:16], 0, 59)                        // e.g., 04
-       sec := parseUint(s[17:19], 0, 59)                        // e.g., 05
-       if !ok || !(s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':') {
-               return Time{}, false
-       }
-       s = s[19:]
-
-       // Parse the fractional second.
-       var nsec int
-       if len(s) >= 2 && s[0] == '.' && isDigit(s, 1) {
-               n := 2
-               for ; n < len(s) && isDigit(s, n); n++ {
-               }
-               nsec, _, _ = parseNanoseconds(s, n)
-               s = s[n:]
-       }
-
-       // Parse the time zone.
-       t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
-       if s != "Z" {
-               if len(s) != len("-07:00") {
-                       return Time{}, false
-               }
-               hr := parseUint(s[1:3], 0, 23) // e.g., 07
-               mm := parseUint(s[4:6], 0, 59) // e.g., 00
-               if !ok || !((s[0] == '-' || s[0] == '+') && s[3] == ':') {
-                       return Time{}, false
-               }
-               zoneOffset := (hr*60 + mm) * 60
-               if s[0] == '-' {
-                       zoneOffset *= -1
-               }
-               t.addSec(-int64(zoneOffset))
-
-               // Use local zone with the given offset if possible.
-               if _, offset, _, _, _ := local.lookup(t.unixSec()); offset == zoneOffset {
-                       t.setLoc(local)
-               } else {
-                       t.setLoc(FixedZone("", zoneOffset))
-               }
-       }
-       return t, true
-}
-
 func parse(layout, value string, defaultLocation, local *Location) (Time, error) {
        alayout, avalue := layout, value
        rangeErrString := "" // set if a value is out of range
diff --git a/src/time/format_rfc3339.go b/src/time/format_rfc3339.go
new file mode 100644 (file)
index 0000000..3d0a9ca
--- /dev/null
@@ -0,0 +1,131 @@
+// Copyright 2022 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 time
+
+// RFC 3339 is the most commonly used format.
+//
+// It is implicitly used by the Time.(Marshal|Unmarshal)(Text|JSON) methods.
+// Also, according to analysis on https://go.dev/issue/52746,
+// RFC 3339 accounts for 57% of all explicitly specified time formats,
+// with the second most popular format only being used 8% of the time.
+// The overwhelming use of RFC 3339 compared to all other formats justifies
+// the addition of logic to optimize formatting and parsing.
+
+func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte {
+       _, offset, abs := t.locabs()
+
+       // Format date.
+       year, month, day, _ := absDate(abs, true)
+       b = appendInt(b, year, 4)
+       b = append(b, '-')
+       b = appendInt(b, int(month), 2)
+       b = append(b, '-')
+       b = appendInt(b, day, 2)
+
+       b = append(b, 'T')
+
+       // Format time.
+       hour, min, sec := absClock(abs)
+       b = appendInt(b, hour, 2)
+       b = append(b, ':')
+       b = appendInt(b, min, 2)
+       b = append(b, ':')
+       b = appendInt(b, sec, 2)
+
+       if nanos {
+               std := stdFracSecond(stdFracSecond9, 9, '.')
+               b = formatNano(b, uint(t.Nanosecond()), std)
+       }
+
+       if offset == 0 {
+               return append(b, 'Z')
+       }
+
+       // Format zone.
+       zone := offset / 60 // convert to minutes
+       if zone < 0 {
+               b = append(b, '-')
+               zone = -zone
+       } else {
+               b = append(b, '+')
+       }
+       b = appendInt(b, zone/60, 2)
+       b = append(b, ':')
+       b = appendInt(b, zone%60, 2)
+       return b
+}
+
+func parseRFC3339(s string, local *Location) (Time, bool) {
+       // parseUint parses s as an unsigned decimal integer and
+       // verifies that it is within some range.
+       // If it is invalid or out-of-range,
+       // it sets ok to false and returns the min value.
+       ok := true
+       parseUint := func(s string, min, max int) (x int) {
+               for _, c := range []byte(s) {
+                       if c < '0' || '9' < c {
+                               ok = false
+                               return min
+                       }
+                       x = x*10 + int(c) - '0'
+               }
+               if x < min || max < x {
+                       ok = false
+                       return min
+               }
+               return x
+       }
+
+       // Parse the date and time.
+       if len(s) < len("2006-01-02T15:04:05") {
+               return Time{}, false
+       }
+       year := parseUint(s[0:4], 0, 9999)                       // e.g., 2006
+       month := parseUint(s[5:7], 1, 12)                        // e.g., 01
+       day := parseUint(s[8:10], 1, daysIn(Month(month), year)) // e.g., 02
+       hour := parseUint(s[11:13], 0, 23)                       // e.g., 15
+       min := parseUint(s[14:16], 0, 59)                        // e.g., 04
+       sec := parseUint(s[17:19], 0, 59)                        // e.g., 05
+       if !ok || !(s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':') {
+               return Time{}, false
+       }
+       s = s[19:]
+
+       // Parse the fractional second.
+       var nsec int
+       if len(s) >= 2 && s[0] == '.' && isDigit(s, 1) {
+               n := 2
+               for ; n < len(s) && isDigit(s, n); n++ {
+               }
+               nsec, _, _ = parseNanoseconds(s, n)
+               s = s[n:]
+       }
+
+       // Parse the time zone.
+       t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
+       if s != "Z" {
+               if len(s) != len("-07:00") {
+                       return Time{}, false
+               }
+               hr := parseUint(s[1:3], 0, 23) // e.g., 07
+               mm := parseUint(s[4:6], 0, 59) // e.g., 00
+               if !ok || !((s[0] == '-' || s[0] == '+') && s[3] == ':') {
+                       return Time{}, false
+               }
+               zoneOffset := (hr*60 + mm) * 60
+               if s[0] == '-' {
+                       zoneOffset *= -1
+               }
+               t.addSec(-int64(zoneOffset))
+
+               // Use local zone with the given offset if possible.
+               if _, offset, _, _, _ := local.lookup(t.unixSec()); offset == zoneOffset {
+                       t.setLoc(local)
+               } else {
+                       t.setLoc(FixedZone("", zoneOffset))
+               }
+       }
+       return t, true
+}