diff options
Diffstat (limited to 'libgo/go/time')
-rw-r--r-- | libgo/go/time/format.go | 405 | ||||
-rw-r--r-- | libgo/go/time/time.go | 54 | ||||
-rw-r--r-- | libgo/go/time/time_test.go | 122 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo.go | 14 | ||||
-rw-r--r-- | libgo/go/time/zoneinfo_read.go | 2 |
5 files changed, 399 insertions, 198 deletions
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index efca3a9..46f4fbc 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -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 + return append(b, '0') } - 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 + + 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,31 @@ func Parse(layout, value string) (Time, error) { } // It's a valid format. zoneName = p - default: - if len(value) < len(std) { - err = errBad + + case stdFracSecond0: + ndigit := std >> stdArgShift + nsec, rangeErrString, err = parseNanoseconds(value, 1+ndigit) + value = value[1+ndigit:] + + case stdFracSecond9: + if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] { + // Fractional second omitted. break } - if len(std) >= 2 && std[0:2] == ".0" { - nsec, rangeErrString, err = parseNanoseconds(value, len(std)) - value = value[len(std):] + // Take any number of digits, even more than asked for, + // because it is what the stdSecond case would do. + i := 0 + for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { + i++ } + nsec, rangeErrString, err = parseNanoseconds(value, 1+i) + value = value[1+i:] } 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 { diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index d48ca0c..011a1e3 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -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 @@ -378,6 +412,13 @@ func (t Time) Nanosecond() int { return int(t.nsec) } +// YearDay returns the day of the year specified by t, in the range [1,365] for non-leap years, +// and [1,366] in leap years. +func (t Time) YearDay() int { + _, _, _, yday := t.date(false) + return yday + 1 +} + // A Duration represents the elapsed time between two instants // as an int64 nanosecond count. The representation limits the // largest representable duration to approximately 290 years. @@ -607,11 +648,16 @@ const ( days1970To2001 = 31*365 + 8 ) -// date computes the year and, only when full=true, +// date computes the year, day of year, and 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 diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index c48e0a4..23bb6a5 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -324,6 +324,16 @@ var parseTests = []ParseTest{ // Leading zeros in other places should not be taken as fractional seconds. {"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1}, {"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2}, + + // 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. + {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, + {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, + {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, + {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, + {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, + {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, } func TestParse(t *testing.T) { @@ -601,6 +611,103 @@ func TestISOWeek(t *testing.T) { } } +type YearDayTest struct { + year, month, day int + yday int +} + +// Test YearDay in several different scenarios +// and corner cases +var yearDayTests = []YearDayTest{ + // Non-leap-year tests + {2007, 1, 1, 1}, + {2007, 1, 15, 15}, + {2007, 2, 1, 32}, + {2007, 2, 15, 46}, + {2007, 3, 1, 60}, + {2007, 3, 15, 74}, + {2007, 4, 1, 91}, + {2007, 12, 31, 365}, + + // Leap-year tests + {2008, 1, 1, 1}, + {2008, 1, 15, 15}, + {2008, 2, 1, 32}, + {2008, 2, 15, 46}, + {2008, 3, 1, 61}, + {2008, 3, 15, 75}, + {2008, 4, 1, 92}, + {2008, 12, 31, 366}, + + // Looks like leap-year (but isn't) tests + {1900, 1, 1, 1}, + {1900, 1, 15, 15}, + {1900, 2, 1, 32}, + {1900, 2, 15, 46}, + {1900, 3, 1, 60}, + {1900, 3, 15, 74}, + {1900, 4, 1, 91}, + {1900, 12, 31, 365}, + + // Year one tests (non-leap) + {1, 1, 1, 1}, + {1, 1, 15, 15}, + {1, 2, 1, 32}, + {1, 2, 15, 46}, + {1, 3, 1, 60}, + {1, 3, 15, 74}, + {1, 4, 1, 91}, + {1, 12, 31, 365}, + + // Year minus one tests (non-leap) + {-1, 1, 1, 1}, + {-1, 1, 15, 15}, + {-1, 2, 1, 32}, + {-1, 2, 15, 46}, + {-1, 3, 1, 60}, + {-1, 3, 15, 74}, + {-1, 4, 1, 91}, + {-1, 12, 31, 365}, + + // 400 BC tests (leap-year) + {-400, 1, 1, 1}, + {-400, 1, 15, 15}, + {-400, 2, 1, 32}, + {-400, 2, 15, 46}, + {-400, 3, 1, 61}, + {-400, 3, 15, 75}, + {-400, 4, 1, 92}, + {-400, 12, 31, 366}, + + // Special Cases + + // Gregorian calendar change (no effect) + {1582, 10, 4, 277}, + {1582, 10, 15, 288}, +} + +// Check to see if YearDay is location sensitive +var yearDayLocations = []*Location{ + FixedZone("UTC-8", -8*60*60), + FixedZone("UTC-4", -4*60*60), + UTC, + FixedZone("UTC+4", 4*60*60), + FixedZone("UTC+8", 8*60*60), +} + +func TestYearDay(t *testing.T) { + for _, loc := range yearDayLocations { + for _, ydt := range yearDayTests { + dt := Date(ydt.year, Month(ydt.month), ydt.day, 0, 0, 0, 0, loc) + yday := dt.YearDay() + if yday != ydt.yday { + t.Errorf("got %d, expected %d for %d-%02d-%02d in %v", + yday, ydt.yday, ydt.year, ydt.month, ydt.day, loc) + } + } + } +} + var durationTests = []struct { str string d Duration @@ -741,7 +848,7 @@ var gobTests = []Time{ Date(0, 1, 2, 3, 4, 5, 6, UTC), Date(7, 8, 9, 10, 11, 12, 13, FixedZone("", 0)), Unix(81985467080890095, 0x76543210), // Time.sec: 0x0123456789ABCDEF - Time{}, // nil location + {}, // nil location Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", 32767*60)), Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", -32768*60)), } @@ -935,9 +1042,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++ { + 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++ { - time.Format("Mon Jan 2 15:04:05 2006") + t.Format("Mon Jan 2 15:04:05 2006") } } diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index 3c57744..116d343 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -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 } diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index ebb4205..d57c09e 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -141,7 +141,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) { if n, ok = zonedata.big4(); !ok { return nil, badData } - zone[i].offset = int(n) + zone[i].offset = int(int32(n)) var b byte if b, ok = zonedata.byte(); !ok { return nil, badData |