aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/time
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/time')
-rw-r--r--libgo/go/time/format.go405
-rw-r--r--libgo/go/time/time.go54
-rw-r--r--libgo/go/time/time_test.go122
-rw-r--r--libgo/go/time/zoneinfo.go14
-rw-r--r--libgo/go/time/zoneinfo_read.go2
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