| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package time_test | |
| import ( | |
| "os" | |
| "strings" | |
| "testing" | |
| "testing/quick" | |
| . "time" | |
| ) | |
| func init() { | |
| // Force US Pacific time for daylight-savings | |
| // tests below (localtests). Needs to be set | |
| // before the first call into the time library. | |
| os.Setenv("TZ", "America/Los_Angeles") | |
| } | |
| type TimeTest struct { | |
| seconds int64 | |
| golden Time | |
| } | |
| var utctests = []TimeTest{ | |
| TimeTest{0, Time{1970, 1, 1, 0, 0, 0, Thursday, 0, "UTC"}}, | |
| TimeTest{1221681866, Time{2008, 9, 17, 20, 4, 26, Wednesday, 0, "UTC"}}, | |
| TimeTest{-1221681866, Time{1931, 4, 16, 3, 55, 34, Thursday, 0, "UTC"}}, | |
| TimeTest{-11644473600, Time{1601, 1, 1, 0, 0, 0, Monday, 0, "UTC"}}, | |
| TimeTest{599529660, Time{1988, 12, 31, 0, 1, 0, Saturday, 0, "UTC"}}, | |
| TimeTest{978220860, Time{2000, 12, 31, 0, 1, 0, Sunday, 0, "UTC"}}, | |
| TimeTest{1e18, Time{31688740476, 10, 23, 1, 46, 40, Friday, 0, "UTC"}}, | |
| TimeTest{-1e18, Time{-31688736537, 3, 10, 22, 13, 20, Tuesday, 0, "UTC"}}, | |
| TimeTest{0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, Sunday, 0, "UTC"}}, | |
| TimeTest{-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, Sunday, 0, "UTC"}}, | |
| } | |
| var localtests = []TimeTest{ | |
| TimeTest{0, Time{1969, 12, 31, 16, 0, 0, Wednesday, -8 * 60 * 60, "PST"}}, | |
| TimeTest{1221681866, Time{2008, 9, 17, 13, 4, 26, Wednesday, -7 * 60 * 60, "PDT"}}, | |
| } | |
| func same(t, u *Time) bool { | |
| return t.Year == u.Year && | |
| t.Month == u.Month && | |
| t.Day == u.Day && | |
| t.Hour == u.Hour && | |
| t.Minute == u.Minute && | |
| t.Second == u.Second && | |
| t.Weekday == u.Weekday && | |
| t.ZoneOffset == u.ZoneOffset && | |
| t.Zone == u.Zone | |
| } | |
| func TestSecondsToUTC(t *testing.T) { | |
| for i := 0; i < len(utctests); i++ { | |
| sec := utctests[i].seconds | |
| golden := &utctests[i].golden | |
| tm := SecondsToUTC(sec) | |
| newsec := tm.Seconds() | |
| if newsec != sec { | |
| t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec) | |
| } | |
| if !same(tm, golden) { | |
| t.Errorf("SecondsToUTC(%d):", sec) | |
| t.Errorf(" want=%+v", *golden) | |
| t.Errorf(" have=%+v", *tm) | |
| } | |
| } | |
| } | |
| func TestSecondsToLocalTime(t *testing.T) { | |
| for i := 0; i < len(localtests); i++ { | |
| sec := localtests[i].seconds | |
| golden := &localtests[i].golden | |
| tm := SecondsToLocalTime(sec) | |
| newsec := tm.Seconds() | |
| if newsec != sec { | |
| t.Errorf("SecondsToLocalTime(%d).Seconds() = %d", sec, newsec) | |
| } | |
| if !same(tm, golden) { | |
| t.Errorf("SecondsToLocalTime(%d):", sec) | |
| t.Errorf(" want=%+v", *golden) | |
| t.Errorf(" have=%+v", *tm) | |
| } | |
| } | |
| } | |
| func TestSecondsToUTCAndBack(t *testing.T) { | |
| f := func(sec int64) bool { return SecondsToUTC(sec).Seconds() == sec } | |
| f32 := func(sec int32) bool { return f(int64(sec)) } | |
| cfg := &quick.Config{MaxCount: 10000} | |
| // Try a reasonable date first, then the huge ones. | |
| if err := quick.Check(f32, cfg); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := quick.Check(f, cfg); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| type TimeFormatTest struct { | |
| time Time | |
| formattedValue string | |
| } | |
| 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-05:00"}, | |
| TimeFormatTest{Time{2000, 12, 26, 1, 15, 6, Wednesday, 15600, "OTO"}, "2000-12-26T01:15:06+04:20"}, | |
| } | |
| 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(RFC3339)) | |
| } | |
| } | |
| } | |
| type FormatTest struct { | |
| name string | |
| format string | |
| result string | |
| } | |
| var formatTests = []FormatTest{ | |
| FormatTest{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010"}, | |
| FormatTest{"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010"}, | |
| FormatTest{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010"}, | |
| 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{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00"}, | |
| FormatTest{"Kitchen", Kitchen, "9:00PM"}, | |
| FormatTest{"am/pm", "3pm", "9pm"}, | |
| FormatTest{"AM/PM", "3PM", "9PM"}, | |
| } | |
| func TestFormat(t *testing.T) { | |
| // The numeric time represents Thu Feb 4 21:00:57 PST 2010 | |
| time := SecondsToLocalTime(1265346057) | |
| for _, test := range formatTests { | |
| result := time.Format(test.format) | |
| if result != test.result { | |
| t.Errorf("%s expected %q got %q", test.name, test.result, result) | |
| } | |
| } | |
| } | |
| type ParseTest struct { | |
| name string | |
| format string | |
| value string | |
| hasTZ bool // contains a time zone | |
| hasWD bool // contains a weekday | |
| yearSign int64 // sign of year | |
| } | |
| var parseTests = []ParseTest{ | |
| ParseTest{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1}, | |
| ParseTest{"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010", true, true, 1}, | |
| 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{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1}, | |
| ParseTest{"custom: \"2006-01-02 15:04:05-07\"", "2006-01-02 15:04:05-07", "2010-02-04 21:00:57-08", 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}, | |
| } | |
| func TestParse(t *testing.T) { | |
| for _, test := range parseTests { | |
| time, err := Parse(test.format, test.value) | |
| if err != nil { | |
| t.Errorf("%s error: %v", test.name, err) | |
| } else { | |
| checkTime(time, &test, t) | |
| } | |
| } | |
| } | |
| var rubyTests = []ParseTest{ | |
| ParseTest{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1}, | |
| // Ignore the time zone in the test. If it parses, it'll be OK. | |
| ParseTest{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0000 2010", false, true, 1}, | |
| ParseTest{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +0000 2010", false, true, 1}, | |
| ParseTest{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +1130 2010", false, true, 1}, | |
| } | |
| // Problematic time zone format needs special tests. | |
| func TestRubyParse(t *testing.T) { | |
| for _, test := range rubyTests { | |
| time, err := Parse(test.format, test.value) | |
| if err != nil { | |
| t.Errorf("%s error: %v", test.name, err) | |
| } else { | |
| checkTime(time, &test, t) | |
| } | |
| } | |
| } | |
| func checkTime(time *Time, test *ParseTest, t *testing.T) { | |
| // The time should be Thu Feb 4 21:00:57 PST 2010 | |
| if test.yearSign*time.Year != 2010 { | |
| t.Errorf("%s: bad year: %d not %d\n", test.name, time.Year, 2010) | |
| } | |
| if time.Month != 2 { | |
| t.Errorf("%s: bad month: %d not %d\n", test.name, time.Month, 2) | |
| } | |
| if time.Day != 4 { | |
| t.Errorf("%s: bad day: %d not %d\n", test.name, time.Day, 4) | |
| } | |
| if time.Hour != 21 { | |
| t.Errorf("%s: bad hour: %d not %d\n", test.name, time.Hour, 21) | |
| } | |
| if time.Minute != 0 { | |
| t.Errorf("%s: bad minute: %d not %d\n", test.name, time.Minute, 0) | |
| } | |
| if time.Second != 57 { | |
| t.Errorf("%s: bad second: %d not %d\n", test.name, time.Second, 57) | |
| } | |
| if test.hasTZ && time.ZoneOffset != -28800 { | |
| t.Errorf("%s: bad tz offset: %d not %d\n", test.name, time.ZoneOffset, -28800) | |
| } | |
| if test.hasWD && time.Weekday != 4 { | |
| t.Errorf("%s: bad weekday: %d not %d\n", test.name, time.Weekday, 4) | |
| } | |
| } | |
| func TestFormatAndParse(t *testing.T) { | |
| const fmt = "Mon MST " + RFC3339 // all fields | |
| f := func(sec int64) bool { | |
| t1 := SecondsToLocalTime(sec) | |
| if t1.Year < 1000 || t1.Year > 9999 { | |
| // not required to work | |
| return true | |
| } | |
| t2, err := Parse(fmt, t1.Format(fmt)) | |
| if err != nil { | |
| t.Errorf("error: %s", err) | |
| return false | |
| } | |
| if !same(t1, t2) { | |
| t.Errorf("different: %q %q", t1, t2) | |
| return false | |
| } | |
| return true | |
| } | |
| f32 := func(sec int32) bool { return f(int64(sec)) } | |
| cfg := &quick.Config{MaxCount: 10000} | |
| // Try a reasonable date first, then the huge ones. | |
| if err := quick.Check(f32, cfg); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := quick.Check(f, cfg); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| type ParseErrorTest struct { | |
| format string | |
| value string | |
| expect string // must appear within the error | |
| } | |
| var parseErrorTests = []ParseErrorTest{ | |
| ParseErrorTest{ANSIC, "Feb 4 21:00:60 2010", "parse"}, // cannot parse Feb as Mon | |
| ParseErrorTest{ANSIC, "Thu Feb 4 21:00:57 @2010", "parse"}, | |
| ParseErrorTest{ANSIC, "Thu Feb 4 21:00:60 2010", "second out of range"}, | |
| ParseErrorTest{ANSIC, "Thu Feb 4 21:61:57 2010", "minute out of range"}, | |
| ParseErrorTest{ANSIC, "Thu Feb 4 24:00:60 2010", "hour out of range"}, | |
| } | |
| func TestParseErrors(t *testing.T) { | |
| for _, test := range parseErrorTests { | |
| _, err := Parse(test.format, test.value) | |
| if err == nil { | |
| t.Errorf("expected error for %q %q\n", test.format, test.value) | |
| } else if strings.Index(err.String(), test.expect) < 0 { | |
| t.Errorf("expected error with %q for %q %q; got %s\n", test.expect, test.format, test.value, err) | |
| } | |
| } | |
| } | |
| // Check that a time without a Zone still produces a (numeric) time zone | |
| // when formatted with MST as a requested zone. | |
| func TestMissingZone(t *testing.T) { | |
| time, err := Parse(RubyDate, "Tue Feb 02 16:10:03 -0500 2006") | |
| if err != nil { | |
| t.Fatal("error parsing date:", err) | |
| } | |
| expect := "Tue Feb 2 16:10:03 -0500 2006" // -0500 not EST | |
| str := time.Format(UnixDate) // uses MST as its time zone | |
| if str != expect { | |
| t.Errorf("expected %q got %q", expect, str) | |
| } | |
| } | |
| func TestMinutesInTimeZone(t *testing.T) { | |
| time, err := Parse(RubyDate, "Mon Jan 02 15:04:05 +0123 2006") | |
| if err != nil { | |
| t.Fatal("error parsing date:", err) | |
| } | |
| expected := (1*60 + 23) * 60 | |
| if time.ZoneOffset != expected { | |
| t.Errorf("ZoneOffset incorrect, expected %d got %d", expected, time.ZoneOffset) | |
| } | |
| } | |
| func BenchmarkSeconds(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| Seconds() | |
| } | |
| } | |
| func BenchmarkNanoseconds(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| Nanoseconds() | |
| } | |
| } | |
| func BenchmarkFormat(b *testing.B) { | |
| time := SecondsToLocalTime(1265346057) | |
| for i := 0; i < b.N; i++ { | |
| time.Format("Mon Jan 2 15:04:05 2006") | |
| } | |
| } | |
| func BenchmarkParse(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| Parse(ANSIC, "Mon Jan 2 15:04:05 2006") | |
| } | |
| } |