diff options
Diffstat (limited to 'libgo/go/time/format.go')
-rw-r--r-- | libgo/go/time/format.go | 225 |
1 files changed, 119 insertions, 106 deletions
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index 04e79f32dcd..873d3ffde91 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -39,6 +39,9 @@ import "errors" // offset for the UTC zone. Thus: // Z0700 Z or ±hhmm // Z07:00 Z or ±hh:mm +// +// The executable example for time.Format demonstrates the working +// of the layout string in detail and is a good reference. const ( ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" @@ -288,7 +291,7 @@ var longMonthNames = []string{ "December", } -// match returns true if s1 and s2 match ignoring case. +// match reports whether s1 and s2 match ignoring case. // It is assumed s1 and s2 are the same length. func match(s1, s2 string) bool { for i := 0; i < len(s1); i++ { @@ -315,36 +318,34 @@ 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. +// appendInt appends the decimal form of x to b and returns the result. +// If the decimal form (excluding sign) is shorter than width, the result is padded with leading 0's. // Duplicates functionality in strconv, but avoids dependency. -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 +func appendInt(b []byte, x int, width int) []byte { + u := uint(x) + if x < 0 { + b = append(b, '-') + u = uint(-x) } - var buf [32]byte - n := len(buf) - if x == 0 { - return append(b, '0') + // Assemble decimal in reverse order. + var buf [20]byte + i := len(buf) + for u >= 10 { + i-- + q := u / 10 + buf[i] = byte('0' + u - q*10) + u = q } - for x >= 10 { - n-- - buf[n] = byte(x%10 + '0') - x /= 10 + i-- + buf[i] = byte('0' + u) + + // Add 0-padding. + for w := len(buf) - i; w < width; w++ { + b = append(b, '0') } - n-- - buf[n] = byte(x + '0') - return append(b, buf[n:]...) + + return append(b, buf[i:]...) } // Never printed, just needs to be non-nil for return by atoi. @@ -407,11 +408,32 @@ func (t Time) String() string { // would be displayed if it were the value; it serves as an example of the // desired output. The same display rules will then be applied to the time // value. +// +// A fractional second is represented by adding a period and zeros +// to the end of the seconds section of layout string, as in "15:04:05.000" +// to format a time stamp with millisecond precision. +// // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard // and convenient representations of the reference time. For more information // about the formats and the definition of the reference time, see the // documentation for ANSIC and the other constants defined by this package. func (t Time) Format(layout string) string { + const bufSize = 64 + var b []byte + max := len(layout) + 10 + if max < bufSize { + var buf [bufSize]byte + b = buf[:0] + } else { + b = make([]byte, 0, max) + } + b = t.AppendFormat(b, layout) + return string(b) +} + +// AppendFormat is like Format but appends the textual +// representation to b and returns the extended buffer. +func (t Time) AppendFormat(b []byte, layout string) []byte { var ( name, offset, abs = t.locabs() @@ -421,16 +443,7 @@ func (t Time) Format(layout string) string { hour int = -1 min int sec int - - 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 layout != "" { prefix, std, suffix := nextStdChunk(layout) @@ -458,75 +471,56 @@ func (t Time) Format(layout string) string { if y < 0 { y = -y } - b = appendUint(b, uint(y%100), '0') + b = appendInt(b, y%100, 2) case stdLongYear: - // Pad year to at least 4 digits. - y := year - switch { - case year <= -1000: - b = append(b, '-') - y = -y - case year <= -100: - b = append(b, "-0"...) - y = -y - case year <= -10: - b = append(b, "-00"...) - y = -y - case year < 0: - b = append(b, "-000"...) - y = -y - case year < 10: - b = append(b, "000"...) - case year < 100: - b = append(b, "00"...) - case year < 1000: - b = append(b, '0') - } - b = appendUint(b, uint(y), 0) + b = appendInt(b, year, 4) case stdMonth: b = append(b, month.String()[:3]...) case stdLongMonth: m := month.String() b = append(b, m...) case stdNumMonth: - b = appendUint(b, uint(month), 0) + b = appendInt(b, int(month), 0) case stdZeroMonth: - b = appendUint(b, uint(month), '0') + b = appendInt(b, int(month), 2) case stdWeekDay: b = append(b, absWeekday(abs).String()[:3]...) case stdLongWeekDay: s := absWeekday(abs).String() b = append(b, s...) case stdDay: - b = appendUint(b, uint(day), 0) + b = appendInt(b, day, 0) case stdUnderDay: - b = appendUint(b, uint(day), ' ') + if day < 10 { + b = append(b, ' ') + } + b = appendInt(b, day, 0) case stdZeroDay: - b = appendUint(b, uint(day), '0') + b = appendInt(b, day, 2) case stdHour: - b = appendUint(b, uint(hour), '0') + b = appendInt(b, hour, 2) case stdHour12: // Noon is 12PM, midnight is 12AM. hr := hour % 12 if hr == 0 { hr = 12 } - b = appendUint(b, uint(hr), 0) + b = appendInt(b, hr, 0) case stdZeroHour12: // Noon is 12PM, midnight is 12AM. hr := hour % 12 if hr == 0 { hr = 12 } - b = appendUint(b, uint(hr), '0') + b = appendInt(b, hr, 2) case stdMinute: - b = appendUint(b, uint(min), 0) + b = appendInt(b, min, 0) case stdZeroMinute: - b = appendUint(b, uint(min), '0') + b = appendInt(b, min, 2) case stdSecond: - b = appendUint(b, uint(sec), 0) + b = appendInt(b, sec, 2) case stdZeroSecond: - b = appendUint(b, uint(sec), '0') + b = appendInt(b, sec, 2) case stdPM: if hour >= 12 { b = append(b, "PM"...) @@ -555,18 +549,18 @@ func (t Time) Format(layout string) string { } else { b = append(b, '+') } - b = appendUint(b, uint(zone/60), '0') + b = appendInt(b, zone/60, 2) if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ { b = append(b, ':') } - b = appendUint(b, uint(zone%60), '0') + b = appendInt(b, zone%60, 2) // append seconds if appropriate if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { b = append(b, ':') } - b = appendUint(b, uint(absoffset%60), '0') + b = appendInt(b, absoffset%60, 2) } case stdTZ: @@ -583,13 +577,13 @@ func (t Time) Format(layout string) string { } else { b = append(b, '+') } - b = appendUint(b, uint(zone/60), '0') - b = appendUint(b, uint(zone%60), '0') + b = appendInt(b, zone/60, 2) + b = appendInt(b, zone%60, 2) case stdFracSecond0, stdFracSecond9: b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9) } } - return string(b) + return b } var errBad = errors.New("bad value for field") // placeholder not passed to user @@ -620,8 +614,7 @@ func (e *ParseError) Error() string { quote(e.Value) + e.Message } -// isDigit returns true if s[i] is a decimal digit, false if not or -// if s[i] is out of range. +// isDigit reports whether s[i] is in range and is a decimal digit. func isDigit(s string, i int) bool { if len(s) <= i { return false @@ -681,10 +674,13 @@ func skip(value, prefix string) (string, error) { // would be interpreted if it were the value; it serves as an example of // the input format. The same interpretation will then be made to the // input string. +// // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard // and convenient representations of the reference time. For more information // about the formats and the definition of the reference time, see the // documentation for ANSIC and the other constants defined by this package. +// Also, the executable example for time.Format demonstrates the working +// of the layout string in detail and is a good reference. // // Elements omitted from the value are assumed to be zero or, when // zero is impossible, one, so parsing "3:04pm" returns the time @@ -1131,24 +1127,28 @@ func leadingInt(s string) (x int64, rem string, err error) { if c < '0' || c > '9' { break } - if x >= (1<<63-10)/10 { + if x > (1<<63-1)/10 { // overflow return 0, "", errLeadingInt } x = x*10 + int64(c) - '0' + if x < 0 { + // overflow + return 0, "", errLeadingInt + } } return x, s[i:], nil } -var unitMap = map[string]float64{ - "ns": float64(Nanosecond), - "us": float64(Microsecond), - "µs": float64(Microsecond), // U+00B5 = micro symbol - "μs": float64(Microsecond), // U+03BC = Greek letter mu - "ms": float64(Millisecond), - "s": float64(Second), - "m": float64(Minute), - "h": float64(Hour), +var unitMap = map[string]int64{ + "ns": int64(Nanosecond), + "us": int64(Microsecond), + "µs": int64(Microsecond), // U+00B5 = micro symbol + "μs": int64(Microsecond), // U+03BC = Greek letter mu + "ms": int64(Millisecond), + "s": int64(Second), + "m": int64(Minute), + "h": int64(Hour), } // ParseDuration parses a duration string. @@ -1159,7 +1159,7 @@ var unitMap = map[string]float64{ func ParseDuration(s string) (Duration, error) { // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ orig := s - f := float64(0) + var d int64 neg := false // Consume [-+]? @@ -1178,22 +1178,23 @@ func ParseDuration(s string) (Duration, error) { return 0, errors.New("time: invalid duration " + orig) } for s != "" { - g := float64(0) // this element of the sequence + var ( + v, f int64 // integers before, after decimal point + scale float64 = 1 // value = v + f/scale + ) - var x int64 var err error // The next character must be [0-9.] - if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { + if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') { return 0, errors.New("time: invalid duration " + orig) } // Consume [0-9]* pl := len(s) - x, s, err = leadingInt(s) + v, s, err = leadingInt(s) if err != nil { return 0, errors.New("time: invalid duration " + orig) } - g = float64(x) pre := pl != len(s) // whether we consumed anything before a period // Consume (\.[0-9]*)? @@ -1201,15 +1202,13 @@ func ParseDuration(s string) (Duration, error) { if s != "" && s[0] == '.' { s = s[1:] pl := len(s) - x, s, err = leadingInt(s) + f, s, err = leadingInt(s) if err != nil { return 0, errors.New("time: invalid duration " + orig) } - scale := 1.0 for n := pl - len(s); n > 0; n-- { scale *= 10 } - g += float64(x) / scale post = pl != len(s) } if !pre && !post { @@ -1221,7 +1220,7 @@ func ParseDuration(s string) (Duration, error) { i := 0 for ; i < len(s); i++ { c := s[i] - if c == '.' || ('0' <= c && c <= '9') { + if c == '.' || '0' <= c && c <= '9' { break } } @@ -1234,15 +1233,29 @@ func ParseDuration(s string) (Duration, error) { if !ok { return 0, errors.New("time: unknown unit " + u + " in duration " + orig) } - - f += g * unit + if v > (1<<63-1)/unit { + // overflow + return 0, errors.New("time: invalid duration " + orig) + } + v *= unit + if f > 0 { + // float64 is needed to be nanosecond accurate for fractions of hours. + // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) + v += int64(float64(f) * (float64(unit) / scale)) + if v < 0 { + // overflow + return 0, errors.New("time: invalid duration " + orig) + } + } + d += v + if d < 0 { + // overflow + return 0, errors.New("time: invalid duration " + orig) + } } if neg { - f = -f - } - if f < float64(-1<<63) || f > float64(1<<63-1) { - return 0, errors.New("time: overflow parsing duration") + d = -d } - return Duration(f), nil + return Duration(d), nil } |