mirror of
https://github.com/golang/go
synced 2024-11-20 10:44:41 -07:00
time: make Format 2.7x faster
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
This commit is contained in:
parent
eb4138f481
commit
a76c8b2430
@ -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"
|
||||
return append(b, '0')
|
||||
}
|
||||
u := uint(x)
|
||||
if x < 0 {
|
||||
u = -u
|
||||
}
|
||||
for u > 0 {
|
||||
for x >= 10 {
|
||||
n--
|
||||
buf[n] = byte(u%10 + '0')
|
||||
u /= 10
|
||||
buf[n] = byte(x%10 + '0')
|
||||
x /= 10
|
||||
}
|
||||
if x < 0 {
|
||||
n--
|
||||
buf[n] = '-'
|
||||
}
|
||||
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
|
||||
b = append(b, name...)
|
||||
break
|
||||
}
|
||||
// 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 {
|
||||
// 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)
|
||||
}
|
||||
default:
|
||||
if len(std) >= 2 && (std[0:2] == ".0" || std[0:2] == ".9") {
|
||||
p = formatNano(t.Nanosecond(), len(std)-1, std[1] == '9')
|
||||
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 {
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user