summaryrefslogtreecommitdiff
path: root/libgo/go/time/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/time/format.go')
-rw-r--r--libgo/go/time/format.go225
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
}