diff options
Diffstat (limited to 'libgo/go/time/format.go')
-rw-r--r-- | libgo/go/time/format.go | 405 |
1 files changed, 221 insertions, 184 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 { |