mirror of
https://github.com/golang/go
synced 2024-10-07 03:21:21 -06:00
00cd6a3be3
New in Go 1 will be nanosecond precision in the result of time.Now on Linux. This will break code that stores time in external formats at microsecond precision, reads it back, and expects to get exactly the same time. Code like that can be fixed by using time.Now().Round(time.Microsecond) instead of time.Now() in those contexts. R=golang-dev, bradfitz, iant, remyoudompheng CC=golang-dev https://golang.org/cl/6903050
1313 lines
39 KiB
Go
1313 lines
39 KiB
Go
// 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 (
|
|
"bytes"
|
|
"encoding/gob"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"math/rand"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"testing/quick"
|
|
. "time"
|
|
)
|
|
|
|
// 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 := Now()
|
|
// PST is 8 hours west, PDT is 7 hours west. We could use the name but it's not unique.
|
|
if name, off := lt.Zone(); 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", name, off)
|
|
t.Error("Likely problem: the time zone files have not been installed.")
|
|
}
|
|
}
|
|
|
|
// parsedTime is the struct representing a parsed time value.
|
|
type parsedTime struct {
|
|
Year int
|
|
Month Month
|
|
Day int
|
|
Hour, Minute, Second int // 15:04:05 is 15, 4, 5.
|
|
Nanosecond int // Fractional second.
|
|
Weekday Weekday
|
|
ZoneOffset int // seconds east of UTC, e.g. -7*60*60 for -0700
|
|
Zone string // e.g., "MST"
|
|
}
|
|
|
|
type TimeTest struct {
|
|
seconds int64
|
|
golden parsedTime
|
|
}
|
|
|
|
var utctests = []TimeTest{
|
|
{0, parsedTime{1970, January, 1, 0, 0, 0, 0, Thursday, 0, "UTC"}},
|
|
{1221681866, parsedTime{2008, September, 17, 20, 4, 26, 0, Wednesday, 0, "UTC"}},
|
|
{-1221681866, parsedTime{1931, April, 16, 3, 55, 34, 0, Thursday, 0, "UTC"}},
|
|
{-11644473600, parsedTime{1601, January, 1, 0, 0, 0, 0, Monday, 0, "UTC"}},
|
|
{599529660, parsedTime{1988, December, 31, 0, 1, 0, 0, Saturday, 0, "UTC"}},
|
|
{978220860, parsedTime{2000, December, 31, 0, 1, 0, 0, Sunday, 0, "UTC"}},
|
|
}
|
|
|
|
var nanoutctests = []TimeTest{
|
|
{0, parsedTime{1970, January, 1, 0, 0, 0, 1e8, Thursday, 0, "UTC"}},
|
|
{1221681866, parsedTime{2008, September, 17, 20, 4, 26, 2e8, Wednesday, 0, "UTC"}},
|
|
}
|
|
|
|
var localtests = []TimeTest{
|
|
{0, parsedTime{1969, December, 31, 16, 0, 0, 0, Wednesday, -8 * 60 * 60, "PST"}},
|
|
{1221681866, parsedTime{2008, September, 17, 13, 4, 26, 0, Wednesday, -7 * 60 * 60, "PDT"}},
|
|
}
|
|
|
|
var nanolocaltests = []TimeTest{
|
|
{0, parsedTime{1969, December, 31, 16, 0, 0, 1e8, Wednesday, -8 * 60 * 60, "PST"}},
|
|
{1221681866, parsedTime{2008, September, 17, 13, 4, 26, 3e8, Wednesday, -7 * 60 * 60, "PDT"}},
|
|
}
|
|
|
|
func same(t Time, u *parsedTime) bool {
|
|
// Check aggregates.
|
|
year, month, day := t.Date()
|
|
hour, min, sec := t.Clock()
|
|
name, offset := t.Zone()
|
|
if year != u.Year || month != u.Month || day != u.Day ||
|
|
hour != u.Hour || min != u.Minute || sec != u.Second ||
|
|
name != u.Zone || offset != u.ZoneOffset {
|
|
return false
|
|
}
|
|
// Check individual entries.
|
|
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
|
|
}
|
|
|
|
func TestSecondsToUTC(t *testing.T) {
|
|
for _, test := range utctests {
|
|
sec := test.seconds
|
|
golden := &test.golden
|
|
tm := Unix(sec, 0).UTC()
|
|
newsec := tm.Unix()
|
|
if newsec != sec {
|
|
t.Errorf("SecondsToUTC(%d).Seconds() = %d", sec, newsec)
|
|
}
|
|
if !same(tm, golden) {
|
|
t.Errorf("SecondsToUTC(%d): // %#v", sec, tm)
|
|
t.Errorf(" want=%+v", *golden)
|
|
t.Errorf(" have=%v", tm.Format(RFC3339+" MST"))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNanosecondsToUTC(t *testing.T) {
|
|
for _, test := range nanoutctests {
|
|
golden := &test.golden
|
|
nsec := test.seconds*1e9 + int64(golden.Nanosecond)
|
|
tm := Unix(0, nsec).UTC()
|
|
newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond())
|
|
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.Format(RFC3339+" MST"))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSecondsToLocalTime(t *testing.T) {
|
|
for _, test := range localtests {
|
|
sec := test.seconds
|
|
golden := &test.golden
|
|
tm := Unix(sec, 0)
|
|
newsec := tm.Unix()
|
|
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.Format(RFC3339+" MST"))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNanosecondsToLocalTime(t *testing.T) {
|
|
for _, test := range nanolocaltests {
|
|
golden := &test.golden
|
|
nsec := test.seconds*1e9 + int64(golden.Nanosecond)
|
|
tm := Unix(0, nsec)
|
|
newnsec := tm.Unix()*1e9 + int64(tm.Nanosecond())
|
|
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.Format(RFC3339+" MST"))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSecondsToUTCAndBack(t *testing.T) {
|
|
f := func(sec int64) bool { return Unix(sec, 0).UTC().Unix() == 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 {
|
|
t := Unix(0, nsec).UTC()
|
|
ns := t.Unix()*1e9 + int64(t.Nanosecond())
|
|
return ns == 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)
|
|
}
|
|
}
|
|
|
|
// The time routines provide no way to get absolute time
|
|
// (seconds since zero), but we need it to compute the right
|
|
// answer for bizarre roundings like "to the nearest 3 ns".
|
|
// Compute as t - year1 = (t - 1970) + (1970 - 2001) + (2001 - 1).
|
|
// t - 1970 is returned by Unix and Nanosecond.
|
|
// 1970 - 2001 is -(31*365+8)*86400 = -978307200 seconds.
|
|
// 2001 - 1 is 2000*365.2425*86400 = 63113904000 seconds.
|
|
const unixToZero = -978307200 + 63113904000
|
|
|
|
// abs returns the absolute time stored in t, as seconds and nanoseconds.
|
|
func abs(t Time) (sec, nsec int64) {
|
|
unix := t.Unix()
|
|
nano := t.Nanosecond()
|
|
return unix + unixToZero, int64(nano)
|
|
}
|
|
|
|
// absString returns abs as a decimal string.
|
|
func absString(t Time) string {
|
|
sec, nsec := abs(t)
|
|
if sec < 0 {
|
|
sec = -sec
|
|
nsec = -nsec
|
|
if nsec < 0 {
|
|
nsec += 1e9
|
|
sec--
|
|
}
|
|
return fmt.Sprintf("-%d%09d", sec, nsec)
|
|
}
|
|
return fmt.Sprintf("%d%09d", sec, nsec)
|
|
}
|
|
|
|
var truncateRoundTests = []struct {
|
|
t Time
|
|
d Duration
|
|
}{
|
|
{Date(-1, January, 1, 12, 15, 30, 5e8, UTC), 3},
|
|
{Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3},
|
|
{Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second},
|
|
{Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second},
|
|
}
|
|
|
|
func TestTruncateRound(t *testing.T) {
|
|
var (
|
|
bsec = new(big.Int)
|
|
bnsec = new(big.Int)
|
|
bd = new(big.Int)
|
|
bt = new(big.Int)
|
|
br = new(big.Int)
|
|
bq = new(big.Int)
|
|
b1e9 = new(big.Int)
|
|
)
|
|
|
|
b1e9.SetInt64(1e9)
|
|
|
|
testOne := func(ti, tns, di int64) bool {
|
|
t0 := Unix(ti, int64(tns)).UTC()
|
|
d := Duration(di)
|
|
if d < 0 {
|
|
d = -d
|
|
}
|
|
if d <= 0 {
|
|
d = 1
|
|
}
|
|
|
|
// Compute bt = absolute nanoseconds.
|
|
sec, nsec := abs(t0)
|
|
bsec.SetInt64(sec)
|
|
bnsec.SetInt64(nsec)
|
|
bt.Mul(bsec, b1e9)
|
|
bt.Add(bt, bnsec)
|
|
|
|
// Compute quotient and remainder mod d.
|
|
bd.SetInt64(int64(d))
|
|
bq.DivMod(bt, bd, br)
|
|
|
|
// To truncate, subtract remainder.
|
|
// br is < d, so it fits in an int64.
|
|
r := br.Int64()
|
|
t1 := t0.Add(-Duration(r))
|
|
|
|
// Check that time.Truncate works.
|
|
if trunc := t0.Truncate(d); trunc != t1 {
|
|
t.Errorf("Time.Truncate(%s, %s) = %s, want %s\n"+
|
|
"%v trunc %v =\n%v want\n%v",
|
|
t0.Format(RFC3339Nano), d, trunc, t1.Format(RFC3339Nano),
|
|
absString(t0), int64(d), absString(trunc), absString(t1))
|
|
return false
|
|
}
|
|
|
|
// To round, add d back if remainder r > d/2 or r == exactly d/2.
|
|
// The commented out code would round half to even instead of up,
|
|
// but that makes it time-zone dependent, which is a bit strange.
|
|
if r > int64(d)/2 || r+r == int64(d) /*&& bq.Bit(0) == 1*/ {
|
|
t1 = t1.Add(Duration(d))
|
|
}
|
|
|
|
// Check that time.Round works.
|
|
if rnd := t0.Round(d); rnd != t1 {
|
|
t.Errorf("Time.Round(%s, %s) = %s, want %s\n"+
|
|
"%v round %v =\n%v want\n%v",
|
|
t0.Format(RFC3339Nano), d, rnd, t1.Format(RFC3339Nano),
|
|
absString(t0), int64(d), absString(rnd), absString(t1))
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// manual test cases
|
|
for _, tt := range truncateRoundTests {
|
|
testOne(tt.t.Unix(), int64(tt.t.Nanosecond()), int64(tt.d))
|
|
}
|
|
|
|
// exhaustive near 0
|
|
for i := 0; i < 100; i++ {
|
|
for j := 1; j < 100; j++ {
|
|
testOne(unixToZero, int64(i), int64(j))
|
|
testOne(unixToZero, -int64(i), int64(j))
|
|
if t.Failed() {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if t.Failed() {
|
|
return
|
|
}
|
|
|
|
// randomly generated test cases
|
|
cfg := &quick.Config{MaxCount: 100000}
|
|
if testing.Short() {
|
|
cfg.MaxCount = 1000
|
|
}
|
|
|
|
// divisors of Second
|
|
f1 := func(ti int64, tns int32, logdi int32) bool {
|
|
d := Duration(1)
|
|
a, b := uint(logdi%9), (logdi>>16)%9
|
|
d <<= a
|
|
for i := 0; i < int(b); i++ {
|
|
d *= 5
|
|
}
|
|
return testOne(ti, int64(tns), int64(d))
|
|
}
|
|
quick.Check(f1, cfg)
|
|
|
|
// multiples of Second
|
|
f2 := func(ti int64, tns int32, di int32) bool {
|
|
d := Duration(di) * Second
|
|
if d < 0 {
|
|
d = -d
|
|
}
|
|
return testOne(ti, int64(tns), int64(d))
|
|
}
|
|
quick.Check(f2, cfg)
|
|
|
|
// halfway cases
|
|
f3 := func(tns, di int64) bool {
|
|
di &= 0xfffffffe
|
|
if di == 0 {
|
|
di = 2
|
|
}
|
|
tns -= tns % di
|
|
if tns < 0 {
|
|
tns += di / 2
|
|
} else {
|
|
tns -= di / 2
|
|
}
|
|
return testOne(0, tns, di)
|
|
}
|
|
quick.Check(f3, cfg)
|
|
|
|
// full generality
|
|
f4 := func(ti int64, tns int32, di int64) bool {
|
|
return testOne(ti, int64(tns), di)
|
|
}
|
|
quick.Check(f4, cfg)
|
|
}
|
|
|
|
type TimeFormatTest struct {
|
|
time Time
|
|
formattedValue string
|
|
}
|
|
|
|
var rfc3339Formats = []TimeFormatTest{
|
|
{Date(2008, 9, 17, 20, 4, 26, 0, UTC), "2008-09-17T20:04:26Z"},
|
|
{Date(1994, 9, 17, 20, 4, 26, 0, FixedZone("EST", -18000)), "1994-09-17T20:04:26-05:00"},
|
|
{Date(2000, 12, 26, 1, 15, 6, 0, FixedZone("OTO", 15600)), "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 21:00 PST"},
|
|
{"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"},
|
|
{"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"},
|
|
{"RFC1123Z", RFC1123Z, "Wed, 04 Feb 2009 21:00:57 -0800"},
|
|
{"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"},
|
|
{"RFC3339Nano", RFC3339Nano, "2009-02-04T21:00:57.0123456-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.012345600"},
|
|
}
|
|
|
|
func TestFormat(t *testing.T) {
|
|
// The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2010
|
|
time := Unix(0, 1233810057012345600)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFormatShortYear(t *testing.T) {
|
|
years := []int{
|
|
-100001, -100000, -99999,
|
|
-10001, -10000, -9999,
|
|
-1001, -1000, -999,
|
|
-101, -100, -99,
|
|
-11, -10, -9,
|
|
-1, 0, 1,
|
|
9, 10, 11,
|
|
99, 100, 101,
|
|
999, 1000, 1001,
|
|
9999, 10000, 10001,
|
|
99999, 100000, 100001,
|
|
}
|
|
|
|
for _, y := range years {
|
|
time := Date(y, January, 1, 0, 0, 0, 0, UTC)
|
|
result := time.Format("2006.01.02")
|
|
var want string
|
|
if y < 0 {
|
|
// The 4 in %04d counts the - sign, so print -y instead
|
|
// and introduce our own - sign.
|
|
want = fmt.Sprintf("-%04d.%02d.%02d", -y, 1, 1)
|
|
} else {
|
|
want = fmt.Sprintf("%04d.%02d.%02d", y, 1, 1)
|
|
}
|
|
if result != want {
|
|
t.Errorf("(jan 1 %d).Format(\"2006.01.02\") = %q, want %q", y, result, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type ParseTest struct {
|
|
name string
|
|
format string
|
|
value string
|
|
hasTZ bool // contains a time zone
|
|
hasWD bool // contains a weekday
|
|
yearSign int // 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},
|
|
{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57 -0800", 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},
|
|
{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57.01234 -0800", true, true, 1, 5},
|
|
{"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9},
|
|
{"custom: \"2006-01-02 15:04:05\"", "2006-01-02 15:04:05", "2010-02-04 21:00:57.0", false, false, 1, 0},
|
|
// 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},
|
|
// Case 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},
|
|
|
|
// Accept any number of fractional second digits (including none) for .999...
|
|
// In Go 1, .999... was completely ignored in the format, meaning the first two
|
|
// cases would succeed, but the next four would not. Go 1.1 accepts all six.
|
|
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
|
|
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
|
|
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
|
|
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
|
|
{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
|
|
{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
|
|
}
|
|
|
|
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() != February {
|
|
t.Errorf("%s: bad month: %s not %s", test.name, time.Month(), February)
|
|
}
|
|
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.ParseUint("012345678"[:test.fracDigits]+"000000000"[:9-test.fracDigits], 10, 0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if time.Nanosecond() != int(nanosec) {
|
|
t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond(), nanosec)
|
|
}
|
|
name, offset := time.Zone()
|
|
if test.hasTZ && offset != -28800 {
|
|
t.Errorf("%s: bad tz offset: %s %d not %d", test.name, name, offset, -28800)
|
|
}
|
|
if test.hasWD && time.Weekday() != Thursday {
|
|
t.Errorf("%s: bad weekday: %s not %s", test.name, time.Weekday(), Thursday)
|
|
}
|
|
}
|
|
|
|
func TestFormatAndParse(t *testing.T) {
|
|
const fmt = "Mon MST " + RFC3339 // all fields
|
|
f := func(sec int64) bool {
|
|
t1 := Unix(sec, 0)
|
|
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 t1.Unix() != t2.Unix() || t1.Nanosecond() != t2.Nanosecond() {
|
|
t.Errorf("FormatAndParse %d: %q(%d) %q(%d)", sec, t1, t1.Unix(), t2, t2.Unix())
|
|
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.Error(), 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 := Date(0, January, 1, 12, 0, 0, 0, UTC)
|
|
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 := Date(0, January, 1, 0, 0, 0, 0, UTC)
|
|
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, "Thu Feb 02 16:10:03 -0500 2006")
|
|
if err != nil {
|
|
t.Fatal("error parsing date:", err)
|
|
}
|
|
expect := "Thu 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("got %s; expect %s", str, expect)
|
|
}
|
|
}
|
|
|
|
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
|
|
_, offset := time.Zone()
|
|
if offset != expected {
|
|
t.Errorf("ZoneOffset = %d, want %d", offset, expected)
|
|
}
|
|
}
|
|
|
|
type ISOWeekTest struct {
|
|
year int // year
|
|
month, day int // month and day
|
|
yex int // expected year
|
|
wex int // expected week
|
|
}
|
|
|
|
var isoWeekTests = []ISOWeekTest{
|
|
{1981, 1, 1, 1981, 1}, {1982, 1, 1, 1981, 53}, {1983, 1, 1, 1982, 52},
|
|
{1984, 1, 1, 1983, 52}, {1985, 1, 1, 1985, 1}, {1986, 1, 1, 1986, 1},
|
|
{1987, 1, 1, 1987, 1}, {1988, 1, 1, 1987, 53}, {1989, 1, 1, 1988, 52},
|
|
{1990, 1, 1, 1990, 1}, {1991, 1, 1, 1991, 1}, {1992, 1, 1, 1992, 1},
|
|
{1993, 1, 1, 1992, 53}, {1994, 1, 1, 1993, 52}, {1995, 1, 2, 1995, 1},
|
|
{1996, 1, 1, 1996, 1}, {1996, 1, 7, 1996, 1}, {1996, 1, 8, 1996, 2},
|
|
{1997, 1, 1, 1997, 1}, {1998, 1, 1, 1998, 1}, {1999, 1, 1, 1998, 53},
|
|
{2000, 1, 1, 1999, 52}, {2001, 1, 1, 2001, 1}, {2002, 1, 1, 2002, 1},
|
|
{2003, 1, 1, 2003, 1}, {2004, 1, 1, 2004, 1}, {2005, 1, 1, 2004, 53},
|
|
{2006, 1, 1, 2005, 52}, {2007, 1, 1, 2007, 1}, {2008, 1, 1, 2008, 1},
|
|
{2009, 1, 1, 2009, 1}, {2010, 1, 1, 2009, 53}, {2010, 1, 1, 2009, 53},
|
|
{2011, 1, 1, 2010, 52}, {2011, 1, 2, 2010, 52}, {2011, 1, 3, 2011, 1},
|
|
{2011, 1, 4, 2011, 1}, {2011, 1, 5, 2011, 1}, {2011, 1, 6, 2011, 1},
|
|
{2011, 1, 7, 2011, 1}, {2011, 1, 8, 2011, 1}, {2011, 1, 9, 2011, 1},
|
|
{2011, 1, 10, 2011, 2}, {2011, 1, 11, 2011, 2}, {2011, 6, 12, 2011, 23},
|
|
{2011, 6, 13, 2011, 24}, {2011, 12, 25, 2011, 51}, {2011, 12, 26, 2011, 52},
|
|
{2011, 12, 27, 2011, 52}, {2011, 12, 28, 2011, 52}, {2011, 12, 29, 2011, 52},
|
|
{2011, 12, 30, 2011, 52}, {2011, 12, 31, 2011, 52}, {1995, 1, 1, 1994, 52},
|
|
{2012, 1, 1, 2011, 52}, {2012, 1, 2, 2012, 1}, {2012, 1, 8, 2012, 1},
|
|
{2012, 1, 9, 2012, 2}, {2012, 12, 23, 2012, 51}, {2012, 12, 24, 2012, 52},
|
|
{2012, 12, 30, 2012, 52}, {2012, 12, 31, 2013, 1}, {2013, 1, 1, 2013, 1},
|
|
{2013, 1, 6, 2013, 1}, {2013, 1, 7, 2013, 2}, {2013, 12, 22, 2013, 51},
|
|
{2013, 12, 23, 2013, 52}, {2013, 12, 29, 2013, 52}, {2013, 12, 30, 2014, 1},
|
|
{2014, 1, 1, 2014, 1}, {2014, 1, 5, 2014, 1}, {2014, 1, 6, 2014, 2},
|
|
{2015, 1, 1, 2015, 1}, {2016, 1, 1, 2015, 53}, {2017, 1, 1, 2016, 52},
|
|
{2018, 1, 1, 2018, 1}, {2019, 1, 1, 2019, 1}, {2020, 1, 1, 2020, 1},
|
|
{2021, 1, 1, 2020, 53}, {2022, 1, 1, 2021, 52}, {2023, 1, 1, 2022, 52},
|
|
{2024, 1, 1, 2024, 1}, {2025, 1, 1, 2025, 1}, {2026, 1, 1, 2026, 1},
|
|
{2027, 1, 1, 2026, 53}, {2028, 1, 1, 2027, 52}, {2029, 1, 1, 2029, 1},
|
|
{2030, 1, 1, 2030, 1}, {2031, 1, 1, 2031, 1}, {2032, 1, 1, 2032, 1},
|
|
{2033, 1, 1, 2032, 53}, {2034, 1, 1, 2033, 52}, {2035, 1, 1, 2035, 1},
|
|
{2036, 1, 1, 2036, 1}, {2037, 1, 1, 2037, 1}, {2038, 1, 1, 2037, 53},
|
|
{2039, 1, 1, 2038, 52}, {2040, 1, 1, 2039, 52},
|
|
}
|
|
|
|
func TestISOWeek(t *testing.T) {
|
|
// Selected dates and corner cases
|
|
for _, wt := range isoWeekTests {
|
|
dt := Date(wt.year, Month(wt.month), wt.day, 0, 0, 0, 0, UTC)
|
|
y, w := dt.ISOWeek()
|
|
if w != wt.wex || y != wt.yex {
|
|
t.Errorf("got %d/%d; expected %d/%d for %d-%02d-%02d",
|
|
y, w, wt.yex, wt.wex, wt.year, wt.month, wt.day)
|
|
}
|
|
}
|
|
|
|
// The only real invariant: Jan 04 is in week 1
|
|
for year := 1950; year < 2100; year++ {
|
|
if y, w := Date(year, January, 4, 0, 0, 0, 0, UTC).ISOWeek(); y != year || w != 1 {
|
|
t.Errorf("got %d/%d; expected %d/1 for Jan 04", y, w, year)
|
|
}
|
|
}
|
|
}
|
|
|
|
type YearDayTest struct {
|
|
year, month, day int
|
|
yday int
|
|
}
|
|
|
|
// Test YearDay in several different scenarios
|
|
// and corner cases
|
|
var yearDayTests = []YearDayTest{
|
|
// Non-leap-year tests
|
|
{2007, 1, 1, 1},
|
|
{2007, 1, 15, 15},
|
|
{2007, 2, 1, 32},
|
|
{2007, 2, 15, 46},
|
|
{2007, 3, 1, 60},
|
|
{2007, 3, 15, 74},
|
|
{2007, 4, 1, 91},
|
|
{2007, 12, 31, 365},
|
|
|
|
// Leap-year tests
|
|
{2008, 1, 1, 1},
|
|
{2008, 1, 15, 15},
|
|
{2008, 2, 1, 32},
|
|
{2008, 2, 15, 46},
|
|
{2008, 3, 1, 61},
|
|
{2008, 3, 15, 75},
|
|
{2008, 4, 1, 92},
|
|
{2008, 12, 31, 366},
|
|
|
|
// Looks like leap-year (but isn't) tests
|
|
{1900, 1, 1, 1},
|
|
{1900, 1, 15, 15},
|
|
{1900, 2, 1, 32},
|
|
{1900, 2, 15, 46},
|
|
{1900, 3, 1, 60},
|
|
{1900, 3, 15, 74},
|
|
{1900, 4, 1, 91},
|
|
{1900, 12, 31, 365},
|
|
|
|
// Year one tests (non-leap)
|
|
{1, 1, 1, 1},
|
|
{1, 1, 15, 15},
|
|
{1, 2, 1, 32},
|
|
{1, 2, 15, 46},
|
|
{1, 3, 1, 60},
|
|
{1, 3, 15, 74},
|
|
{1, 4, 1, 91},
|
|
{1, 12, 31, 365},
|
|
|
|
// Year minus one tests (non-leap)
|
|
{-1, 1, 1, 1},
|
|
{-1, 1, 15, 15},
|
|
{-1, 2, 1, 32},
|
|
{-1, 2, 15, 46},
|
|
{-1, 3, 1, 60},
|
|
{-1, 3, 15, 74},
|
|
{-1, 4, 1, 91},
|
|
{-1, 12, 31, 365},
|
|
|
|
// 400 BC tests (leap-year)
|
|
{-400, 1, 1, 1},
|
|
{-400, 1, 15, 15},
|
|
{-400, 2, 1, 32},
|
|
{-400, 2, 15, 46},
|
|
{-400, 3, 1, 61},
|
|
{-400, 3, 15, 75},
|
|
{-400, 4, 1, 92},
|
|
{-400, 12, 31, 366},
|
|
|
|
// Special Cases
|
|
|
|
// Gregorian calendar change (no effect)
|
|
{1582, 10, 4, 277},
|
|
{1582, 10, 15, 288},
|
|
}
|
|
|
|
// Check to see if YearDay is location sensitive
|
|
var yearDayLocations = []*Location{
|
|
FixedZone("UTC-8", -8*60*60),
|
|
FixedZone("UTC-4", -4*60*60),
|
|
UTC,
|
|
FixedZone("UTC+4", 4*60*60),
|
|
FixedZone("UTC+8", 8*60*60),
|
|
}
|
|
|
|
func TestYearDay(t *testing.T) {
|
|
for _, loc := range yearDayLocations {
|
|
for _, ydt := range yearDayTests {
|
|
dt := Date(ydt.year, Month(ydt.month), ydt.day, 0, 0, 0, 0, loc)
|
|
yday := dt.YearDay()
|
|
if yday != ydt.yday {
|
|
t.Errorf("got %d, expected %d for %d-%02d-%02d in %v",
|
|
yday, ydt.yday, ydt.year, ydt.month, ydt.day, loc)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var durationTests = []struct {
|
|
str string
|
|
d Duration
|
|
}{
|
|
{"0", 0},
|
|
{"1ns", 1 * Nanosecond},
|
|
{"1.1us", 1100 * Nanosecond},
|
|
{"2.2ms", 2200 * Microsecond},
|
|
{"3.3s", 3300 * Millisecond},
|
|
{"4m5s", 4*Minute + 5*Second},
|
|
{"4m5.001s", 4*Minute + 5001*Millisecond},
|
|
{"5h6m7.001s", 5*Hour + 6*Minute + 7001*Millisecond},
|
|
{"8m0.000000001s", 8*Minute + 1*Nanosecond},
|
|
{"2562047h47m16.854775807s", 1<<63 - 1},
|
|
{"-2562047h47m16.854775808s", -1 << 63},
|
|
}
|
|
|
|
func TestDurationString(t *testing.T) {
|
|
for _, tt := range durationTests {
|
|
if str := tt.d.String(); str != tt.str {
|
|
t.Errorf("Duration(%d).String() = %s, want %s", int64(tt.d), str, tt.str)
|
|
}
|
|
if tt.d > 0 {
|
|
if str := (-tt.d).String(); str != "-"+tt.str {
|
|
t.Errorf("Duration(%d).String() = %s, want %s", int64(-tt.d), str, "-"+tt.str)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var dateTests = []struct {
|
|
year, month, day, hour, min, sec, nsec int
|
|
z *Location
|
|
unix int64
|
|
}{
|
|
{2011, 11, 6, 1, 0, 0, 0, Local, 1320566400}, // 1:00:00 PDT
|
|
{2011, 11, 6, 1, 59, 59, 0, Local, 1320569999}, // 1:59:59 PDT
|
|
{2011, 11, 6, 2, 0, 0, 0, Local, 1320573600}, // 2:00:00 PST
|
|
|
|
{2011, 3, 13, 1, 0, 0, 0, Local, 1300006800}, // 1:00:00 PST
|
|
{2011, 3, 13, 1, 59, 59, 0, Local, 1300010399}, // 1:59:59 PST
|
|
{2011, 3, 13, 3, 0, 0, 0, Local, 1300010400}, // 3:00:00 PDT
|
|
{2011, 3, 13, 2, 30, 0, 0, Local, 1300008600}, // 2:30:00 PDT ≡ 1:30 PST
|
|
|
|
// Many names for Fri Nov 18 7:56:35 PST 2011
|
|
{2011, 11, 18, 7, 56, 35, 0, Local, 1321631795}, // Nov 18 7:56:35
|
|
{2011, 11, 19, -17, 56, 35, 0, Local, 1321631795}, // Nov 19 -17:56:35
|
|
{2011, 11, 17, 31, 56, 35, 0, Local, 1321631795}, // Nov 17 31:56:35
|
|
{2011, 11, 18, 6, 116, 35, 0, Local, 1321631795}, // Nov 18 6:116:35
|
|
{2011, 10, 49, 7, 56, 35, 0, Local, 1321631795}, // Oct 49 7:56:35
|
|
{2011, 11, 18, 7, 55, 95, 0, Local, 1321631795}, // Nov 18 7:55:95
|
|
{2011, 11, 18, 7, 56, 34, 1e9, Local, 1321631795}, // Nov 18 7:56:34 + 10⁹ns
|
|
{2011, 12, -12, 7, 56, 35, 0, Local, 1321631795}, // Dec -21 7:56:35
|
|
{2012, 1, -43, 7, 56, 35, 0, Local, 1321631795}, // Jan -52 7:56:35 2012
|
|
{2012, int(January - 2), 18, 7, 56, 35, 0, Local, 1321631795}, // (Jan-2) 18 7:56:35 2012
|
|
{2010, int(December + 11), 18, 7, 56, 35, 0, Local, 1321631795}, // (Dec+11) 18 7:56:35 2010
|
|
}
|
|
|
|
func TestDate(t *testing.T) {
|
|
for _, tt := range dateTests {
|
|
time := Date(tt.year, Month(tt.month), tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z)
|
|
want := Unix(tt.unix, 0)
|
|
if !time.Equal(want) {
|
|
t.Errorf("Date(%d, %d, %d, %d, %d, %d, %d, %s) = %v, want %v",
|
|
tt.year, tt.month, tt.day, tt.hour, tt.min, tt.sec, tt.nsec, tt.z,
|
|
time, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Several ways of getting from
|
|
// Fri Nov 18 7:56:35 PST 2011
|
|
// to
|
|
// Thu Mar 19 7:56:35 PST 2016
|
|
var addDateTests = []struct {
|
|
years, months, days int
|
|
}{
|
|
{4, 4, 1},
|
|
{3, 16, 1},
|
|
{3, 15, 30},
|
|
{5, -6, -18 - 30 - 12},
|
|
}
|
|
|
|
func TestAddDate(t *testing.T) {
|
|
t0 := Date(2011, 11, 18, 7, 56, 35, 0, UTC)
|
|
t1 := Date(2016, 3, 19, 7, 56, 35, 0, UTC)
|
|
for _, at := range addDateTests {
|
|
time := t0.AddDate(at.years, at.months, at.days)
|
|
if !time.Equal(t1) {
|
|
t.Errorf("AddDate(%d, %d, %d) = %v, want %v",
|
|
at.years, at.months, at.days,
|
|
time, t1)
|
|
}
|
|
}
|
|
}
|
|
|
|
var daysInTests = []struct {
|
|
year, month, di int
|
|
}{
|
|
{2011, 1, 31}, // January, first month, 31 days
|
|
{2011, 2, 28}, // February, non-leap year, 28 days
|
|
{2012, 2, 29}, // February, leap year, 29 days
|
|
{2011, 6, 30}, // June, 30 days
|
|
{2011, 12, 31}, // December, last month, 31 days
|
|
}
|
|
|
|
func TestDaysIn(t *testing.T) {
|
|
// The daysIn function is not exported.
|
|
// Test the daysIn function via the `var DaysIn = daysIn`
|
|
// statement in the internal_test.go file.
|
|
for _, tt := range daysInTests {
|
|
di := DaysIn(Month(tt.month), tt.year)
|
|
if di != tt.di {
|
|
t.Errorf("got %d; expected %d for %d-%02d",
|
|
di, tt.di, tt.year, tt.month)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddToExactSecond(t *testing.T) {
|
|
// Add an amount to the current time to round it up to the next exact second.
|
|
// This test checks that the nsec field still lies within the range [0, 999999999].
|
|
t1 := Now()
|
|
t2 := t1.Add(Second - Duration(t1.Nanosecond()))
|
|
sec := (t1.Second() + 1) % 60
|
|
if t2.Second() != sec || t2.Nanosecond() != 0 {
|
|
t.Errorf("sec = %d, nsec = %d, want sec = %d, nsec = 0", t2.Second(), t2.Nanosecond(), sec)
|
|
}
|
|
}
|
|
|
|
func equalTimeAndZone(a, b Time) bool {
|
|
aname, aoffset := a.Zone()
|
|
bname, boffset := b.Zone()
|
|
return a.Equal(b) && aoffset == boffset && aname == bname
|
|
}
|
|
|
|
var gobTests = []Time{
|
|
Date(0, 1, 2, 3, 4, 5, 6, UTC),
|
|
Date(7, 8, 9, 10, 11, 12, 13, FixedZone("", 0)),
|
|
Unix(81985467080890095, 0x76543210), // Time.sec: 0x0123456789ABCDEF
|
|
{}, // nil location
|
|
Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", 32767*60)),
|
|
Date(1, 2, 3, 4, 5, 6, 7, FixedZone("", -32768*60)),
|
|
}
|
|
|
|
func TestTimeGob(t *testing.T) {
|
|
var b bytes.Buffer
|
|
enc := gob.NewEncoder(&b)
|
|
dec := gob.NewDecoder(&b)
|
|
for _, tt := range gobTests {
|
|
var gobtt Time
|
|
if err := enc.Encode(&tt); err != nil {
|
|
t.Errorf("%v gob Encode error = %q, want nil", tt, err)
|
|
} else if err := dec.Decode(&gobtt); err != nil {
|
|
t.Errorf("%v gob Decode error = %q, want nil", tt, err)
|
|
} else if !equalTimeAndZone(gobtt, tt) {
|
|
t.Errorf("Decoded time = %v, want %v", gobtt, tt)
|
|
}
|
|
b.Reset()
|
|
}
|
|
}
|
|
|
|
var invalidEncodingTests = []struct {
|
|
bytes []byte
|
|
want string
|
|
}{
|
|
{[]byte{}, "Time.GobDecode: no data"},
|
|
{[]byte{0, 2, 3}, "Time.GobDecode: unsupported version"},
|
|
{[]byte{1, 2, 3}, "Time.GobDecode: invalid length"},
|
|
}
|
|
|
|
func TestInvalidTimeGob(t *testing.T) {
|
|
for _, tt := range invalidEncodingTests {
|
|
var ignored Time
|
|
err := ignored.GobDecode(tt.bytes)
|
|
if err == nil || err.Error() != tt.want {
|
|
t.Errorf("time.GobDecode(%#v) error = %v, want %v", tt.bytes, err, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
var notEncodableTimes = []struct {
|
|
time Time
|
|
want string
|
|
}{
|
|
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.GobEncode: zone offset has fractional minute"},
|
|
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.GobEncode: unexpected zone offset"},
|
|
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.GobEncode: unexpected zone offset"},
|
|
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.GobEncode: unexpected zone offset"},
|
|
}
|
|
|
|
func TestNotGobEncodableTime(t *testing.T) {
|
|
for _, tt := range notEncodableTimes {
|
|
_, err := tt.time.GobEncode()
|
|
if err == nil || err.Error() != tt.want {
|
|
t.Errorf("%v GobEncode error = %v, want %v", tt.time, err, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
var jsonTests = []struct {
|
|
time Time
|
|
json string
|
|
}{
|
|
{Date(9999, 4, 12, 23, 20, 50, 520*1e6, UTC), `"9999-04-12T23:20:50.52Z"`},
|
|
{Date(1996, 12, 19, 16, 39, 57, 0, Local), `"1996-12-19T16:39:57-08:00"`},
|
|
{Date(0, 1, 1, 0, 0, 0, 1, FixedZone("", 1*60)), `"0000-01-01T00:00:00.000000001+00:01"`},
|
|
}
|
|
|
|
func TestTimeJSON(t *testing.T) {
|
|
for _, tt := range jsonTests {
|
|
var jsonTime Time
|
|
|
|
if jsonBytes, err := json.Marshal(tt.time); err != nil {
|
|
t.Errorf("%v json.Marshal error = %v, want nil", tt.time, err)
|
|
} else if string(jsonBytes) != tt.json {
|
|
t.Errorf("%v JSON = %#q, want %#q", tt.time, string(jsonBytes), tt.json)
|
|
} else if err = json.Unmarshal(jsonBytes, &jsonTime); err != nil {
|
|
t.Errorf("%v json.Unmarshal error = %v, want nil", tt.time, err)
|
|
} else if !equalTimeAndZone(jsonTime, tt.time) {
|
|
t.Errorf("Unmarshaled time = %v, want %v", jsonTime, tt.time)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInvalidTimeJSON(t *testing.T) {
|
|
var tt Time
|
|
err := json.Unmarshal([]byte(`{"now is the time":"buddy"}`), &tt)
|
|
_, isParseErr := err.(*ParseError)
|
|
if !isParseErr {
|
|
t.Errorf("expected *time.ParseError unmarshaling JSON, got %v", err)
|
|
}
|
|
}
|
|
|
|
var notJSONEncodableTimes = []struct {
|
|
time Time
|
|
want string
|
|
}{
|
|
{Date(10000, 1, 1, 0, 0, 0, 0, UTC), "Time.MarshalJSON: year outside of range [0,9999]"},
|
|
{Date(-1, 1, 1, 0, 0, 0, 0, UTC), "Time.MarshalJSON: year outside of range [0,9999]"},
|
|
}
|
|
|
|
func TestNotJSONEncodableTime(t *testing.T) {
|
|
for _, tt := range notJSONEncodableTimes {
|
|
_, err := tt.time.MarshalJSON()
|
|
if err == nil || err.Error() != tt.want {
|
|
t.Errorf("%v MarshalJSON error = %v, want %v", tt.time, err, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
var parseDurationTests = []struct {
|
|
in string
|
|
ok bool
|
|
want Duration
|
|
}{
|
|
// simple
|
|
{"0", true, 0},
|
|
{"5s", true, 5 * Second},
|
|
{"30s", true, 30 * Second},
|
|
{"1478s", true, 1478 * Second},
|
|
// sign
|
|
{"-5s", true, -5 * Second},
|
|
{"+5s", true, 5 * Second},
|
|
{"-0", true, 0},
|
|
{"+0", true, 0},
|
|
// decimal
|
|
{"5.0s", true, 5 * Second},
|
|
{"5.6s", true, 5*Second + 600*Millisecond},
|
|
{"5.s", true, 5 * Second},
|
|
{".5s", true, 500 * Millisecond},
|
|
{"1.0s", true, 1 * Second},
|
|
{"1.00s", true, 1 * Second},
|
|
{"1.004s", true, 1*Second + 4*Millisecond},
|
|
{"1.0040s", true, 1*Second + 4*Millisecond},
|
|
{"100.00100s", true, 100*Second + 1*Millisecond},
|
|
// different units
|
|
{"10ns", true, 10 * Nanosecond},
|
|
{"11us", true, 11 * Microsecond},
|
|
{"12µs", true, 12 * Microsecond}, // U+00B5
|
|
{"12μs", true, 12 * Microsecond}, // U+03BC
|
|
{"13ms", true, 13 * Millisecond},
|
|
{"14s", true, 14 * Second},
|
|
{"15m", true, 15 * Minute},
|
|
{"16h", true, 16 * Hour},
|
|
// composite durations
|
|
{"3h30m", true, 3*Hour + 30*Minute},
|
|
{"10.5s4m", true, 4*Minute + 10*Second + 500*Millisecond},
|
|
{"-2m3.4s", true, -(2*Minute + 3*Second + 400*Millisecond)},
|
|
{"1h2m3s4ms5us6ns", true, 1*Hour + 2*Minute + 3*Second + 4*Millisecond + 5*Microsecond + 6*Nanosecond},
|
|
{"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond},
|
|
// large value
|
|
{"52763797000ns", true, 52763797000 * Nanosecond},
|
|
|
|
// errors
|
|
{"", false, 0},
|
|
{"3", false, 0},
|
|
{"-", false, 0},
|
|
{"s", false, 0},
|
|
{".", false, 0},
|
|
{"-.", false, 0},
|
|
{".s", false, 0},
|
|
{"+.s", false, 0},
|
|
}
|
|
|
|
func TestParseDuration(t *testing.T) {
|
|
for _, tc := range parseDurationTests {
|
|
d, err := ParseDuration(tc.in)
|
|
if tc.ok && (err != nil || d != tc.want) {
|
|
t.Errorf("ParseDuration(%q) = %v, %v, want %v, nil", tc.in, d, err, tc.want)
|
|
} else if !tc.ok && err == nil {
|
|
t.Errorf("ParseDuration(%q) = _, nil, want _, non-nil", tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseDurationRoundTrip(t *testing.T) {
|
|
for i := 0; i < 100; i++ {
|
|
// Resolutions finer than milliseconds will result in
|
|
// imprecise round-trips.
|
|
d0 := Duration(rand.Int31()) * Millisecond
|
|
s := d0.String()
|
|
d1, err := ParseDuration(s)
|
|
if err != nil || d0 != d1 {
|
|
t.Errorf("round-trip failed: %d => %q => %d, %v", d0, s, d1, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
t Time
|
|
u int64
|
|
)
|
|
|
|
var mallocTest = []struct {
|
|
count int
|
|
desc string
|
|
fn func()
|
|
}{
|
|
{0, `time.Now()`, func() { t = Now() }},
|
|
{0, `time.Now().UnixNano()`, func() { u = Now().UnixNano() }},
|
|
}
|
|
|
|
func TestCountMallocs(t *testing.T) {
|
|
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
|
|
for _, mt := range mallocTest {
|
|
const N = 100
|
|
memstats := new(runtime.MemStats)
|
|
runtime.ReadMemStats(memstats)
|
|
mallocs := 0 - memstats.Mallocs
|
|
for i := 0; i < N; i++ {
|
|
mt.fn()
|
|
}
|
|
runtime.ReadMemStats(memstats)
|
|
mallocs += memstats.Mallocs
|
|
if mallocs/N > uint64(mt.count) {
|
|
t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkNow(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
t = Now()
|
|
}
|
|
}
|
|
|
|
func BenchmarkNowUnixNano(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
u = Now().UnixNano()
|
|
}
|
|
}
|
|
|
|
func BenchmarkFormat(b *testing.B) {
|
|
t := Unix(1265346057, 0)
|
|
for i := 0; i < b.N; i++ {
|
|
t.Format("Mon Jan 2 15:04:05 2006")
|
|
}
|
|
}
|
|
|
|
func BenchmarkFormatNow(b *testing.B) {
|
|
// Like BenchmarkFormat, but easier, because the time zone
|
|
// lookup cache is optimized for the present.
|
|
t := Now()
|
|
for i := 0; i < b.N; i++ {
|
|
t.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")
|
|
}
|
|
}
|
|
|
|
func BenchmarkHour(b *testing.B) {
|
|
t := Now()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = t.Hour()
|
|
}
|
|
}
|
|
|
|
func BenchmarkSecond(b *testing.B) {
|
|
t := Now()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = t.Second()
|
|
}
|
|
}
|
|
|
|
func BenchmarkYear(b *testing.B) {
|
|
t := Now()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = t.Year()
|
|
}
|
|
}
|
|
|
|
func BenchmarkDay(b *testing.B) {
|
|
t := Now()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = t.Day()
|
|
}
|
|
}
|