]> Cypherpunks repositories - gostls13.git/commitdiff
time: make Format 2.7x faster
authorRuss Cox <rsc@golang.org>
Sun, 3 Jun 2012 15:08:17 +0000 (11:08 -0400)
committerRuss Cox <rsc@golang.org>
Sun, 3 Jun 2012 15:08:17 +0000 (11:08 -0400)
benchmark             old ns/op    new ns/op    delta
BenchmarkFormat            2495          937  -62.44%
BenchmarkFormatNow         2308          889  -61.48%

Update #3679.

R=r
CC=golang-dev
https://golang.org/cl/6278047

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

index 9946890f7c605bd541a7438ac1b6728951e00a7d..b36a58b6612b9cb4b18737b3d65912d0bcf06172 100644 (file)
@@ -57,63 +57,74 @@ const (
 )
 
 const (
-       stdLongMonth      = "January"
-       stdMonth          = "Jan"
-       stdNumMonth       = "1"
-       stdZeroMonth      = "01"
-       stdLongWeekDay    = "Monday"
-       stdWeekDay        = "Mon"
-       stdDay            = "2"
-       stdUnderDay       = "_2"
-       stdZeroDay        = "02"
-       stdHour           = "15"
-       stdHour12         = "3"
-       stdZeroHour12     = "03"
-       stdMinute         = "4"
-       stdZeroMinute     = "04"
-       stdSecond         = "5"
-       stdZeroSecond     = "05"
-       stdLongYear       = "2006"
-       stdYear           = "06"
-       stdPM             = "PM"
-       stdpm             = "pm"
-       stdTZ             = "MST"
-       stdISO8601TZ      = "Z0700"  // prints Z for UTC
-       stdISO8601ColonTZ = "Z07:00" // prints Z for UTC
-       stdNumTZ          = "-0700"  // always numeric
-       stdNumShortTZ     = "-07"    // always numeric
-       stdNumColonTZ     = "-07:00" // always numeric
+       _                 = iota
+       stdLongMonth      = iota + stdNeedDate  // "January"
+       stdMonth                                // "Jan"
+       stdNumMonth                             // "1"
+       stdZeroMonth                            // "01"
+       stdLongWeekDay                          // "Monday"
+       stdWeekDay                              // "Mon"
+       stdDay                                  // "2"
+       stdUnderDay                             // "_2"
+       stdZeroDay                              // "02"
+       stdHour           = iota + stdNeedClock // "15"
+       stdHour12                               // "3"
+       stdZeroHour12                           // "03"
+       stdMinute                               // "4"
+       stdZeroMinute                           // "04"
+       stdSecond                               // "5"
+       stdZeroSecond                           // "05"
+       stdLongYear       = iota + stdNeedDate  // "2006"
+       stdYear                                 // "06"
+       stdPM             = iota + stdNeedClock // "PM"
+       stdpm                                   // "pm"
+       stdTZ             = iota                // "MST"
+       stdISO8601TZ                            // "Z0700"  // prints Z for UTC
+       stdISO8601ColonTZ                       // "Z07:00" // prints Z for UTC
+       stdNumTZ                                // "-0700"  // always numeric
+       stdNumShortTZ                           // "-07"    // always numeric
+       stdNumColonTZ                           // "-07:00" // always numeric
+       stdFracSecond0                          // ".0", ".00", ... , trailing zeros included
+       stdFracSecond9                          // ".9", ".99", ..., trailing zeros omitted
+
+       stdNeedDate  = 1 << 8             // need month, day, year
+       stdNeedClock = 2 << 8             // need hour, minute, second
+       stdArgShift  = 16                 // extra argument in high bits, above low stdArgShift
+       stdMask      = 1<<stdArgShift - 1 // mask out argument
 )
 
+// std0x records the std values for "01", "02", ..., "06".
+var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
+
 // nextStdChunk finds the first occurrence of a std string in
 // layout and returns the text before, the std string, and the text after.
-func nextStdChunk(layout string) (prefix, std, suffix string) {
+func nextStdChunk(layout string) (prefix string, std int, suffix string) {
        for i := 0; i < len(layout); i++ {
-               switch layout[i] {
+               switch c := int(layout[i]); c {
                case 'J': // January, Jan
-                       if len(layout) >= i+7 && layout[i:i+7] == stdLongMonth {
-                               return layout[0:i], stdLongMonth, layout[i+7:]
-                       }
-                       if len(layout) >= i+3 && layout[i:i+3] == stdMonth {
+                       if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
+                               if len(layout) >= i+7 && layout[i:i+7] == "January" {
+                                       return layout[0:i], stdLongMonth, layout[i+7:]
+                               }
                                return layout[0:i], stdMonth, layout[i+3:]
                        }
 
                case 'M': // Monday, Mon, MST
-                       if len(layout) >= i+6 && layout[i:i+6] == stdLongWeekDay {
-                               return layout[0:i], stdLongWeekDay, layout[i+6:]
-                       }
                        if len(layout) >= i+3 {
-                               if layout[i:i+3] == stdWeekDay {
+                               if layout[i:i+3] == "Mon" {
+                                       if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
+                                               return layout[0:i], stdLongWeekDay, layout[i+6:]
+                                       }
                                        return layout[0:i], stdWeekDay, layout[i+3:]
                                }
-                               if layout[i:i+3] == stdTZ {
+                               if layout[i:i+3] == "MST" {
                                        return layout[0:i], stdTZ, layout[i+3:]
                                }
                        }
 
                case '0': // 01, 02, 03, 04, 05, 06
                        if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
-                               return layout[0:i], layout[i : i+2], layout[i+2:]
+                               return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
                        }
 
                case '1': // 15, 1
@@ -123,7 +134,7 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
                        return layout[0:i], stdNumMonth, layout[i+1:]
 
                case '2': // 2006, 2
-                       if len(layout) >= i+4 && layout[i:i+4] == stdLongYear {
+                       if len(layout) >= i+4 && layout[i:i+4] == "2006" {
                                return layout[0:i], stdLongYear, layout[i+4:]
                        }
                        return layout[0:i], stdDay, layout[i+1:]
@@ -133,35 +144,41 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
                                return layout[0:i], stdUnderDay, layout[i+2:]
                        }
 
-               case '3', '4', '5': // 3, 4, 5
-                       return layout[0:i], layout[i : i+1], layout[i+1:]
+               case '3':
+                       return layout[0:i], stdHour12, layout[i+1:]
+
+               case '4':
+                       return layout[0:i], stdMinute, layout[i+1:]
+
+               case '5':
+                       return layout[0:i], stdSecond, layout[i+1:]
 
                case 'P': // PM
                        if len(layout) >= i+2 && layout[i+1] == 'M' {
-                               return layout[0:i], layout[i : i+2], layout[i+2:]
+                               return layout[0:i], stdPM, layout[i+2:]
                        }
 
                case 'p': // pm
                        if len(layout) >= i+2 && layout[i+1] == 'm' {
-                               return layout[0:i], layout[i : i+2], layout[i+2:]
+                               return layout[0:i], stdpm, layout[i+2:]
                        }
 
                case '-': // -0700, -07:00, -07
-                       if len(layout) >= i+5 && layout[i:i+5] == stdNumTZ {
-                               return layout[0:i], layout[i : i+5], layout[i+5:]
+                       if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
+                               return layout[0:i], stdNumTZ, layout[i+5:]
                        }
-                       if len(layout) >= i+6 && layout[i:i+6] == stdNumColonTZ {
-                               return layout[0:i], layout[i : i+6], layout[i+6:]
+                       if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
+                               return layout[0:i], stdNumColonTZ, layout[i+6:]
                        }
-                       if len(layout) >= i+3 && layout[i:i+3] == stdNumShortTZ {
-                               return layout[0:i], layout[i : i+3], layout[i+3:]
+                       if len(layout) >= i+3 && layout[i:i+3] == "-07" {
+                               return layout[0:i], stdNumShortTZ, layout[i+3:]
                        }
                case 'Z': // Z0700, Z07:00
-                       if len(layout) >= i+5 && layout[i:i+5] == stdISO8601TZ {
-                               return layout[0:i], layout[i : i+5], layout[i+5:]
+                       if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
+                               return layout[0:i], stdISO8601TZ, layout[i+5:]
                        }
-                       if len(layout) >= i+6 && layout[i:i+6] == stdISO8601ColonTZ {
-                               return layout[0:i], layout[i : i+6], layout[i+6:]
+                       if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
+                               return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
                        }
                case '.': // .000 or .999 - repeated digits for fractional seconds.
                        if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
@@ -172,12 +189,17 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
                                }
                                // String of digits must end here - only fractional second is all digits.
                                if !isDigit(layout, j) {
-                                       return layout[0:i], layout[i:j], layout[j:]
+                                       std := stdFracSecond0
+                                       if layout[i+1] == '9' {
+                                               std = stdFracSecond9
+                                       }
+                                       std |= (j - (i + 1)) << stdArgShift
+                                       return layout[0:i], std, layout[j:]
                                }
                        }
                }
        }
-       return layout, "", ""
+       return layout, 0, ""
 }
 
 var longDayNames = []string{
@@ -259,27 +281,36 @@ func lookup(tab []string, val string) (int, string, error) {
        return -1, val, errBad
 }
 
+// appendUint appends the decimal form of x to b and returns the result.
+// If x is a single-digit number and pad != 0, appendUint inserts the pad byte
+// before the digit.
 // Duplicates functionality in strconv, but avoids dependency.
-func itoa(x int) string {
+func appendUint(b []byte, x uint, pad byte) []byte {
+       if x < 10 {
+               if pad != 0 {
+                       b = append(b, pad)
+               }
+               return append(b, byte('0'+x))
+       }
+       if x < 100 {
+               b = append(b, byte('0'+x/10))
+               b = append(b, byte('0'+x%10))
+               return b
+       }
+
        var buf [32]byte
        n := len(buf)
        if x == 0 {
-               return "0"
-       }
-       u := uint(x)
-       if x < 0 {
-               u = -u
-       }
-       for u > 0 {
-               n--
-               buf[n] = byte(u%10 + '0')
-               u /= 10
+               return append(b, '0')
        }
-       if x < 0 {
+       for x >= 10 {
                n--
-               buf[n] = '-'
+               buf[n] = byte(x%10 + '0')
+               x /= 10
        }
-       return string(buf[n:])
+       n--
+       buf[n] = byte(x + '0')
+       return append(b, buf[n:]...)
 }
 
 // Never printed, just needs to be non-nil for return by atoi.
@@ -302,37 +333,30 @@ func atoi(s string) (x int, err error) {
        return x, nil
 }
 
-func pad(i int, padding string) string {
-       s := itoa(i)
-       if i < 10 {
-               s = padding + s
+// formatNano appends a fractional second, as nanoseconds, to b
+// and returns the result.
+func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
+       u := nanosec
+       var buf [9]byte
+       for start := len(buf); start > 0; {
+               start--
+               buf[start] = byte(u%10 + '0')
+               u /= 10
        }
-       return s
-}
-
-func zeroPad(i int) string { return pad(i, "0") }
 
-// formatNano formats a fractional second, as nanoseconds.
-func formatNano(nanosec, n int, trim bool) string {
-       // User might give us bad data. Make sure it's positive and in range.
-       // They'll get nonsense output but it will have the right format.
-       s := itoa(int(uint(nanosec) % 1e9))
-       // Zero pad left without fmt.
-       if len(s) < 9 {
-               s = "000000000"[:9-len(s)] + s
-       }
        if n > 9 {
                n = 9
        }
        if trim {
-               for n > 0 && s[n-1] == '0' {
+               for n > 0 && buf[n-1] == '0' {
                        n--
                }
                if n == 0 {
-                       return ""
+                       return b
                }
        }
-       return "." + s[:n]
+       b = append(b, '.')
+       return append(b, buf[:n]...)
 }
 
 // String returns the time formatted using the format string
@@ -341,16 +365,6 @@ func (t Time) String() string {
        return t.Format("2006-01-02 15:04:05.999999999 -0700 MST")
 }
 
-type buffer []byte
-
-func (b *buffer) WriteString(s string) {
-       *b = append(*b, s...)
-}
-
-func (b *buffer) String() string {
-       return string([]byte(*b))
-}
-
 // Format returns a textual representation of the time value formatted
 // according to layout.  The layout defines the format by showing the
 // representation of the standard time,
@@ -361,161 +375,172 @@ func (b *buffer) String() string {
 // definition of the standard time, see the documentation for ANSIC.
 func (t Time) Format(layout string) string {
        var (
+               name, offset, abs = t.locabs()
+
                year  int = -1
                month Month
                day   int
                hour  int = -1
                min   int
                sec   int
-               b     buffer = make([]byte, 0, len(layout))
+
+               b   []byte
+               buf [64]byte
        )
+       max := len(layout) + 10
+       if max <= len(buf) {
+               b = buf[:0]
+       } else {
+               b = make([]byte, 0, max)
+       }
        // Each iteration generates one std value.
-       for {
+       for layout != "" {
                prefix, std, suffix := nextStdChunk(layout)
-               b.WriteString(prefix)
-               if std == "" {
+               if prefix != "" {
+                       b = append(b, prefix...)
+               }
+               if std == 0 {
                        break
                }
+               layout = suffix
 
                // Compute year, month, day if needed.
-               if year < 0 {
-                       // Jan 01 02 2006
-                       if a, z := std[0], std[len(std)-1]; a == 'J' || a == 'j' || z == '1' || z == '2' || z == '6' {
-                               year, month, day = t.Date()
-                       }
+               if year < 0 && std&stdNeedDate != 0 {
+                       year, month, day, _ = absDate(abs, true)
                }
 
                // Compute hour, minute, second if needed.
-               if hour < 0 {
-                       // 03 04 05 15 pm
-                       if z := std[len(std)-1]; z == '3' || z == '4' || z == '5' || z == 'm' || z == 'M' {
-                               hour, min, sec = t.Clock()
-                       }
+               if hour < 0 && std&stdNeedClock != 0 {
+                       hour, min, sec = absClock(abs)
                }
 
-               var p string
-               switch std {
+               switch std & stdMask {
                case stdYear:
-                       p = zeroPad(year % 100)
+                       y := year
+                       if y < 0 {
+                               y = -y
+                       }
+                       b = appendUint(b, uint(y%100), '0')
                case stdLongYear:
                        // Pad year to at least 4 digits.
-                       p = itoa(year)
+                       y := year
                        switch {
                        case year <= -1000:
-                               // ok
+                               b = append(b, '-')
+                               y = -y
                        case year <= -100:
-                               p = p[:1] + "0" + p[1:]
+                               b = append(b, "-0"...)
+                               y = -y
                        case year <= -10:
-                               p = p[:1] + "00" + p[1:]
+                               b = append(b, "-00"...)
+                               y = -y
                        case year < 0:
-                               p = p[:1] + "000" + p[1:]
+                               b = append(b, "-000"...)
+                               y = -y
                        case year < 10:
-                               p = "000" + p
+                               b = append(b, "000"...)
                        case year < 100:
-                               p = "00" + p
+                               b = append(b, "00"...)
                        case year < 1000:
-                               p = "0" + p
+                               b = append(b, '0')
                        }
+                       b = appendUint(b, uint(y), 0)
                case stdMonth:
-                       p = month.String()[:3]
+                       b = append(b, month.String()[:3]...)
                case stdLongMonth:
-                       p = month.String()
+                       m := month.String()
+                       b = append(b, m...)
                case stdNumMonth:
-                       p = itoa(int(month))
+                       b = appendUint(b, uint(month), 0)
                case stdZeroMonth:
-                       p = zeroPad(int(month))
+                       b = appendUint(b, uint(month), '0')
                case stdWeekDay:
-                       p = t.Weekday().String()[:3]
+                       b = append(b, absWeekday(abs).String()[:3]...)
                case stdLongWeekDay:
-                       p = t.Weekday().String()
+                       s := absWeekday(abs).String()
+                       b = append(b, s...)
                case stdDay:
-                       p = itoa(day)
+                       b = appendUint(b, uint(day), 0)
                case stdUnderDay:
-                       p = pad(day, " ")
+                       b = appendUint(b, uint(day), ' ')
                case stdZeroDay:
-                       p = zeroPad(day)
+                       b = appendUint(b, uint(day), '0')
                case stdHour:
-                       p = zeroPad(hour)
+                       b = appendUint(b, uint(hour), '0')
                case stdHour12:
                        // Noon is 12PM, midnight is 12AM.
                        hr := hour % 12
                        if hr == 0 {
                                hr = 12
                        }
-                       p = itoa(hr)
+                       b = appendUint(b, uint(hr), 0)
                case stdZeroHour12:
                        // Noon is 12PM, midnight is 12AM.
                        hr := hour % 12
                        if hr == 0 {
                                hr = 12
                        }
-                       p = zeroPad(hr)
+                       b = appendUint(b, uint(hr), '0')
                case stdMinute:
-                       p = itoa(min)
+                       b = appendUint(b, uint(min), 0)
                case stdZeroMinute:
-                       p = zeroPad(min)
+                       b = appendUint(b, uint(min), '0')
                case stdSecond:
-                       p = itoa(sec)
+                       b = appendUint(b, uint(sec), 0)
                case stdZeroSecond:
-                       p = zeroPad(sec)
+                       b = appendUint(b, uint(sec), '0')
                case stdPM:
                        if hour >= 12 {
-                               p = "PM"
+                               b = append(b, "PM"...)
                        } else {
-                               p = "AM"
+                               b = append(b, "AM"...)
                        }
                case stdpm:
                        if hour >= 12 {
-                               p = "pm"
+                               b = append(b, "pm"...)
                        } else {
-                               p = "am"
+                               b = append(b, "am"...)
                        }
                case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ:
                        // Ugly special case.  We cheat and take the "Z" variants
                        // to mean "the time zone as formatted for ISO 8601".
-                       _, offset := t.Zone()
-                       if offset == 0 && std[0] == 'Z' {
-                               p = "Z"
+                       if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ) {
+                               b = append(b, 'Z')
                                break
                        }
                        zone := offset / 60 // convert to minutes
                        if zone < 0 {
-                               p = "-"
+                               b = append(b, '-')
                                zone = -zone
                        } else {
-                               p = "+"
+                               b = append(b, '+')
                        }
-                       p += zeroPad(zone / 60)
+                       b = appendUint(b, uint(zone/60), '0')
                        if std == stdISO8601ColonTZ || std == stdNumColonTZ {
-                               p += ":"
+                               b = append(b, ':')
                        }
-                       p += zeroPad(zone % 60)
+                       b = appendUint(b, uint(zone%60), '0')
                case stdTZ:
-                       name, offset := t.Zone()
                        if name != "" {
-                               p = name
-                       } else {
-                               // No time zone known for this time, but we must print one.
-                               // Use the -0700 format.
-                               zone := offset / 60 // convert to minutes
-                               if zone < 0 {
-                                       p = "-"
-                                       zone = -zone
-                               } else {
-                                       p = "+"
-                               }
-                               p += zeroPad(zone / 60)
-                               p += zeroPad(zone % 60)
+                               b = append(b, name...)
+                               break
                        }
-               default:
-                       if len(std) >= 2 && (std[0:2] == ".0" || std[0:2] == ".9") {
-                               p = formatNano(t.Nanosecond(), len(std)-1, std[1] == '9')
+                       // No time zone known for this time, but we must print one.
+                       // Use the -0700 format.
+                       zone := offset / 60 // convert to minutes
+                       if zone < 0 {
+                               b = append(b, '-')
+                               zone = -zone
+                       } else {
+                               b = append(b, '+')
                        }
+                       b = appendUint(b, uint(zone/60), '0')
+                       b = appendUint(b, uint(zone%60), '0')
+               case stdFracSecond0, stdFracSecond9:
+                       b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9)
                }
-               b.WriteString(p)
-               layout = suffix
        }
-       return b.String()
+       return string(b)
 }
 
 var errBad = errors.New("bad value for field") // placeholder not passed to user
@@ -638,11 +663,12 @@ func Parse(layout, value string) (Time, error) {
        for {
                var err error
                prefix, std, suffix := nextStdChunk(layout)
+               stdstr := layout[len(prefix) : len(layout)-len(suffix)]
                value, err = skip(value, prefix)
                if err != nil {
                        return Time{}, &ParseError{alayout, avalue, prefix, value, ""}
                }
-               if len(std) == 0 {
+               if std == 0 {
                        if len(value) != 0 {
                                return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
                        }
@@ -650,7 +676,7 @@ func Parse(layout, value string) (Time, error) {
                }
                layout = suffix
                var p string
-               switch std {
+               switch std & stdMask {
                case stdYear:
                        if len(value) < 2 {
                                err = errBad
@@ -716,7 +742,8 @@ func Parse(layout, value string) (Time, error) {
                        // fractional second in the format?
                        if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
                                _, std, _ := nextStdChunk(layout)
-                               if len(std) > 0 && std[0] == '.' && isDigit(std, 1) {
+                               std &= stdMask
+                               if std == stdFracSecond0 || std == stdFracSecond9 {
                                        // Fractional second in the layout; proceed normally
                                        break
                                }
@@ -756,7 +783,7 @@ func Parse(layout, value string) (Time, error) {
                                err = errBad
                        }
                case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ:
-                       if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' {
+                       if (std == stdISO8601TZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' {
                                value = value[1:]
                                z = UTC
                                break
@@ -824,21 +851,17 @@ func Parse(layout, value string) (Time, error) {
                        }
                        // It's a valid format.
                        zoneName = p
-               default:
-                       if len(value) < len(std) {
-                               err = errBad
-                               break
-                       }
-                       if len(std) >= 2 && std[0:2] == ".0" {
-                               nsec, rangeErrString, err = parseNanoseconds(value, len(std))
-                               value = value[len(std):]
-                       }
+
+               case stdFracSecond0, stdFracSecond9:
+                       ndigit := std >> stdArgShift
+                       nsec, rangeErrString, err = parseNanoseconds(value, 1+ndigit)
+                       value = value[1+ndigit:]
                }
                if rangeErrString != "" {
-                       return Time{}, &ParseError{alayout, avalue, std, value, ": " + rangeErrString + " out of range"}
+                       return Time{}, &ParseError{alayout, avalue, stdstr, value, ": " + rangeErrString + " out of range"}
                }
                if err != nil {
-                       return Time{}, &ParseError{alayout, avalue, std, value, ""}
+                       return Time{}, &ParseError{alayout, avalue, stdstr, value, ""}
                }
        }
        if pmSet && hour < 12 {
index 2461dac06f5cf2cf53ea60f2fbce176ecb36d1ca..f898284621955d092393bb8ec44aab9d53dc17a5 100644 (file)
@@ -257,6 +257,30 @@ func (t Time) abs() uint64 {
        return uint64(sec + (unixToInternal + internalToAbsolute))
 }
 
+// locabs is a combination of the Zone and abs methods,
+// extracting both return values from a single zone lookup.
+func (t Time) locabs() (name string, offset int, abs uint64) {
+       l := t.loc
+       if l == nil {
+               l = &utcLoc
+       }
+       // Avoid function call if we hit the local time cache.
+       sec := t.sec + internalToUnix
+       if l != &utcLoc {
+               if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
+                       name = l.cacheZone.name
+                       offset = l.cacheZone.offset
+               } else {
+                       name, offset, _, _, _ = l.lookup(sec)
+               }
+               sec += int64(offset)
+       } else {
+               name = "UTC"
+       }
+       abs = uint64(sec + (unixToInternal + internalToAbsolute))
+       return
+}
+
 // Date returns the year, month, and day in which t occurs.
 func (t Time) Date() (year int, month Month, day int) {
        year, month, day, _ = t.date(true)
@@ -283,8 +307,13 @@ func (t Time) Day() int {
 
 // Weekday returns the day of the week specified by t.
 func (t Time) Weekday() Weekday {
+       return absWeekday(t.abs())
+}
+
+// absWeekday is like Weekday but operates on an absolute time.
+func absWeekday(abs uint64) Weekday {
        // January 1 of the absolute year, like January 1 of 2001, was a Monday.
-       sec := (t.abs() + uint64(Monday)*secondsPerDay) % secondsPerWeek
+       sec := (abs + uint64(Monday)*secondsPerDay) % secondsPerWeek
        return Weekday(int(sec) / secondsPerDay)
 }
 
@@ -349,7 +378,12 @@ func (t Time) ISOWeek() (year, week int) {
 
 // Clock returns the hour, minute, and second within the day specified by t.
 func (t Time) Clock() (hour, min, sec int) {
-       sec = int(t.abs() % secondsPerDay)
+       return absClock(t.abs())
+}
+
+// absClock is like clock but operates on an absolute time.
+func absClock(abs uint64) (hour, min, sec int) {
+       sec = int(abs % secondsPerDay)
        hour = sec / secondsPerHour
        sec -= hour * secondsPerHour
        min = sec / secondsPerMinute
@@ -610,8 +644,13 @@ const (
 // date computes the year and, only when full=true,
 // the month and day in which t occurs.
 func (t Time) date(full bool) (year int, month Month, day int, yday int) {
+       return absDate(t.abs(), full)
+}
+
+// absDate is like date but operates on an absolute time.
+func absDate(abs uint64, full bool) (year int, month Month, day int, yday int) {
        // Split into time and day.
-       d := t.abs() / secondsPerDay
+       d := abs / secondsPerDay
 
        // Account for 400 year cycles.
        n := d / daysPer400Years
index c48e0a4300c6f050151e7d80216c6045ea3be0c9..68e551e5f73c66cf830986715215360025b61d29 100644 (file)
@@ -935,9 +935,18 @@ func BenchmarkNow(b *testing.B) {
 }
 
 func BenchmarkFormat(b *testing.B) {
-       time := Unix(1265346057, 0)
+       t := Unix(1265346057, 0)
        for i := 0; i < b.N; i++ {
-               time.Format("Mon Jan  2 15:04:05 2006")
+               t.Format("Mon Jan  2 15:04:05 2006")
+       }
+}
+
+func BenchmarkFormatNow(b *testing.B) {
+       // Like BenchmarkFormat, but easier, because the time zone
+       // lookup cache is optimized for the present.
+       t := Now()
+       for i := 0; i < b.N; i++ {
+               t.Format("Mon Jan  2 15:04:05 2006")
        }
 }
 
index 3c57744043e24383c666e49e5e27169b873eee55..116d343005821c20ecf59f4992812494d923a2e0 100644 (file)
@@ -123,21 +123,23 @@ func (l *Location) lookup(sec int64) (name string, offset int, isDST bool, start
        // Not using sort.Search to avoid dependencies.
        tx := l.tx
        end = 1<<63 - 1
-       for len(tx) > 1 {
-               m := len(tx) / 2
+       lo := 0
+       hi := len(tx)
+       for hi-lo > 1 {
+               m := lo + (hi-lo)/2
                lim := tx[m].when
                if sec < lim {
                        end = lim
-                       tx = tx[0:m]
+                       hi = m
                } else {
-                       tx = tx[m:]
+                       lo = m
                }
        }
-       zone := &l.zone[tx[0].index]
+       zone := &l.zone[tx[lo].index]
        name = zone.name
        offset = zone.offset
        isDST = zone.isDST
-       start = tx[0].when
+       start = tx[lo].when
        // end = maintained during the search
        return
 }