summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMicah Stetson <micah.stetson@gmail.com>2010-04-27 00:05:24 -0700
committerMicah Stetson <micah.stetson@gmail.com>2010-04-27 00:05:24 -0700
commit7ea4381bb05ee5ba398c6831679faa29907e12b0 (patch)
tree2147d9ad622de3df9fd3ef7b325a8d5207bc98e3
parentfb94e329ccb52595c2c263e98421c3e53e4a18b3 (diff)
downloadgolang-7ea4381bb05ee5ba398c6831679faa29907e12b0.tar.gz
time: remove incorrect time.ISO8601 and add time.RFC3339
Fixes issue 734. R=rsc, r CC=golang-dev http://codereview.appspot.com/975042 Committer: Russ Cox <rsc@golang.org>
-rw-r--r--src/pkg/time/format.go120
-rw-r--r--src/pkg/time/time_test.go22
2 files changed, 88 insertions, 54 deletions
diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go
index dbf4f38a0..226826aca 100644
--- a/src/pkg/time/format.go
+++ b/src/pkg/time/format.go
@@ -24,6 +24,15 @@ const (
// may be replaced by a digit if the following number
// (a day) has two digits; for compatibility with
// fixed-width Unix time formats.
+//
+// Numeric time zone offsets format as follows:
+// -0700 ±hhmm
+// -07:00 ±hh:mm
+// 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
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
@@ -34,35 +43,35 @@ const (
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
Kitchen = "3:04PM"
- // Special case: use Z to get the time zone formatted according to ISO 8601,
- // which is -0700 or Z for UTC
- ISO8601 = "2006-01-02T15:04:05Z"
+ RFC3339 = "2006-01-02T15:04:05Z07:00"
)
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 = "Z" // prints Z for UTC
- stdNumTZ = "-0700" // always numeric
+ 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
+ stdNumColonTZ = "-07:00" // always numeric
)
// nextStdChunk finds the first occurrence of a std string in
@@ -113,7 +122,7 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
return layout[0:i], stdUnderDay, layout[i+2:]
}
- case '3', '4', '5', 'Z': // 3, 4, 5, Z
+ case '3', '4', '5': // 3, 4, 5
return layout[0:i], layout[i : i+1], layout[i+1:]
case 'P': // PM
@@ -126,10 +135,20 @@ func nextStdChunk(layout string) (prefix, std, suffix string) {
return layout[0:i], layout[i : i+2], layout[i+2:]
}
- case '-': // -0700
+ case '-': // -0700, -07:00
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+6 && layout[i:i+6] == stdNumColonTZ {
+ return layout[0:i], layout[i : i+6], layout[i+6:]
+ }
+ 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+6 && layout[i:i+6] == stdISO8601ColonTZ {
+ return layout[0:i], layout[i : i+6], layout[i+6:]
+ }
}
}
return layout, "", ""
@@ -210,7 +229,7 @@ func zeroPad(i int) string { return pad(i, "0") }
// according to layout. The layout defines the format by showing the
// representation of a standard time, which is then used to describe
// the time to be formatted. Predefined layouts ANSIC, UnixDate,
-// ISO8601 and others describe standard representations.
+// RFC3339 and others describe standard representations.
func (t *Time) Format(layout string) string {
b := new(bytes.Buffer)
// Each iteration generates one std value.
@@ -258,10 +277,10 @@ func (t *Time) Format(layout string) string {
p = strconv.Itoa(t.Second)
case stdZeroSecond:
p = zeroPad(t.Second)
- case stdISO8601TZ, stdNumTZ:
- // Ugly special case. We cheat and take "Z" to mean "the time
- // zone as formatted for ISO 8601".
- if std == stdISO8601TZ && t.ZoneOffset == 0 {
+ 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".
+ if t.ZoneOffset == 0 && std[0] == 'Z' {
p = "Z"
break
}
@@ -273,6 +292,9 @@ func (t *Time) Format(layout string) string {
p = "+"
}
p += zeroPad(zone / 60)
+ if std == stdISO8601ColonTZ || std == stdNumColonTZ {
+ p += ":"
+ }
p += zeroPad(zone % 60)
case stdPM:
if t.Hour >= 12 {
@@ -383,7 +405,7 @@ func skip(value, prefix string) (string, os.Error) {
// Parse parses a formatted string and returns the time value it represents.
// The layout defines the format by showing the representation of a standard
// time, which is then used to describe the string to be parsed. Predefined
-// layouts ANSIC, UnixDate, ISO8601 and others describe standard
+// layouts ANSIC, UnixDate, RFC3339 and others describe standard
// representations.
//
// Only those elements present in the value will be set in the returned time
@@ -475,22 +497,34 @@ func Parse(alayout, avalue string) (*Time, os.Error) {
if t.Second < 0 || 60 <= t.Second {
rangeErrString = "second"
}
- case stdISO8601TZ, stdNumTZ:
- if std == stdISO8601TZ && len(value) >= 1 && value[0] == 'Z' {
+ case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ:
+ if std[0] == 'Z' && len(value) >= 1 && value[0] == 'Z' {
value = value[1:]
t.Zone = "UTC"
break
}
- if len(value) < 5 {
- err = errBad
- break
+ var sign, hh, mm string
+ if std == stdISO8601ColonTZ || std == stdNumColonTZ {
+ if len(value) < 6 {
+ err = errBad
+ break
+ }
+ if value[3] != ':' {
+ err = errBad
+ break
+ }
+ sign, hh, mm, value = value[0:1], value[1:3], value[4:6], value[6:]
+ } else {
+ if len(value) < 5 {
+ err = errBad
+ break
+ }
+ sign, hh, mm, value = value[0:1], value[1:3], value[3:5], value[5:]
}
- var sign string
- sign, p, value = value[0:1], value[1:5], value[5:]
var hr, min int
- hr, err = strconv.Atoi(p[0:2])
+ hr, err = strconv.Atoi(hh)
if err != nil {
- min, err = strconv.Atoi(p[2:4])
+ min, err = strconv.Atoi(mm)
}
t.ZoneOffset = (hr*60 + min) * 60 // offset is in seconds
switch sign[0] {
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
index a5a680986..32bf9652e 100644
--- a/src/pkg/time/time_test.go
+++ b/src/pkg/time/time_test.go
@@ -107,18 +107,18 @@ type TimeFormatTest struct {
formattedValue string
}
-var iso8601Formats = []TimeFormatTest{
+var rfc3339Formats = []TimeFormatTest{
TimeFormatTest{Time{2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC"}, "2008-09-17T20:04:26Z"},
- TimeFormatTest{Time{1994, 9, 17, 20, 4, 26, Wednesday, -18000, "EST"}, "1994-09-17T20:04:26-0500"},
- TimeFormatTest{Time{2000, 12, 26, 1, 15, 6, Wednesday, 15600, "OTO"}, "2000-12-26T01:15:06+0420"},
+ TimeFormatTest{Time{1994, 9, 17, 20, 4, 26, Wednesday, -18000, "EST"}, "1994-09-17T20:04:26-05:00"},
+ TimeFormatTest{Time{2000, 12, 26, 1, 15, 6, Wednesday, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"},
}
-func TestISO8601Conversion(t *testing.T) {
- for _, f := range iso8601Formats {
- if f.time.Format(ISO8601) != f.formattedValue {
- t.Error("ISO8601:")
+func TestRFC3339Conversion(t *testing.T) {
+ for _, f := range rfc3339Formats {
+ if f.time.Format(RFC3339) != f.formattedValue {
+ t.Error("RFC3339:")
t.Errorf(" want=%+v", f.formattedValue)
- t.Errorf(" have=%+v", f.time.Format(ISO8601))
+ t.Errorf(" have=%+v", f.time.Format(RFC3339))
}
}
}
@@ -136,7 +136,7 @@ var formatTests = []FormatTest{
FormatTest{"RFC822", RFC822, "04 Feb 10 2100 PST"},
FormatTest{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST"},
FormatTest{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST"},
- FormatTest{"ISO8601", ISO8601, "2010-02-04T21:00:57-0800"},
+ FormatTest{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00"},
FormatTest{"Kitchen", Kitchen, "9:00PM"},
FormatTest{"am/pm", "3pm", "9pm"},
FormatTest{"AM/PM", "3PM", "9PM"},
@@ -168,7 +168,7 @@ var parseTests = []ParseTest{
ParseTest{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1},
ParseTest{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1},
ParseTest{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1},
- ParseTest{"ISO8601", ISO8601, "2010-02-04T21:00:57-0800", true, false, 1},
+ ParseTest{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1},
// Amount of white space should not matter.
ParseTest{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1},
ParseTest{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1},
@@ -234,7 +234,7 @@ func checkTime(time *Time, test *ParseTest, t *testing.T) {
}
func TestFormatAndParse(t *testing.T) {
- const fmt = "Mon MST " + ISO8601 // all fields
+ const fmt = "Mon MST " + RFC3339 // all fields
f := func(sec int64) bool {
t1 := SecondsToLocalTime(sec)
if t1.Year < 1000 || t1.Year > 9999 {