diff options
Diffstat (limited to 'libgo/go/time/format.go')
-rw-r--r-- | libgo/go/time/format.go | 271 |
1 files changed, 189 insertions, 82 deletions
diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index f11fb7ed304..bb173a21c2d 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -7,70 +7,89 @@ package time import "errors" // These are predefined layouts for use in Time.Format and time.Parse. -// The reference time used in the layouts is the specific time: -// Mon Jan 2 15:04:05 MST 2006 -// which is Unix time 1136239445. Since MST is GMT-0700, -// the reference time can be thought of as +// The reference time used in these layouts is the specific time stamp: // 01/02 03:04:05PM '06 -0700 -// To define your own format, write down what the reference time would look -// like formatted your way; see the values of constants like ANSIC, -// StampMicro or Kitchen for examples. The model is to demonstrate what the -// reference time looks like so that the Format and Parse methods can apply -// the same transformation to a general time value. +// (January 2, 15:04:05, 2006, in time zone seven hours west of GMT). +// That value is recorded as the constant named Layout, listed below. As a Unix +// time, this is 1136239445. Since MST is GMT-0700, the reference would be +// printed by the Unix date command as: +// Mon Jan 2 15:04:05 MST 2006 +// It is a regrettable historic error that the date uses the American convention +// of putting the numerical month before the day. // -// Some valid layouts are invalid time values for time.Parse, due to formats -// such as _ for space padding and Z for zone information. +// The example for Time.Format demonstrates the working of the layout string +// in detail and is a good reference. // -// Within the format string, an underscore _ represents a space that may be -// replaced by a digit if the following number (a day) has two digits; for -// compatibility with fixed-width Unix time formats. +// Note that the RFC822, RFC850, and RFC1123 formats should be applied +// only to local times. Applying them to UTC times will use "UTC" as the +// time zone abbreviation, while strictly speaking those RFCs require the +// use of "GMT" in that case. +// In general RFC1123Z should be used instead of RFC1123 for servers +// that insist on that format, and RFC3339 should be preferred for new protocols. +// RFC3339, RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting; +// when used with time.Parse they do not accept all the time formats +// permitted by the RFCs and they do accept time formats not formally defined. +// The RFC3339Nano format removes trailing zeros from the seconds field +// and thus may not sort correctly once formatted. // -// A decimal point followed by one or more zeros represents a fractional -// second, printed to the given number of decimal places. A decimal point -// followed by one or more nines represents a fractional second, printed to -// the given number of decimal places, with trailing zeros removed. -// When parsing (only), the input may contain a fractional second -// field immediately after the seconds field, even if the layout does not -// signify its presence. In that case a decimal point followed by a maximal -// series of digits is parsed as a fractional second. +// Most programs can use one of the defined constants as the layout passed to +// Format or Parse. The rest of this comment can be ignored unless you are +// creating a custom layout string. +// +// To define your own format, write down what the reference time would look like +// formatted your way; see the values of constants like ANSIC, StampMicro or +// Kitchen for examples. The model is to demonstrate what the reference time +// looks like so that the Format and Parse methods can apply the same +// transformation to a general time value. +// +// Here is a summary of the components of a layout string. Each element shows by +// example the formatting of an element of the reference time. Only these values +// are recognized. Text in the layout string that is not recognized as part of +// the reference time is echoed verbatim during Format and expected to appear +// verbatim in the input to Parse. +// +// Year: "2006" "06" +// Month: "Jan" "January" +// Textual day of the week: "Mon" "Monday" +// Numeric day of the month: "2" "_2" "02" +// Numeric day of the year: "__2" "002" +// Hour: "15" "3" "03" (PM or AM) +// Minute: "4" "04" +// Second: "5" "05" +// AM/PM mark: "PM" // // Numeric time zone offsets format as follows: -// -0700 ±hhmm -// -07:00 ±hh:mm -// -07 ±hh +// "-0700" ±hhmm +// "-07:00" ±hh:mm +// "-07" ±hh // Replacing the sign in the format with a Z triggers // the ISO 8601 behavior of printing Z instead of an // offset for the UTC zone. Thus: -// Z0700 Z or ±hhmm -// Z07:00 Z or ±hh:mm -// Z07 Z or ±hh +// "Z0700" Z or ±hhmm +// "Z07:00" Z or ±hh:mm +// "Z07" Z or ±hh // -// The recognized day of week formats are "Mon" and "Monday". -// The recognized month formats are "Jan" and "January". +// Within the format string, the underscores in "_2" and "__2" represent spaces +// that may be replaced by digits if the following number has multiple digits, +// for compatibility with fixed-width Unix time formats. A leading zero represents +// a zero-padded value. // -// The formats 2, _2, and 02 are unpadded, space-padded, and zero-padded -// day of month. The formats __2 and 002 are space-padded and zero-padded +// The formats and 002 are space-padded and zero-padded // three-character day of year; there is no unpadded day of year format. // -// Text in the format string that is not recognized as part of the reference -// time is echoed verbatim during Format and expected to appear verbatim -// in the input to Parse. +// A decimal point followed by one or more zeros represents a fractional +// second, printed to the given number of decimal places. +// Either a comma or decimal point followed by one or more nines represents +// a fractional second, printed to the given number of decimal places, with +// trailing zeros removed. +// For example "15:04:05,000" or "15:04:05.000" formats or parses with +// millisecond precision. // -// The executable example for Time.Format demonstrates the working -// of the layout string in detail and is a good reference. +// Some valid layouts are invalid time values for time.Parse, due to formats +// such as _ for space padding and Z for zone information. // -// Note that the RFC822, RFC850, and RFC1123 formats should be applied -// only to local times. Applying them to UTC times will use "UTC" as the -// time zone abbreviation, while strictly speaking those RFCs require the -// use of "GMT" in that case. -// In general RFC1123Z should be used instead of RFC1123 for servers -// that insist on that format, and RFC3339 should be preferred for new protocols. -// RFC3339, RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting; -// when used with time.Parse they do not accept all the time formats -// permitted by the RFCs and they do accept time formats not formally defined. -// The RFC3339Nano format removes trailing zeros from the seconds field -// and thus may not sort correctly once formatted. const ( + Layout = "01/02 03:04:05PM '06 -0700" // The reference time, in numerical order. ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" RubyDate = "Mon Jan 02 15:04:05 -0700 2006" @@ -261,7 +280,7 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) { return layout[0:i], stdISO8601ShortTZ, layout[i+3:] } - case '.': // .000 or .999 - repeated digits for fractional seconds. + case '.', ',': // ,000, or .000, or ,999, or .999 - repeated digits for fractional seconds. if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { ch := layout[i+1] j := i + 1 @@ -476,22 +495,66 @@ func (t Time) String() string { return s } -// Format returns a textual representation of the time value formatted -// according to layout, which defines the format by showing how the reference -// time, defined to be -// Mon Jan 2 15:04:05 -0700 MST 2006 -// 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. +// GoString implements fmt.GoStringer and formats t to be printed in Go source +// code. +func (t Time) GoString() string { + buf := []byte("time.Date(") + buf = appendInt(buf, t.Year(), 0) + month := t.Month() + if January <= month && month <= December { + buf = append(buf, ", time."...) + buf = append(buf, t.Month().String()...) + } else { + // It's difficult to construct a time.Time with a date outside the + // standard range but we might as well try to handle the case. + buf = appendInt(buf, int(month), 0) + } + buf = append(buf, ", "...) + buf = appendInt(buf, t.Day(), 0) + buf = append(buf, ", "...) + buf = appendInt(buf, t.Hour(), 0) + buf = append(buf, ", "...) + buf = appendInt(buf, t.Minute(), 0) + buf = append(buf, ", "...) + buf = appendInt(buf, t.Second(), 0) + buf = append(buf, ", "...) + buf = appendInt(buf, t.Nanosecond(), 0) + buf = append(buf, ", "...) + switch loc := t.Location(); loc { + case UTC, nil: + buf = append(buf, "time.UTC"...) + case Local: + buf = append(buf, "time.Local"...) + default: + // there are several options for how we could display this, none of + // which are great: + // + // - use Location(loc.name), which is not technically valid syntax + // - use LoadLocation(loc.name), which will cause a syntax error when + // embedded and also would require us to escape the string without + // importing fmt or strconv + // - try to use FixedZone, which would also require escaping the name + // and would represent e.g. "America/Los_Angeles" daylight saving time + // shifts inaccurately + // - use the pointer format, which is no worse than you'd get with the + // old fmt.Sprintf("%#v", t) format. + // + // Of these, Location(loc.name) is the least disruptive. This is an edge + // case we hope not to hit too often. + buf = append(buf, `time.Location(`...) + buf = append(buf, []byte(quote(loc.name))...) + buf = append(buf, `)`...) + } + buf = append(buf, ')') + return string(buf) +} + +// Format returns a textual representation of the time value formatted according +// to the layout defined by the argument. See the documentation for the +// constant called Layout to see how to represent the layout format. // -// 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. +// The executable example for Time.Format demonstrates the working +// of the layout string in detail and is a good reference. func (t Time) Format(layout string) string { const bufSize = 64 var b []byte @@ -686,8 +749,48 @@ type ParseError struct { Message string } +// These are borrowed from unicode/utf8 and strconv and replicate behavior in +// that package, since we can't take a dependency on either. +const ( + lowerhex = "0123456789abcdef" + runeSelf = 0x80 + runeError = '\uFFFD' +) + func quote(s string) string { - return "\"" + s + "\"" + buf := make([]byte, 1, len(s)+2) // slice will be at least len(s) + quotes + buf[0] = '"' + for i, c := range s { + if c >= runeSelf || c < ' ' { + // This means you are asking us to parse a time.Duration or + // time.Location with unprintable or non-ASCII characters in it. + // We don't expect to hit this case very often. We could try to + // reproduce strconv.Quote's behavior with full fidelity but + // given how rarely we expect to hit these edge cases, speed and + // conciseness are better. + var width int + if c == runeError { + width = 1 + if i+2 < len(s) && s[i:i+3] == string(runeError) { + width = 3 + } + } else { + width = len(string(c)) + } + for j := 0; j < width; j++ { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[i+j]>>4]) + buf = append(buf, lowerhex[s[i+j]&0xF]) + } + } else { + if c == '"' || c == '\\' { + buf = append(buf, '\\') + } + buf = append(buf, string(c)...) + } + } + buf = append(buf, '"') + return string(buf) } // Error returns the string representation of a ParseError. @@ -771,21 +874,19 @@ func skip(value, prefix string) (string, error) { } // Parse parses a formatted string and returns the time value it represents. -// The layout defines the format by showing how the reference time, -// defined to be -// Mon Jan 2 15:04:05 -0700 MST 2006 -// 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. +// See the documentation for the constant called Layout to see how to +// represent the format. The second argument must be parseable using +// the format string (layout) provided as the first argument. // -// 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. +// The example for Time.Format demonstrates the working of the layout string +// in detail and is a good reference. +// +// When parsing (only), the input may contain a fractional second +// field immediately after the seconds field, even if the layout does not +// signify its presence. In that case either a comma or a decimal point +// followed by a maximal series of digits is parsed as a fractional second. // -// Elements omitted from the value are assumed to be zero or, when +// Elements omitted from the layout are assumed to be zero or, when // zero is impossible, one, so parsing "3:04pm" returns the time // corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is // 0, this time is before the zero Time). @@ -795,6 +896,8 @@ func skip(value, prefix string) (string, error) { // For layouts specifying the two-digit year 06, a value NN >= 69 will be treated // as 19NN and a value NN < 69 will be treated as 20NN. // +// The remainder of this comment describes the handling of time zones. +// // In the absence of a time zone indicator, Parse returns a time in UTC. // // When parsing a time with a zone offset like -0700, if the offset corresponds @@ -940,7 +1043,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) } // Special case: do we have a fractional second but no // fractional second in the format? - if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) { + if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) { _, std, _ = nextStdChunk(layout) std &= stdMask if std == stdFracSecond0 || std == stdFracSecond9 { @@ -1070,7 +1173,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) value = value[ndigit:] case stdFracSecond9: - if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] { + if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] { // Fractional second omitted. break } @@ -1152,7 +1255,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) // Look for local zone with the given offset. // If that zone was in effect at the given time, use it. - name, offset, _, _ := local.lookup(t.unixSec()) + name, offset, _, _, _ := local.lookup(t.unixSec()) if offset == zoneOffset && (zoneName == "" || name == zoneName) { t.setLoc(local) return t, nil @@ -1279,8 +1382,12 @@ func parseSignedOffset(value string) int { return len(value) - len(rem) } +func commaOrPeriod(b byte) bool { + return b == '.' || b == ',' +} + func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) { - if value[0] != '.' { + if !commaOrPeriod(value[0]) { err = errBad return } |