1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package time_test
6
7import (
8	"fmt"
9	"strconv"
10	"strings"
11	"testing"
12	"testing/quick"
13	. "time"
14)
15
16type TimeFormatTest struct {
17	time           Time
18	formattedValue string
19}
20
21var rfc3339Formats = []TimeFormatTest{
22	{Date(2008, 9, 17, 20, 4, 26, 0, UTC), "2008-09-17T20:04:26Z"},
23	{Date(1994, 9, 17, 20, 4, 26, 0, FixedZone("EST", -18000)), "1994-09-17T20:04:26-05:00"},
24	{Date(2000, 12, 26, 1, 15, 6, 0, FixedZone("OTO", 15600)), "2000-12-26T01:15:06+04:20"},
25}
26
27func TestRFC3339Conversion(t *testing.T) {
28	for _, f := range rfc3339Formats {
29		if f.time.Format(RFC3339) != f.formattedValue {
30			t.Error("RFC3339:")
31			t.Errorf("  want=%+v", f.formattedValue)
32			t.Errorf("  have=%+v", f.time.Format(RFC3339))
33		}
34	}
35}
36
37type FormatTest struct {
38	name   string
39	format string
40	result string
41}
42
43var formatTests = []FormatTest{
44	{"ANSIC", ANSIC, "Wed Feb  4 21:00:57 2009"},
45	{"UnixDate", UnixDate, "Wed Feb  4 21:00:57 PST 2009"},
46	{"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"},
47	{"RFC822", RFC822, "04 Feb 09 21:00 PST"},
48	{"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"},
49	{"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"},
50	{"RFC1123Z", RFC1123Z, "Wed, 04 Feb 2009 21:00:57 -0800"},
51	{"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"},
52	{"RFC3339Nano", RFC3339Nano, "2009-02-04T21:00:57.0123456-08:00"},
53	{"Kitchen", Kitchen, "9:00PM"},
54	{"am/pm", "3pm", "9pm"},
55	{"AM/PM", "3PM", "9PM"},
56	{"two-digit year", "06 01 02", "09 02 04"},
57	// Three-letter months and days must not be followed by lower-case letter.
58	{"Janet", "Hi Janet, the Month is January", "Hi Janet, the Month is February"},
59	// Time stamps, Fractional seconds.
60	{"Stamp", Stamp, "Feb  4 21:00:57"},
61	{"StampMilli", StampMilli, "Feb  4 21:00:57.012"},
62	{"StampMicro", StampMicro, "Feb  4 21:00:57.012345"},
63	{"StampNano", StampNano, "Feb  4 21:00:57.012345600"},
64}
65
66func TestFormat(t *testing.T) {
67	// The numeric time represents Thu Feb  4 21:00:57.012345600 PST 2010
68	time := Unix(0, 1233810057012345600)
69	for _, test := range formatTests {
70		result := time.Format(test.format)
71		if result != test.result {
72			t.Errorf("%s expected %q got %q", test.name, test.result, result)
73		}
74	}
75}
76
77// issue 12440.
78func TestFormatSingleDigits(t *testing.T) {
79	time := Date(2001, 2, 3, 4, 5, 6, 700000000, UTC)
80	test := FormatTest{"single digit format", "3:4:5", "4:5:6"}
81	result := time.Format(test.format)
82	if result != test.result {
83		t.Errorf("%s expected %q got %q", test.name, test.result, result)
84	}
85}
86
87func TestFormatShortYear(t *testing.T) {
88	years := []int{
89		-100001, -100000, -99999,
90		-10001, -10000, -9999,
91		-1001, -1000, -999,
92		-101, -100, -99,
93		-11, -10, -9,
94		-1, 0, 1,
95		9, 10, 11,
96		99, 100, 101,
97		999, 1000, 1001,
98		9999, 10000, 10001,
99		99999, 100000, 100001,
100	}
101
102	for _, y := range years {
103		time := Date(y, January, 1, 0, 0, 0, 0, UTC)
104		result := time.Format("2006.01.02")
105		var want string
106		if y < 0 {
107			// The 4 in %04d counts the - sign, so print -y instead
108			// and introduce our own - sign.
109			want = fmt.Sprintf("-%04d.%02d.%02d", -y, 1, 1)
110		} else {
111			want = fmt.Sprintf("%04d.%02d.%02d", y, 1, 1)
112		}
113		if result != want {
114			t.Errorf("(jan 1 %d).Format(\"2006.01.02\") = %q, want %q", y, result, want)
115		}
116	}
117}
118
119type ParseTest struct {
120	name       string
121	format     string
122	value      string
123	hasTZ      bool // contains a time zone
124	hasWD      bool // contains a weekday
125	yearSign   int  // sign of year, -1 indicates the year is not present in the format
126	fracDigits int  // number of digits of fractional second
127}
128
129var parseTests = []ParseTest{
130	{"ANSIC", ANSIC, "Thu Feb  4 21:00:57 2010", false, true, 1, 0},
131	{"UnixDate", UnixDate, "Thu Feb  4 21:00:57 PST 2010", true, true, 1, 0},
132	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
133	{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1, 0},
134	{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0},
135	{"RFC1123", RFC1123, "Thu, 04 Feb 2010 22:00:57 PDT", true, true, 1, 0},
136	{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57 -0800", true, true, 1, 0},
137	{"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0},
138	{"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},
139	// Optional fractional seconds.
140	{"ANSIC", ANSIC, "Thu Feb  4 21:00:57.0 2010", false, true, 1, 1},
141	{"UnixDate", UnixDate, "Thu Feb  4 21:00:57.01 PST 2010", true, true, 1, 2},
142	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57.012 -0800 2010", true, true, 1, 3},
143	{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57.0123 PST", true, true, 1, 4},
144	{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57.01234 PST", true, true, 1, 5},
145	{"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57.01234 -0800", true, true, 1, 5},
146	{"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9},
147	{"custom: \"2006-01-02 15:04:05\"", "2006-01-02 15:04:05", "2010-02-04 21:00:57.0", false, false, 1, 0},
148	// Amount of white space should not matter.
149	{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0},
150	{"ANSIC", ANSIC, "Thu      Feb     4     21:00:57     2010", false, true, 1, 0},
151	// Case should not matter
152	{"ANSIC", ANSIC, "THU FEB 4 21:00:57 2010", false, true, 1, 0},
153	{"ANSIC", ANSIC, "thu feb 4 21:00:57 2010", false, true, 1, 0},
154	// Fractional seconds.
155	{"millisecond", "Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 21:00:57.012 2010", false, true, 1, 3},
156	{"microsecond", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb  4 21:00:57.012345 2010", false, true, 1, 6},
157	{"nanosecond", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb  4 21:00:57.012345678 2010", false, true, 1, 9},
158	// Leading zeros in other places should not be taken as fractional seconds.
159	{"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1},
160	{"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2},
161	// Month and day names only match when not followed by a lower-case letter.
162	{"Janet", "Hi Janet, the Month is January: Jan _2 15:04:05 2006", "Hi Janet, the Month is February: Feb  4 21:00:57 2010", false, true, 1, 0},
163
164	// GMT with offset.
165	{"GMT-8", UnixDate, "Fri Feb  5 05:00:57 GMT-8 2010", true, true, 1, 0},
166
167	// Accept any number of fractional second digits (including none) for .999...
168	// In Go 1, .999... was completely ignored in the format, meaning the first two
169	// cases would succeed, but the next four would not. Go 1.1 accepts all six.
170	{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
171	{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0},
172	{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
173	{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4},
174	{"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
175	{"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9},
176
177	// issue 4502.
178	{"", StampNano, "Feb  4 21:00:57.012345678", false, false, -1, 9},
179	{"", "Jan _2 15:04:05.999", "Feb  4 21:00:57.012300000", false, false, -1, 4},
180	{"", "Jan _2 15:04:05.999", "Feb  4 21:00:57.012345678", false, false, -1, 9},
181	{"", "Jan _2 15:04:05.999999999", "Feb  4 21:00:57.0123", false, false, -1, 4},
182	{"", "Jan _2 15:04:05.999999999", "Feb  4 21:00:57.012345678", false, false, -1, 9},
183}
184
185func TestParse(t *testing.T) {
186	for _, test := range parseTests {
187		time, err := Parse(test.format, test.value)
188		if err != nil {
189			t.Errorf("%s error: %v", test.name, err)
190		} else {
191			checkTime(time, &test, t)
192		}
193	}
194}
195
196// All parsed with ANSIC.
197var dayOutOfRangeTests = []struct {
198	date string
199	ok   bool
200}{
201	{"Thu Jan 99 21:00:57 2010", false},
202	{"Thu Jan 31 21:00:57 2010", true},
203	{"Thu Jan 32 21:00:57 2010", false},
204	{"Thu Feb 28 21:00:57 2012", true},
205	{"Thu Feb 29 21:00:57 2012", true},
206	{"Thu Feb 29 21:00:57 2010", false},
207	{"Thu Mar 31 21:00:57 2010", true},
208	{"Thu Mar 32 21:00:57 2010", false},
209	{"Thu Apr 30 21:00:57 2010", true},
210	{"Thu Apr 31 21:00:57 2010", false},
211	{"Thu May 31 21:00:57 2010", true},
212	{"Thu May 32 21:00:57 2010", false},
213	{"Thu Jun 30 21:00:57 2010", true},
214	{"Thu Jun 31 21:00:57 2010", false},
215	{"Thu Jul 31 21:00:57 2010", true},
216	{"Thu Jul 32 21:00:57 2010", false},
217	{"Thu Aug 31 21:00:57 2010", true},
218	{"Thu Aug 32 21:00:57 2010", false},
219	{"Thu Sep 30 21:00:57 2010", true},
220	{"Thu Sep 31 21:00:57 2010", false},
221	{"Thu Oct 31 21:00:57 2010", true},
222	{"Thu Oct 32 21:00:57 2010", false},
223	{"Thu Nov 30 21:00:57 2010", true},
224	{"Thu Nov 31 21:00:57 2010", false},
225	{"Thu Dec 31 21:00:57 2010", true},
226	{"Thu Dec 32 21:00:57 2010", false},
227	{"Thu Dec 00 21:00:57 2010", false},
228}
229
230func TestParseDayOutOfRange(t *testing.T) {
231	for _, test := range dayOutOfRangeTests {
232		_, err := Parse(ANSIC, test.date)
233		switch {
234		case test.ok && err == nil:
235			// OK
236		case !test.ok && err != nil:
237			if !strings.Contains(err.Error(), "day out of range") {
238				t.Errorf("%q: expected 'day' error, got %v", test.date, err)
239			}
240		case test.ok && err != nil:
241			t.Errorf("%q: unexpected error: %v", test.date, err)
242		case !test.ok && err == nil:
243			t.Errorf("%q: expected 'day' error, got none", test.date)
244		}
245	}
246}
247
248// TestParseInLocation checks that the Parse and ParseInLocation
249// functions do not get confused by the fact that AST (Arabia Standard
250// Time) and AST (Atlantic Standard Time) are different time zones,
251// even though they have the same abbreviation.
252//
253// ICANN has been slowly phasing out invented abbreviation in favor of
254// numeric time zones (for example, the Asia/Baghdad time zone
255// abbreviation got changed from AST to +03 in the 2017a tzdata
256// release); but we still want to make sure that the time package does
257// not get confused on systems with slightly older tzdata packages.
258func TestParseInLocation(t *testing.T) {
259
260	baghdad, err := LoadLocation("Asia/Baghdad")
261	if err != nil {
262		t.Fatal(err)
263	}
264
265	var t1, t2 Time
266
267	t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", baghdad)
268	if err != nil {
269		t.Fatal(err)
270	}
271
272	_, offset := t1.Zone()
273
274	// A zero offset means that ParseInLocation did not recognize the
275	// 'AST' abbreviation as matching the current location (Baghdad,
276	// where we'd expect a +03 hrs offset); likely because we're using
277	// a recent tzdata release (2017a or newer).
278	// If it happens, skip the Baghdad test.
279	if offset != 0 {
280		t2 = Date(2013, February, 1, 00, 00, 00, 0, baghdad)
281		if t1 != t2 {
282			t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad) = %v, want %v", t1, t2)
283		}
284		if offset != 3*60*60 {
285			t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad).Zone = _, %d, want _, %d", offset, 3*60*60)
286		}
287	}
288
289	blancSablon, err := LoadLocation("America/Blanc-Sablon")
290	if err != nil {
291		t.Fatal(err)
292	}
293
294	// In this case 'AST' means 'Atlantic Standard Time', and we
295	// expect the abbreviation to correctly match the american
296	// location.
297	t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", blancSablon)
298	if err != nil {
299		t.Fatal(err)
300	}
301	t2 = Date(2013, February, 1, 00, 00, 00, 0, blancSablon)
302	if t1 != t2 {
303		t.Fatalf("ParseInLocation(Feb 01 2013 AST, Blanc-Sablon) = %v, want %v", t1, t2)
304	}
305	_, offset = t1.Zone()
306	if offset != -4*60*60 {
307		t.Fatalf("ParseInLocation(Feb 01 2013 AST, Blanc-Sablon).Zone = _, %d, want _, %d", offset, -4*60*60)
308	}
309}
310
311func TestLoadLocationZipFile(t *testing.T) {
312	t.Skip("gccgo does not use the zip file")
313
314	ForceZipFileForTesting(true)
315	defer ForceZipFileForTesting(false)
316
317	_, err := LoadLocation("Australia/Sydney")
318	if err != nil {
319		t.Fatal(err)
320	}
321}
322
323var rubyTests = []ParseTest{
324	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0},
325	// Ignore the time zone in the test. If it parses, it'll be OK.
326	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0000 2010", false, true, 1, 0},
327	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +0000 2010", false, true, 1, 0},
328	{"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +1130 2010", false, true, 1, 0},
329}
330
331// Problematic time zone format needs special tests.
332func TestRubyParse(t *testing.T) {
333	for _, test := range rubyTests {
334		time, err := Parse(test.format, test.value)
335		if err != nil {
336			t.Errorf("%s error: %v", test.name, err)
337		} else {
338			checkTime(time, &test, t)
339		}
340	}
341}
342
343func checkTime(time Time, test *ParseTest, t *testing.T) {
344	// The time should be Thu Feb  4 21:00:57 PST 2010
345	if test.yearSign >= 0 && test.yearSign*time.Year() != 2010 {
346		t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010)
347	}
348	if time.Month() != February {
349		t.Errorf("%s: bad month: %s not %s", test.name, time.Month(), February)
350	}
351	if time.Day() != 4 {
352		t.Errorf("%s: bad day: %d not %d", test.name, time.Day(), 4)
353	}
354	if time.Hour() != 21 {
355		t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour(), 21)
356	}
357	if time.Minute() != 0 {
358		t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute(), 0)
359	}
360	if time.Second() != 57 {
361		t.Errorf("%s: bad second: %d not %d", test.name, time.Second(), 57)
362	}
363	// Nanoseconds must be checked against the precision of the input.
364	nanosec, err := strconv.ParseUint("012345678"[:test.fracDigits]+"000000000"[:9-test.fracDigits], 10, 0)
365	if err != nil {
366		panic(err)
367	}
368	if time.Nanosecond() != int(nanosec) {
369		t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond(), nanosec)
370	}
371	name, offset := time.Zone()
372	if test.hasTZ && offset != -28800 {
373		t.Errorf("%s: bad tz offset: %s %d not %d", test.name, name, offset, -28800)
374	}
375	if test.hasWD && time.Weekday() != Thursday {
376		t.Errorf("%s: bad weekday: %s not %s", test.name, time.Weekday(), Thursday)
377	}
378}
379
380func TestFormatAndParse(t *testing.T) {
381	const fmt = "Mon MST " + RFC3339 // all fields
382	f := func(sec int64) bool {
383		t1 := Unix(sec/2, 0)
384		if t1.Year() < 1000 || t1.Year() > 9999 || t1.Unix() != sec {
385			// not required to work
386			return true
387		}
388		t2, err := Parse(fmt, t1.Format(fmt))
389		if err != nil {
390			t.Errorf("error: %s", err)
391			return false
392		}
393		if t1.Unix() != t2.Unix() || t1.Nanosecond() != t2.Nanosecond() {
394			t.Errorf("FormatAndParse %d: %q(%d) %q(%d)", sec, t1, t1.Unix(), t2, t2.Unix())
395			return false
396		}
397		return true
398	}
399	f32 := func(sec int32) bool { return f(int64(sec)) }
400	cfg := &quick.Config{MaxCount: 10000}
401
402	// Try a reasonable date first, then the huge ones.
403	if err := quick.Check(f32, cfg); err != nil {
404		t.Fatal(err)
405	}
406	if err := quick.Check(f, cfg); err != nil {
407		t.Fatal(err)
408	}
409}
410
411type ParseTimeZoneTest struct {
412	value  string
413	length int
414	ok     bool
415}
416
417var parseTimeZoneTests = []ParseTimeZoneTest{
418	{"gmt hi there", 0, false},
419	{"GMT hi there", 3, true},
420	{"GMT+12 hi there", 6, true},
421	{"GMT+00 hi there", 3, true}, // 0 or 00 is not a legal offset.
422	{"GMT-5 hi there", 5, true},
423	{"GMT-51 hi there", 3, true},
424	{"ChST hi there", 4, true},
425	{"MeST hi there", 4, true},
426	{"MSDx", 3, true},
427	{"MSDY", 0, false}, // four letters must end in T.
428	{"ESAST hi", 5, true},
429	{"ESASTT hi", 0, false}, // run of upper-case letters too long.
430	{"ESATY hi", 0, false},  // five letters must end in T.
431	{"WITA hi", 4, true},    // Issue #18251
432}
433
434func TestParseTimeZone(t *testing.T) {
435	for _, test := range parseTimeZoneTests {
436		length, ok := ParseTimeZone(test.value)
437		if ok != test.ok {
438			t.Errorf("expected %t for %q got %t", test.ok, test.value, ok)
439		} else if length != test.length {
440			t.Errorf("expected %d for %q got %d", test.length, test.value, length)
441		}
442	}
443}
444
445type ParseErrorTest struct {
446	format string
447	value  string
448	expect string // must appear within the error
449}
450
451var parseErrorTests = []ParseErrorTest{
452	{ANSIC, "Feb  4 21:00:60 2010", "cannot parse"}, // cannot parse Feb as Mon
453	{ANSIC, "Thu Feb  4 21:00:57 @2010", "cannot parse"},
454	{ANSIC, "Thu Feb  4 21:00:60 2010", "second out of range"},
455	{ANSIC, "Thu Feb  4 21:61:57 2010", "minute out of range"},
456	{ANSIC, "Thu Feb  4 24:00:60 2010", "hour out of range"},
457	{"Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 23:00:59x01 2010", "cannot parse"},
458	{"Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 23:00:59.xxx 2010", "cannot parse"},
459	{"Mon Jan _2 15:04:05.000 2006", "Thu Feb  4 23:00:59.-123 2010", "fractional second out of range"},
460	// issue 4502. StampNano requires exactly 9 digits of precision.
461	{StampNano, "Dec  7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`},
462	{StampNano, "Dec  7 11:22:01.0000000000", "extra text: 0"},
463	// issue 4493. Helpful errors.
464	{RFC3339, "2006-01-02T15:04:05Z07:00", `parsing time "2006-01-02T15:04:05Z07:00": extra text: 07:00`},
465	{RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`},
466	{RFC3339, "2006-01-02T15:04:05_abc", `parsing time "2006-01-02T15:04:05_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as "Z07:00"`},
467	{RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: _abc`},
468	// invalid second followed by optional fractional seconds
469	{RFC3339, "2010-02-04T21:00:67.012345678-08:00", "second out of range"},
470	// issue 21113
471	{"_2 Jan 06 15:04 MST", "4 --- 00 00:00 GMT", "cannot parse"},
472	{"_2 January 06 15:04 MST", "4 --- 00 00:00 GMT", "cannot parse"},
473}
474
475func TestParseErrors(t *testing.T) {
476	for _, test := range parseErrorTests {
477		_, err := Parse(test.format, test.value)
478		if err == nil {
479			t.Errorf("expected error for %q %q", test.format, test.value)
480		} else if !strings.Contains(err.Error(), test.expect) {
481			t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err)
482		}
483	}
484}
485
486func TestNoonIs12PM(t *testing.T) {
487	noon := Date(0, January, 1, 12, 0, 0, 0, UTC)
488	const expect = "12:00PM"
489	got := noon.Format("3:04PM")
490	if got != expect {
491		t.Errorf("got %q; expect %q", got, expect)
492	}
493	got = noon.Format("03:04PM")
494	if got != expect {
495		t.Errorf("got %q; expect %q", got, expect)
496	}
497}
498
499func TestMidnightIs12AM(t *testing.T) {
500	midnight := Date(0, January, 1, 0, 0, 0, 0, UTC)
501	expect := "12:00AM"
502	got := midnight.Format("3:04PM")
503	if got != expect {
504		t.Errorf("got %q; expect %q", got, expect)
505	}
506	got = midnight.Format("03:04PM")
507	if got != expect {
508		t.Errorf("got %q; expect %q", got, expect)
509	}
510}
511
512func Test12PMIsNoon(t *testing.T) {
513	noon, err := Parse("3:04PM", "12:00PM")
514	if err != nil {
515		t.Fatal("error parsing date:", err)
516	}
517	if noon.Hour() != 12 {
518		t.Errorf("got %d; expect 12", noon.Hour())
519	}
520	noon, err = Parse("03:04PM", "12:00PM")
521	if err != nil {
522		t.Fatal("error parsing date:", err)
523	}
524	if noon.Hour() != 12 {
525		t.Errorf("got %d; expect 12", noon.Hour())
526	}
527}
528
529func Test12AMIsMidnight(t *testing.T) {
530	midnight, err := Parse("3:04PM", "12:00AM")
531	if err != nil {
532		t.Fatal("error parsing date:", err)
533	}
534	if midnight.Hour() != 0 {
535		t.Errorf("got %d; expect 0", midnight.Hour())
536	}
537	midnight, err = Parse("03:04PM", "12:00AM")
538	if err != nil {
539		t.Fatal("error parsing date:", err)
540	}
541	if midnight.Hour() != 0 {
542		t.Errorf("got %d; expect 0", midnight.Hour())
543	}
544}
545
546// Check that a time without a Zone still produces a (numeric) time zone
547// when formatted with MST as a requested zone.
548func TestMissingZone(t *testing.T) {
549	time, err := Parse(RubyDate, "Thu Feb 02 16:10:03 -0500 2006")
550	if err != nil {
551		t.Fatal("error parsing date:", err)
552	}
553	expect := "Thu Feb  2 16:10:03 -0500 2006" // -0500 not EST
554	str := time.Format(UnixDate)               // uses MST as its time zone
555	if str != expect {
556		t.Errorf("got %s; expect %s", str, expect)
557	}
558}
559
560func TestMinutesInTimeZone(t *testing.T) {
561	time, err := Parse(RubyDate, "Mon Jan 02 15:04:05 +0123 2006")
562	if err != nil {
563		t.Fatal("error parsing date:", err)
564	}
565	expected := (1*60 + 23) * 60
566	_, offset := time.Zone()
567	if offset != expected {
568		t.Errorf("ZoneOffset = %d, want %d", offset, expected)
569	}
570}
571
572type SecondsTimeZoneOffsetTest struct {
573	format         string
574	value          string
575	expectedoffset int
576}
577
578var secondsTimeZoneOffsetTests = []SecondsTimeZoneOffsetTest{
579	{"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)},
580	{"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02-00:34:08", -(34*60 + 8)},
581	{"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02+003408", 34*60 + 8},
582	{"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8},
583	{"2006-01-02T15:04:05Z070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)},
584	{"2006-01-02T15:04:05Z07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8},
585	{"2006-01-02T15:04:05-07", "1871-01-01T05:33:02+01", 1 * 60 * 60},
586	{"2006-01-02T15:04:05-07", "1871-01-01T05:33:02-02", -2 * 60 * 60},
587	{"2006-01-02T15:04:05Z07", "1871-01-01T05:33:02-02", -2 * 60 * 60},
588}
589
590func TestParseSecondsInTimeZone(t *testing.T) {
591	// should accept timezone offsets with seconds like: Zone America/New_York   -4:56:02 -      LMT     1883 Nov 18 12:03:58
592	for _, test := range secondsTimeZoneOffsetTests {
593		time, err := Parse(test.format, test.value)
594		if err != nil {
595			t.Fatal("error parsing date:", err)
596		}
597		_, offset := time.Zone()
598		if offset != test.expectedoffset {
599			t.Errorf("ZoneOffset = %d, want %d", offset, test.expectedoffset)
600		}
601	}
602}
603
604func TestFormatSecondsInTimeZone(t *testing.T) {
605	for _, test := range secondsTimeZoneOffsetTests {
606		d := Date(1871, 1, 1, 5, 33, 2, 0, FixedZone("LMT", test.expectedoffset))
607		timestr := d.Format(test.format)
608		if timestr != test.value {
609			t.Errorf("Format = %s, want %s", timestr, test.value)
610		}
611	}
612}
613
614// Issue 11334.
615func TestUnderscoreTwoThousand(t *testing.T) {
616	format := "15:04_20060102"
617	input := "14:38_20150618"
618	time, err := Parse(format, input)
619	if err != nil {
620		t.Error(err)
621	}
622	if y, m, d := time.Date(); y != 2015 || m != 6 || d != 18 {
623		t.Errorf("Incorrect y/m/d, got %d/%d/%d", y, m, d)
624	}
625	if h := time.Hour(); h != 14 {
626		t.Errorf("Incorrect hour, got %d", h)
627	}
628	if m := time.Minute(); m != 38 {
629		t.Errorf("Incorrect minute, got %d", m)
630	}
631}
632