summaryrefslogtreecommitdiff
path: root/src/pkg/time/time_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/time/time_test.go')
-rw-r--r--src/pkg/time/time_test.go506
1 files changed, 506 insertions, 0 deletions
diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go
new file mode 100644
index 000000000..dceed491a
--- /dev/null
+++ b/src/pkg/time/time_test.go
@@ -0,0 +1,506 @@
+// 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"
+ "strconv"
+ "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")
+}
+
+// We should be in PST/PDT, but if the time zone files are missing we
+// won't be. The purpose of this test is to at least explain why some of
+// the subsequent tests fail.
+func TestZoneData(t *testing.T) {
+ lt := LocalTime()
+ // PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique.
+ if off := lt.ZoneOffset; off != -8*60*60 && off != -7*60*60 {
+ t.Errorf("Unable to find US Pacific time zone data for testing; time zone is %q offset %d", lt.Zone, off)
+ t.Error("Likely problem: the time zone files have not been installed.")
+ }
+}
+
+type TimeTest struct {
+ seconds int64
+ golden Time
+}
+
+var utctests = []TimeTest{
+ {0, Time{1970, 1, 1, 0, 0, 0, 0, Thursday, 0, "UTC"}},
+ {1221681866, Time{2008, 9, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}},
+ {-1221681866, Time{1931, 4, 16, 3, 55, 34, 0, Thursday, 0, "UTC"}},
+ {-11644473600, Time{1601, 1, 1, 0, 0, 0, 0, Monday, 0, "UTC"}},
+ {599529660, Time{1988, 12, 31, 0, 1, 0, 0, Saturday, 0, "UTC"}},
+ {978220860, Time{2000, 12, 31, 0, 1, 0, 0, Sunday, 0, "UTC"}},
+ {1e18, Time{31688740476, 10, 23, 1, 46, 40, 0, Friday, 0, "UTC"}},
+ {-1e18, Time{-31688736537, 3, 10, 22, 13, 20, 0, Tuesday, 0, "UTC"}},
+ {0x7fffffffffffffff, Time{292277026596, 12, 4, 15, 30, 7, 0, Sunday, 0, "UTC"}},
+ {-0x8000000000000000, Time{-292277022657, 1, 27, 8, 29, 52, 0, Sunday, 0, "UTC"}},
+}
+
+var nanoutctests = []TimeTest{
+ {0, Time{1970, 1, 1, 0, 0, 0, 1e8, Thursday, 0, "UTC"}},
+ {1221681866, Time{2008, 9, 17, 20, 4, 26, 2e8, Wednesday, 0, "UTC"}},
+}
+
+var localtests = []TimeTest{
+ {0, Time{1969, 12, 31, 16, 0, 0, 0, Wednesday, -8 * 60 * 60, "PST"}},
+ {1221681866, Time{2008, 9, 17, 13, 4, 26, 0, Wednesday, -7 * 60 * 60, "PDT"}},
+}
+
+var nanolocaltests = []TimeTest{
+ {0, Time{1969, 12, 31, 16, 0, 0, 1e8, Wednesday, -8 * 60 * 60, "PST"}},
+ {1221681866, Time{2008, 9, 17, 13, 4, 26, 3e8, 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.Nanosecond == u.Nanosecond &&
+ t.Weekday == u.Weekday &&
+ t.ZoneOffset == u.ZoneOffset &&
+ t.Zone == u.Zone
+}
+
+func TestSecondsToUTC(t *testing.T) {
+ for _, test := range utctests {
+ sec := test.seconds
+ golden := &test.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 TestNanosecondsToUTC(t *testing.T) {
+ for _, test := range nanoutctests {
+ golden := &test.golden
+ nsec := test.seconds*1e9 + int64(golden.Nanosecond)
+ tm := NanosecondsToUTC(nsec)
+ newnsec := tm.Nanoseconds()
+ if newnsec != nsec {
+ t.Errorf("NanosecondsToUTC(%d).Nanoseconds() = %d", nsec, newnsec)
+ }
+ if !same(tm, golden) {
+ t.Errorf("NanosecondsToUTC(%d):", nsec)
+ t.Errorf(" want=%+v", *golden)
+ t.Errorf(" have=%+v", *tm)
+ }
+ }
+}
+
+func TestSecondsToLocalTime(t *testing.T) {
+ for _, test := range localtests {
+ sec := test.seconds
+ golden := &test.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 TestNanoecondsToLocalTime(t *testing.T) {
+ for _, test := range nanolocaltests {
+ golden := &test.golden
+ nsec := test.seconds*1e9 + int64(golden.Nanosecond)
+ tm := NanosecondsToLocalTime(nsec)
+ newnsec := tm.Nanoseconds()
+ if newnsec != nsec {
+ t.Errorf("NanosecondsToLocalTime(%d).Seconds() = %d", nsec, newnsec)
+ }
+ if !same(tm, golden) {
+ t.Errorf("NanosecondsToLocalTime(%d):", nsec)
+ 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)
+ }
+}
+
+func TestNanosecondsToUTCAndBack(t *testing.T) {
+ f := func(nsec int64) bool { return NanosecondsToUTC(nsec).Nanoseconds() == nsec }
+ f32 := func(nsec int32) bool { return f(int64(nsec)) }
+ cfg := &quick.Config{MaxCount: 10000}
+
+ // Try a small date first, then the large ones. (The span is only a few hundred years
+ // for nanoseconds in an int64.)
+ 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{
+ {Time{2008, 9, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}, "2008-09-17T20:04:26Z"},
+ {Time{1994, 9, 17, 20, 4, 26, 0, Wednesday, -18000, "EST"}, "1994-09-17T20:04:26-05:00"},
+ {Time{2000, 12, 26, 1, 15, 6, 0, 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{
+ {"ANSIC", ANSIC, "Wed Feb 4 21:00:57 2009"},
+ {"UnixDate", UnixDate, "Wed Feb 4 21:00:57 PST 2009"},
+ {"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"},
+ {"RFC822", RFC822, "04 Feb 09 2100 PST"},
+ {"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"},
+ {"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"},
+ {"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"},
+ {"Kitchen", Kitchen, "9:00PM"},
+ {"am/pm", "3pm", "9pm"},
+ {"AM/PM", "3PM", "9PM"},
+ {"two-digit year", "06 01 02", "09 02 04"},
+ // Time stamps, Fractional seconds.
+ {"Stamp", Stamp, "Feb 4 21:00:57"},
+ {"StampMilli", StampMilli, "Feb 4 21:00:57.012"},
+ {"StampMicro", StampMicro, "Feb 4 21:00:57.012345"},
+ {"StampNano", StampNano, "Feb 4 21:00:57.012345678"},
+}
+
+func TestFormat(t *testing.T) {
+ // The numeric time represents Thu Feb 4 21:00:57.012345678 PST 2010
+ time := NanosecondsToLocalTime(1233810057012345678)
+ 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
+ fracDigits int // number of digits of fractional second
+}
+
+var parseTests = []ParseTest{
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
+ {"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010", true, true, 1, 0},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
+ {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1, 0},
+ {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0},
+ {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0},
+ {"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, 0},
+ // Optional fractional seconds.
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57.0 2010", false, true, 1, 1},
+ {"UnixDate", UnixDate, "Thu Feb 4 21:00:57.01 PST 2010", true, true, 1, 2},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57.012 -0800 2010", true, true, 1, 3},
+ {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57.0123 PST", true, true, 1, 4},
+ {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57.01234 PST", true, true, 1, 5},
+ {"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9},
+ // Amount of white space should not matter.
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
+ {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
+ // Fractional seconds.
+ {"millisecond", "Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3},
+ {"microsecond", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6},
+ {"nanosecond", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9},
+ // Leading zeros in other places should not be taken as fractional seconds.
+ {"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
+ {"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2},
+}
+
+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{
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
+ // Ignore the time zone in the test. If it parses, it'll be OK.
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0000 2010", false, true, 1, 0},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +0000 2010", false, true, 1, 0},
+ {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +1130 2010", false, true, 1, 0},
+}
+
+// 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", test.name, time.Year, 2010)
+ }
+ if time.Month != 2 {
+ t.Errorf("%s: bad month: %d not %d", test.name, time.Month, 2)
+ }
+ if time.Day != 4 {
+ t.Errorf("%s: bad day: %d not %d", test.name, time.Day, 4)
+ }
+ if time.Hour != 21 {
+ t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour, 21)
+ }
+ if time.Minute != 0 {
+ t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute, 0)
+ }
+ if time.Second != 57 {
+ t.Errorf("%s: bad second: %d not %d", test.name, time.Second, 57)
+ }
+ // Nanoseconds must be checked against the precision of the input.
+ nanosec, err := strconv.Atoui("012345678"[:test.fracDigits] + "000000000"[:9-test.fracDigits])
+ if err != nil {
+ panic(err)
+ }
+ if time.Nanosecond != int(nanosec) {
+ t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond, nanosec)
+ }
+ if test.hasTZ && time.ZoneOffset != -28800 {
+ t.Errorf("%s: bad tz offset: %d not %d", test.name, time.ZoneOffset, -28800)
+ }
+ if test.hasWD && time.Weekday != 4 {
+ t.Errorf("%s: bad weekday: %d not %d", 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{
+ {ANSIC, "Feb 4 21:00:60 2010", "cannot parse"}, // cannot parse Feb as Mon
+ {ANSIC, "Thu Feb 4 21:00:57 @2010", "cannot parse"},
+ {ANSIC, "Thu Feb 4 21:00:60 2010", "second out of range"},
+ {ANSIC, "Thu Feb 4 21:61:57 2010", "minute out of range"},
+ {ANSIC, "Thu Feb 4 24:00:60 2010", "hour out of range"},
+ {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59x01 2010", "cannot parse"},
+ {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.xxx 2010", "cannot parse"},
+ {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second 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", test.format, test.value)
+ } else if strings.Index(err.String(), test.expect) < 0 {
+ t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err)
+ }
+ }
+}
+
+func TestNoonIs12PM(t *testing.T) {
+ noon := Time{Hour: 12}
+ const expect = "12:00PM"
+ got := noon.Format("3:04PM")
+ if got != expect {
+ t.Errorf("got %q; expect %q", got, expect)
+ }
+ got = noon.Format("03:04PM")
+ if got != expect {
+ t.Errorf("got %q; expect %q", got, expect)
+ }
+}
+
+func TestMidnightIs12AM(t *testing.T) {
+ midnight := Time{Hour: 0}
+ expect := "12:00AM"
+ got := midnight.Format("3:04PM")
+ if got != expect {
+ t.Errorf("got %q; expect %q", got, expect)
+ }
+ got = midnight.Format("03:04PM")
+ if got != expect {
+ t.Errorf("got %q; expect %q", got, expect)
+ }
+}
+
+func Test12PMIsNoon(t *testing.T) {
+ noon, err := Parse("3:04PM", "12:00PM")
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ if noon.Hour != 12 {
+ t.Errorf("got %d; expect 12", noon.Hour)
+ }
+ noon, err = Parse("03:04PM", "12:00PM")
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ if noon.Hour != 12 {
+ t.Errorf("got %d; expect 12", noon.Hour)
+ }
+}
+
+func Test12AMIsMidnight(t *testing.T) {
+ midnight, err := Parse("3:04PM", "12:00AM")
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ if midnight.Hour != 0 {
+ t.Errorf("got %d; expect 0", midnight.Hour)
+ }
+ midnight, err = Parse("03:04PM", "12:00AM")
+ if err != nil {
+ t.Fatal("error parsing date:", err)
+ }
+ if midnight.Hour != 0 {
+ t.Errorf("got %d; expect 0", midnight.Hour)
+ }
+}
+
+// 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")
+ }
+}