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 "bytes" 9 "fmt" 10 "math" 11 "strconv" 12 "strings" 13 "testing" 14 "testing/quick" 15 . "time" 16) 17 18var nextStdChunkTests = []string{ 19 "(2006)-(01)-(02)T(15):(04):(05)(Z07:00)", 20 "(2006)-(01)-(02) (002) (15):(04):(05)", 21 "(2006)-(01) (002) (15):(04):(05)", 22 "(2006)-(002) (15):(04):(05)", 23 "(2006)(002)(01) (15):(04):(05)", 24 "(2006)(002)(04) (15):(04):(05)", 25} 26 27func TestNextStdChunk(t *testing.T) { 28 // Most bugs in Parse or Format boil down to problems with 29 // the exact detection of format chunk boundaries in the 30 // helper function nextStdChunk (here called as NextStdChunk). 31 // This test checks nextStdChunk's behavior directly, 32 // instead of needing to test it only indirectly through Parse/Format. 33 34 // markChunks returns format with each detected 35 // 'format chunk' parenthesized. 36 // For example showChunks("2006-01-02") == "(2006)-(01)-(02)". 37 markChunks := func(format string) string { 38 // Note that NextStdChunk and StdChunkNames 39 // are not part of time's public API. 40 // They are exported in export_test for this test. 41 out := "" 42 for s := format; s != ""; { 43 prefix, std, suffix := NextStdChunk(s) 44 out += prefix 45 if std > 0 { 46 out += "(" + StdChunkNames[std] + ")" 47 } 48 s = suffix 49 } 50 return out 51 } 52 53 noParens := func(r rune) rune { 54 if r == '(' || r == ')' { 55 return -1 56 } 57 return r 58 } 59 60 for _, marked := range nextStdChunkTests { 61 // marked is an expected output from markChunks. 62 // If we delete the parens and pass it through markChunks, 63 // we should get the original back. 64 format := strings.Map(noParens, marked) 65 out := markChunks(format) 66 if out != marked { 67 t.Errorf("nextStdChunk parses %q as %q, want %q", format, out, marked) 68 } 69 } 70} 71 72type TimeFormatTest struct { 73 time Time 74 formattedValue string 75} 76 77var rfc3339Formats = []TimeFormatTest{ 78 {Date(2008, 9, 17, 20, 4, 26, 0, UTC), "2008-09-17T20:04:26Z"}, 79 {Date(1994, 9, 17, 20, 4, 26, 0, FixedZone("EST", -18000)), "1994-09-17T20:04:26-05:00"}, 80 {Date(2000, 12, 26, 1, 15, 6, 0, FixedZone("OTO", 15600)), "2000-12-26T01:15:06+04:20"}, 81} 82 83func TestRFC3339Conversion(t *testing.T) { 84 for _, f := range rfc3339Formats { 85 if f.time.Format(RFC3339) != f.formattedValue { 86 t.Error("RFC3339:") 87 t.Errorf(" want=%+v", f.formattedValue) 88 t.Errorf(" have=%+v", f.time.Format(RFC3339)) 89 } 90 } 91} 92 93func TestAppendInt(t *testing.T) { 94 tests := []struct { 95 in int 96 width int 97 want string 98 }{ 99 {0, 0, "0"}, 100 {0, 1, "0"}, 101 {0, 2, "00"}, 102 {0, 3, "000"}, 103 {1, 0, "1"}, 104 {1, 1, "1"}, 105 {1, 2, "01"}, 106 {1, 3, "001"}, 107 {-1, 0, "-1"}, 108 {-1, 1, "-1"}, 109 {-1, 2, "-01"}, 110 {-1, 3, "-001"}, 111 {99, 2, "99"}, 112 {100, 2, "100"}, 113 {1, 4, "0001"}, 114 {12, 4, "0012"}, 115 {123, 4, "0123"}, 116 {1234, 4, "1234"}, 117 {12345, 4, "12345"}, 118 {1, 5, "00001"}, 119 {12, 5, "00012"}, 120 {123, 5, "00123"}, 121 {1234, 5, "01234"}, 122 {12345, 5, "12345"}, 123 {123456, 5, "123456"}, 124 {0, 9, "000000000"}, 125 {123, 9, "000000123"}, 126 {123456, 9, "000123456"}, 127 {123456789, 9, "123456789"}, 128 } 129 var got []byte 130 for _, tt := range tests { 131 got = AppendInt(got[:0], tt.in, tt.width) 132 if string(got) != tt.want { 133 t.Errorf("appendInt(%d, %d) = %s, want %s", tt.in, tt.width, got, tt.want) 134 } 135 } 136} 137 138type FormatTest struct { 139 name string 140 format string 141 result string 142} 143 144var formatTests = []FormatTest{ 145 {"ANSIC", ANSIC, "Wed Feb 4 21:00:57 2009"}, 146 {"UnixDate", UnixDate, "Wed Feb 4 21:00:57 PST 2009"}, 147 {"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"}, 148 {"RFC822", RFC822, "04 Feb 09 21:00 PST"}, 149 {"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"}, 150 {"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"}, 151 {"RFC1123Z", RFC1123Z, "Wed, 04 Feb 2009 21:00:57 -0800"}, 152 {"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"}, 153 {"RFC3339Nano", RFC3339Nano, "2009-02-04T21:00:57.0123456-08:00"}, 154 {"Kitchen", Kitchen, "9:00PM"}, 155 {"am/pm", "3pm", "9pm"}, 156 {"AM/PM", "3PM", "9PM"}, 157 {"two-digit year", "06 01 02", "09 02 04"}, 158 // Three-letter months and days must not be followed by lower-case letter. 159 {"Janet", "Hi Janet, the Month is January", "Hi Janet, the Month is February"}, 160 // Time stamps, Fractional seconds. 161 {"Stamp", Stamp, "Feb 4 21:00:57"}, 162 {"StampMilli", StampMilli, "Feb 4 21:00:57.012"}, 163 {"StampMicro", StampMicro, "Feb 4 21:00:57.012345"}, 164 {"StampNano", StampNano, "Feb 4 21:00:57.012345600"}, 165 {"DateTime", DateTime, "2009-02-04 21:00:57"}, 166 {"DateOnly", DateOnly, "2009-02-04"}, 167 {"TimeOnly", TimeOnly, "21:00:57"}, 168 {"YearDay", "Jan 2 002 __2 2", "Feb 4 035 35 4"}, 169 {"Year", "2006 6 06 _6 __6 ___6", "2009 6 09 _6 __6 ___6"}, 170 {"Month", "Jan January 1 01 _1", "Feb February 2 02 _2"}, 171 {"DayOfMonth", "2 02 _2 __2", "4 04 4 35"}, 172 {"DayOfWeek", "Mon Monday", "Wed Wednesday"}, 173 {"Hour", "15 3 03 _3", "21 9 09 _9"}, 174 {"Minute", "4 04 _4", "0 00 _0"}, 175 {"Second", "5 05 _5", "57 57 _57"}, 176} 177 178func TestFormat(t *testing.T) { 179 // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2009 180 time := Unix(0, 1233810057012345600) 181 for _, test := range formatTests { 182 result := time.Format(test.format) 183 if result != test.result { 184 t.Errorf("%s expected %q got %q", test.name, test.result, result) 185 } 186 } 187} 188 189var goStringTests = []struct { 190 in Time 191 want string 192}{ 193 {Date(2009, February, 5, 5, 0, 57, 12345600, UTC), 194 "time.Date(2009, time.February, 5, 5, 0, 57, 12345600, time.UTC)"}, 195 {Date(2009, February, 5, 5, 0, 57, 12345600, Local), 196 "time.Date(2009, time.February, 5, 5, 0, 57, 12345600, time.Local)"}, 197 {Date(2009, February, 5, 5, 0, 57, 12345600, FixedZone("Europe/Berlin", 3*60*60)), 198 `time.Date(2009, time.February, 5, 5, 0, 57, 12345600, time.Location("Europe/Berlin"))`, 199 }, 200 {Date(2009, February, 5, 5, 0, 57, 12345600, FixedZone("Non-ASCII character ⏰", 3*60*60)), 201 `time.Date(2009, time.February, 5, 5, 0, 57, 12345600, time.Location("Non-ASCII character \xe2\x8f\xb0"))`, 202 }, 203} 204 205func TestGoString(t *testing.T) { 206 // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2009 207 for _, tt := range goStringTests { 208 if tt.in.GoString() != tt.want { 209 t.Errorf("GoString (%q): got %q want %q", tt.in, tt.in.GoString(), tt.want) 210 } 211 } 212} 213 214// issue 12440. 215func TestFormatSingleDigits(t *testing.T) { 216 time := Date(2001, 2, 3, 4, 5, 6, 700000000, UTC) 217 test := FormatTest{"single digit format", "3:4:5", "4:5:6"} 218 result := time.Format(test.format) 219 if result != test.result { 220 t.Errorf("%s expected %q got %q", test.name, test.result, result) 221 } 222} 223 224func TestFormatShortYear(t *testing.T) { 225 years := []int{ 226 -100001, -100000, -99999, 227 -10001, -10000, -9999, 228 -1001, -1000, -999, 229 -101, -100, -99, 230 -11, -10, -9, 231 -1, 0, 1, 232 9, 10, 11, 233 99, 100, 101, 234 999, 1000, 1001, 235 9999, 10000, 10001, 236 99999, 100000, 100001, 237 } 238 239 for _, y := range years { 240 time := Date(y, January, 1, 0, 0, 0, 0, UTC) 241 result := time.Format("2006.01.02") 242 var want string 243 if y < 0 { 244 // The 4 in %04d counts the - sign, so print -y instead 245 // and introduce our own - sign. 246 want = fmt.Sprintf("-%04d.%02d.%02d", -y, 1, 1) 247 } else { 248 want = fmt.Sprintf("%04d.%02d.%02d", y, 1, 1) 249 } 250 if result != want { 251 t.Errorf("(jan 1 %d).Format(\"2006.01.02\") = %q, want %q", y, result, want) 252 } 253 } 254} 255 256type ParseTest struct { 257 name string 258 format string 259 value string 260 hasTZ bool // contains a time zone 261 hasWD bool // contains a weekday 262 yearSign int // sign of year, -1 indicates the year is not present in the format 263 fracDigits int // number of digits of fractional second 264} 265 266var parseTests = []ParseTest{ 267 {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0}, 268 {"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010", true, true, 1, 0}, 269 {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0}, 270 {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true, 1, 0}, 271 {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true, 1, 0}, 272 {"RFC1123", RFC1123, "Thu, 04 Feb 2010 22:00:57 PDT", true, true, 1, 0}, 273 {"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57 -0800", true, true, 1, 0}, 274 {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00", true, false, 1, 0}, 275 {"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}, 276 // Optional fractional seconds. 277 {"ANSIC", ANSIC, "Thu Feb 4 21:00:57.0 2010", false, true, 1, 1}, 278 {"UnixDate", UnixDate, "Thu Feb 4 21:00:57.01 PST 2010", true, true, 1, 2}, 279 {"RubyDate", RubyDate, "Thu Feb 04 21:00:57.012 -0800 2010", true, true, 1, 3}, 280 {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57.0123 PST", true, true, 1, 4}, 281 {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57.01234 PST", true, true, 1, 5}, 282 {"RFC1123Z", RFC1123Z, "Thu, 04 Feb 2010 21:00:57.01234 -0800", true, true, 1, 5}, 283 {"RFC3339", RFC3339, "2010-02-04T21:00:57.012345678-08:00", true, false, 1, 9}, 284 {"custom: \"2006-01-02 15:04:05\"", "2006-01-02 15:04:05", "2010-02-04 21:00:57.0", false, false, 1, 0}, 285 // Amount of white space should not matter. 286 {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0}, 287 {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true, 1, 0}, 288 // Case should not matter 289 {"ANSIC", ANSIC, "THU FEB 4 21:00:57 2010", false, true, 1, 0}, 290 {"ANSIC", ANSIC, "thu feb 4 21:00:57 2010", false, true, 1, 0}, 291 // Fractional seconds. 292 {"millisecond:: dot separator", "Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3}, 293 {"microsecond:: dot separator", "Mon Jan _2 15:04:05.000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6}, 294 {"nanosecond:: dot separator", "Mon Jan _2 15:04:05.000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9}, 295 {"millisecond:: comma separator", "Mon Jan _2 15:04:05,000 2006", "Thu Feb 4 21:00:57.012 2010", false, true, 1, 3}, 296 {"microsecond:: comma separator", "Mon Jan _2 15:04:05,000000 2006", "Thu Feb 4 21:00:57.012345 2010", false, true, 1, 6}, 297 {"nanosecond:: comma separator", "Mon Jan _2 15:04:05,000000000 2006", "Thu Feb 4 21:00:57.012345678 2010", false, true, 1, 9}, 298 299 // Leading zeros in other places should not be taken as fractional seconds. 300 {"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1}, 301 {"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2}, 302 // Month and day names only match when not followed by a lower-case letter. 303 {"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}, 304 305 // GMT with offset. 306 {"GMT-8", UnixDate, "Fri Feb 5 05:00:57 GMT-8 2010", true, true, 1, 0}, 307 308 // Accept any number of fractional second digits (including none) for .999... 309 // In Go 1, .999... was completely ignored in the format, meaning the first two 310 // cases would succeed, but the next four would not. Go 1.1 accepts all six. 311 // decimal "." separator. 312 {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, 313 {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, 314 {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, 315 {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, 316 {"", "2006-01-02 15:04:05.9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, 317 {"", "2006-01-02 15:04:05.999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, 318 // comma "," separator. 319 {"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, 320 {"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57 -0800 PST", true, false, 1, 0}, 321 {"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, 322 {"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57.0123 -0800 PST", true, false, 1, 4}, 323 {"", "2006-01-02 15:04:05,9999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, 324 {"", "2006-01-02 15:04:05,999999999 -0700 MST", "2010-02-04 21:00:57.012345678 -0800 PST", true, false, 1, 9}, 325 326 // issue 4502. 327 {"", StampNano, "Feb 4 21:00:57.012345678", false, false, -1, 9}, 328 {"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012300000", false, false, -1, 4}, 329 {"", "Jan _2 15:04:05.999", "Feb 4 21:00:57.012345678", false, false, -1, 9}, 330 {"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.0123", false, false, -1, 4}, 331 {"", "Jan _2 15:04:05.999999999", "Feb 4 21:00:57.012345678", false, false, -1, 9}, 332 333 // Day of year. 334 {"", "2006-01-02 002 15:04:05", "2010-02-04 035 21:00:57", false, false, 1, 0}, 335 {"", "2006-01 002 15:04:05", "2010-02 035 21:00:57", false, false, 1, 0}, 336 {"", "2006-002 15:04:05", "2010-035 21:00:57", false, false, 1, 0}, 337 {"", "200600201 15:04:05", "201003502 21:00:57", false, false, 1, 0}, 338 {"", "200600204 15:04:05", "201003504 21:00:57", false, false, 1, 0}, 339 340 // Time zone offsets 341 {"", "2006-01-02T15:04:05Z07", "2010-02-04T21:00:57Z", false, false, 1, 0}, 342 {"", "2006-01-02T15:04:05Z07", "2010-02-04T21:00:57+08", false, false, 1, 0}, 343 {"", "2006-01-02T15:04:05Z07", "2010-02-04T21:00:57-08", true, false, 1, 0}, 344 {"", "2006-01-02T15:04:05Z0700", "2010-02-04T21:00:57Z", false, false, 1, 0}, 345 {"", "2006-01-02T15:04:05Z0700", "2010-02-04T21:00:57+0800", false, false, 1, 0}, 346 {"", "2006-01-02T15:04:05Z0700", "2010-02-04T21:00:57-0800", true, false, 1, 0}, 347 {"", "2006-01-02T15:04:05Z07:00", "2010-02-04T21:00:57Z", false, false, 1, 0}, 348 {"", "2006-01-02T15:04:05Z07:00", "2010-02-04T21:00:57+08:00", false, false, 1, 0}, 349 {"", "2006-01-02T15:04:05Z07:00", "2010-02-04T21:00:57-08:00", true, false, 1, 0}, 350 {"", "2006-01-02T15:04:05Z070000", "2010-02-04T21:00:57Z", false, false, 1, 0}, 351 {"", "2006-01-02T15:04:05Z070000", "2010-02-04T21:00:57+080000", false, false, 1, 0}, 352 {"", "2006-01-02T15:04:05Z070000", "2010-02-04T21:00:57-080000", true, false, 1, 0}, 353 {"", "2006-01-02T15:04:05Z07:00:00", "2010-02-04T21:00:57Z", false, false, 1, 0}, 354 {"", "2006-01-02T15:04:05Z07:00:00", "2010-02-04T21:00:57+08:00:00", false, false, 1, 0}, 355 {"", "2006-01-02T15:04:05Z07:00:00", "2010-02-04T21:00:57-08:00:00", true, false, 1, 0}, 356} 357 358func TestParse(t *testing.T) { 359 for _, test := range parseTests { 360 time, err := Parse(test.format, test.value) 361 if err != nil { 362 t.Errorf("%s error: %v", test.name, err) 363 } else { 364 checkTime(time, &test, t) 365 } 366 } 367} 368 369// All parsed with ANSIC. 370var dayOutOfRangeTests = []struct { 371 date string 372 ok bool 373}{ 374 {"Thu Jan 99 21:00:57 2010", false}, 375 {"Thu Jan 31 21:00:57 2010", true}, 376 {"Thu Jan 32 21:00:57 2010", false}, 377 {"Thu Feb 28 21:00:57 2012", true}, 378 {"Thu Feb 29 21:00:57 2012", true}, 379 {"Thu Feb 29 21:00:57 2010", false}, 380 {"Thu Mar 31 21:00:57 2010", true}, 381 {"Thu Mar 32 21:00:57 2010", false}, 382 {"Thu Apr 30 21:00:57 2010", true}, 383 {"Thu Apr 31 21:00:57 2010", false}, 384 {"Thu May 31 21:00:57 2010", true}, 385 {"Thu May 32 21:00:57 2010", false}, 386 {"Thu Jun 30 21:00:57 2010", true}, 387 {"Thu Jun 31 21:00:57 2010", false}, 388 {"Thu Jul 31 21:00:57 2010", true}, 389 {"Thu Jul 32 21:00:57 2010", false}, 390 {"Thu Aug 31 21:00:57 2010", true}, 391 {"Thu Aug 32 21:00:57 2010", false}, 392 {"Thu Sep 30 21:00:57 2010", true}, 393 {"Thu Sep 31 21:00:57 2010", false}, 394 {"Thu Oct 31 21:00:57 2010", true}, 395 {"Thu Oct 32 21:00:57 2010", false}, 396 {"Thu Nov 30 21:00:57 2010", true}, 397 {"Thu Nov 31 21:00:57 2010", false}, 398 {"Thu Dec 31 21:00:57 2010", true}, 399 {"Thu Dec 32 21:00:57 2010", false}, 400 {"Thu Dec 00 21:00:57 2010", false}, 401} 402 403func TestParseDayOutOfRange(t *testing.T) { 404 for _, test := range dayOutOfRangeTests { 405 _, err := Parse(ANSIC, test.date) 406 switch { 407 case test.ok && err == nil: 408 // OK 409 case !test.ok && err != nil: 410 if !strings.Contains(err.Error(), "day out of range") { 411 t.Errorf("%q: expected 'day' error, got %v", test.date, err) 412 } 413 case test.ok && err != nil: 414 t.Errorf("%q: unexpected error: %v", test.date, err) 415 case !test.ok && err == nil: 416 t.Errorf("%q: expected 'day' error, got none", test.date) 417 } 418 } 419} 420 421// TestParseInLocation checks that the Parse and ParseInLocation 422// functions do not get confused by the fact that AST (Arabia Standard 423// Time) and AST (Atlantic Standard Time) are different time zones, 424// even though they have the same abbreviation. 425// 426// ICANN has been slowly phasing out invented abbreviation in favor of 427// numeric time zones (for example, the Asia/Baghdad time zone 428// abbreviation got changed from AST to +03 in the 2017a tzdata 429// release); but we still want to make sure that the time package does 430// not get confused on systems with slightly older tzdata packages. 431func TestParseInLocation(t *testing.T) { 432 433 baghdad, err := LoadLocation("Asia/Baghdad") 434 if err != nil { 435 t.Fatal(err) 436 } 437 438 var t1, t2 Time 439 440 t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", baghdad) 441 if err != nil { 442 t.Fatal(err) 443 } 444 445 _, offset := t1.Zone() 446 447 // A zero offset means that ParseInLocation did not recognize the 448 // 'AST' abbreviation as matching the current location (Baghdad, 449 // where we'd expect a +03 hrs offset); likely because we're using 450 // a recent tzdata release (2017a or newer). 451 // If it happens, skip the Baghdad test. 452 if offset != 0 { 453 t2 = Date(2013, February, 1, 00, 00, 00, 0, baghdad) 454 if t1 != t2 { 455 t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad) = %v, want %v", t1, t2) 456 } 457 if offset != 3*60*60 { 458 t.Fatalf("ParseInLocation(Feb 01 2013 AST, Baghdad).Zone = _, %d, want _, %d", offset, 3*60*60) 459 } 460 } 461 462 blancSablon, err := LoadLocation("America/Blanc-Sablon") 463 if err != nil { 464 t.Fatal(err) 465 } 466 467 // In this case 'AST' means 'Atlantic Standard Time', and we 468 // expect the abbreviation to correctly match the american 469 // location. 470 t1, err = ParseInLocation("Jan 02 2006 MST", "Feb 01 2013 AST", blancSablon) 471 if err != nil { 472 t.Fatal(err) 473 } 474 t2 = Date(2013, February, 1, 00, 00, 00, 0, blancSablon) 475 if t1 != t2 { 476 t.Fatalf("ParseInLocation(Feb 01 2013 AST, Blanc-Sablon) = %v, want %v", t1, t2) 477 } 478 _, offset = t1.Zone() 479 if offset != -4*60*60 { 480 t.Fatalf("ParseInLocation(Feb 01 2013 AST, Blanc-Sablon).Zone = _, %d, want _, %d", offset, -4*60*60) 481 } 482} 483 484func TestLoadLocationZipFile(t *testing.T) { 485 undo := DisablePlatformSources() 486 defer undo() 487 488 _, err := LoadLocation("Australia/Sydney") 489 if err != nil { 490 t.Fatal(err) 491 } 492} 493 494var rubyTests = []ParseTest{ 495 {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010", true, true, 1, 0}, 496 // Ignore the time zone in the test. If it parses, it'll be OK. 497 {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0000 2010", false, true, 1, 0}, 498 {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +0000 2010", false, true, 1, 0}, 499 {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 +1130 2010", false, true, 1, 0}, 500} 501 502// Problematic time zone format needs special tests. 503func TestRubyParse(t *testing.T) { 504 for _, test := range rubyTests { 505 time, err := Parse(test.format, test.value) 506 if err != nil { 507 t.Errorf("%s error: %v", test.name, err) 508 } else { 509 checkTime(time, &test, t) 510 } 511 } 512} 513 514func checkTime(time Time, test *ParseTest, t *testing.T) { 515 // The time should be Thu Feb 4 21:00:57 PST 2010 516 if test.yearSign >= 0 && test.yearSign*time.Year() != 2010 { 517 t.Errorf("%s: bad year: %d not %d", test.name, time.Year(), 2010) 518 } 519 if time.Month() != February { 520 t.Errorf("%s: bad month: %s not %s", test.name, time.Month(), February) 521 } 522 if time.Day() != 4 { 523 t.Errorf("%s: bad day: %d not %d", test.name, time.Day(), 4) 524 } 525 if time.Hour() != 21 { 526 t.Errorf("%s: bad hour: %d not %d", test.name, time.Hour(), 21) 527 } 528 if time.Minute() != 0 { 529 t.Errorf("%s: bad minute: %d not %d", test.name, time.Minute(), 0) 530 } 531 if time.Second() != 57 { 532 t.Errorf("%s: bad second: %d not %d", test.name, time.Second(), 57) 533 } 534 // Nanoseconds must be checked against the precision of the input. 535 nanosec, err := strconv.ParseUint("012345678"[:test.fracDigits]+"000000000"[:9-test.fracDigits], 10, 0) 536 if err != nil { 537 panic(err) 538 } 539 if time.Nanosecond() != int(nanosec) { 540 t.Errorf("%s: bad nanosecond: %d not %d", test.name, time.Nanosecond(), nanosec) 541 } 542 name, offset := time.Zone() 543 if test.hasTZ && offset != -28800 { 544 t.Errorf("%s: bad tz offset: %s %d not %d", test.name, name, offset, -28800) 545 } 546 if test.hasWD && time.Weekday() != Thursday { 547 t.Errorf("%s: bad weekday: %s not %s", test.name, time.Weekday(), Thursday) 548 } 549} 550 551func TestFormatAndParse(t *testing.T) { 552 const fmt = "Mon MST " + RFC3339 // all fields 553 f := func(sec int64) bool { 554 t1 := Unix(sec/2, 0) 555 if t1.Year() < 1000 || t1.Year() > 9999 || t1.Unix() != sec { 556 // not required to work 557 return true 558 } 559 t2, err := Parse(fmt, t1.Format(fmt)) 560 if err != nil { 561 t.Errorf("error: %s", err) 562 return false 563 } 564 if t1.Unix() != t2.Unix() || t1.Nanosecond() != t2.Nanosecond() { 565 t.Errorf("FormatAndParse %d: %q(%d) %q(%d)", sec, t1, t1.Unix(), t2, t2.Unix()) 566 return false 567 } 568 return true 569 } 570 f32 := func(sec int32) bool { return f(int64(sec)) } 571 cfg := &quick.Config{MaxCount: 10000} 572 573 // Try a reasonable date first, then the huge ones. 574 if err := quick.Check(f32, cfg); err != nil { 575 t.Fatal(err) 576 } 577 if err := quick.Check(f, cfg); err != nil { 578 t.Fatal(err) 579 } 580} 581 582type ParseTimeZoneTest struct { 583 value string 584 length int 585 ok bool 586} 587 588var parseTimeZoneTests = []ParseTimeZoneTest{ 589 {"gmt hi there", 0, false}, 590 {"GMT hi there", 3, true}, 591 {"GMT+12 hi there", 6, true}, 592 {"GMT+00 hi there", 6, true}, 593 {"GMT+", 3, true}, 594 {"GMT+3", 5, true}, 595 {"GMT+a", 3, true}, 596 {"GMT+3a", 5, true}, 597 {"GMT-5 hi there", 5, true}, 598 {"GMT-51 hi there", 3, true}, 599 {"ChST hi there", 4, true}, 600 {"MeST hi there", 4, true}, 601 {"MSDx", 3, true}, 602 {"MSDY", 0, false}, // four letters must end in T. 603 {"ESAST hi", 5, true}, 604 {"ESASTT hi", 0, false}, // run of upper-case letters too long. 605 {"ESATY hi", 0, false}, // five letters must end in T. 606 {"WITA hi", 4, true}, // Issue #18251 607 // Issue #24071 608 {"+03 hi", 3, true}, 609 {"-04 hi", 3, true}, 610 // Issue #26032 611 {"+00", 3, true}, 612 {"-11", 3, true}, 613 {"-12", 3, true}, 614 {"-23", 3, true}, 615 {"-24", 0, false}, 616 {"+13", 3, true}, 617 {"+14", 3, true}, 618 {"+23", 3, true}, 619 {"+24", 0, false}, 620} 621 622func TestParseTimeZone(t *testing.T) { 623 for _, test := range parseTimeZoneTests { 624 length, ok := ParseTimeZone(test.value) 625 if ok != test.ok { 626 t.Errorf("expected %t for %q got %t", test.ok, test.value, ok) 627 } else if length != test.length { 628 t.Errorf("expected %d for %q got %d", test.length, test.value, length) 629 } 630 } 631} 632 633type ParseErrorTest struct { 634 format string 635 value string 636 expect string // must appear within the error 637} 638 639var parseErrorTests = []ParseErrorTest{ 640 {ANSIC, "Feb 4 21:00:60 2010", `cannot parse "Feb 4 21:00:60 2010" as "Mon"`}, 641 {ANSIC, "Thu Feb 4 21:00:57 @2010", `cannot parse "@2010" as "2006"`}, 642 {ANSIC, "Thu Feb 4 21:00:60 2010", "second out of range"}, 643 {ANSIC, "Thu Feb 4 21:61:57 2010", "minute out of range"}, 644 {ANSIC, "Thu Feb 4 24:00:60 2010", "hour out of range"}, 645 {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59x01 2010", `cannot parse "x01 2010" as ".000"`}, 646 {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.xxx 2010", `cannot parse ".xxx 2010" as ".000"`}, 647 {"Mon Jan _2 15:04:05.000 2006", "Thu Feb 4 23:00:59.-123 2010", "fractional second out of range"}, 648 // issue 4502. StampNano requires exactly 9 digits of precision. 649 {StampNano, "Dec 7 11:22:01.000000", `cannot parse ".000000" as ".000000000"`}, 650 {StampNano, "Dec 7 11:22:01.0000000000", `extra text: "0"`}, 651 // issue 4493. Helpful errors. 652 {RFC3339, "2006-01-02T15:04:05Z07:00", `parsing time "2006-01-02T15:04:05Z07:00": extra text: "07:00"`}, 653 {RFC3339, "2006-01-02T15:04_abc", `parsing time "2006-01-02T15:04_abc" as "2006-01-02T15:04:05Z07:00": cannot parse "_abc" as ":"`}, 654 {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"`}, 655 {RFC3339, "2006-01-02T15:04:05Z_abc", `parsing time "2006-01-02T15:04:05Z_abc": extra text: "_abc"`}, 656 // invalid second followed by optional fractional seconds 657 {RFC3339, "2010-02-04T21:00:67.012345678-08:00", "second out of range"}, 658 // issue 54569 659 {RFC3339, "0000-01-01T00:00:.0+00:00", `parsing time "0000-01-01T00:00:.0+00:00" as "2006-01-02T15:04:05Z07:00": cannot parse ".0+00:00" as "05"`}, 660 // issue 21113 661 {"_2 Jan 06 15:04 MST", "4 --- 00 00:00 GMT", `cannot parse "--- 00 00:00 GMT" as "Jan"`}, 662 {"_2 January 06 15:04 MST", "4 --- 00 00:00 GMT", `cannot parse "--- 00 00:00 GMT" as "January"`}, 663 664 // invalid or mismatched day-of-year 665 {"Jan _2 002 2006", "Feb 4 034 2006", "day-of-year does not match day"}, 666 {"Jan _2 002 2006", "Feb 4 004 2006", "day-of-year does not match month"}, 667 668 // issue 45391. 669 {`"2006-01-02T15:04:05Z07:00"`, "0", `parsing time "0" as "\"2006-01-02T15:04:05Z07:00\"": cannot parse "0" as "\""`}, 670 {RFC3339, "\"", `parsing time "\"" as "2006-01-02T15:04:05Z07:00": cannot parse "\"" as "2006"`}, 671 672 // issue 54570 673 {RFC3339, "0000-01-01T00:00:00+00:+0", `parsing time "0000-01-01T00:00:00+00:+0" as "2006-01-02T15:04:05Z07:00": cannot parse "+00:+0" as "Z07:00"`}, 674 {RFC3339, "0000-01-01T00:00:00+-0:00", `parsing time "0000-01-01T00:00:00+-0:00" as "2006-01-02T15:04:05Z07:00": cannot parse "+-0:00" as "Z07:00"`}, 675 676 // issue 56730 677 {"2006-01-02", "22-10-25", `parsing time "22-10-25" as "2006-01-02": cannot parse "22-10-25" as "2006"`}, 678 {"06-01-02", "a2-10-25", `parsing time "a2-10-25" as "06-01-02": cannot parse "a2-10-25" as "06"`}, 679 {"03:04PM", "12:03pM", `parsing time "12:03pM" as "03:04PM": cannot parse "pM" as "PM"`}, 680 {"03:04pm", "12:03pM", `parsing time "12:03pM" as "03:04pm": cannot parse "pM" as "pm"`}, 681 682 // issue 67470 683 {"-07", "-25", "time zone offset hour out of range"}, 684 {"-07:00", "+25:00", "time zone offset hour out of range"}, 685 {"-07:00", "-23:61", "time zone offset minute out of range"}, 686 {"-07:00:00", "+23:59:61", "time zone offset second out of range"}, 687 {"Z07", "-25", "time zone offset hour out of range"}, 688 {"Z07:00", "+25:00", "time zone offset hour out of range"}, 689 {"Z07:00", "-23:61", "time zone offset minute out of range"}, 690 {"Z07:00:00", "+23:59:61", "time zone offset second out of range"}, 691} 692 693func TestParseErrors(t *testing.T) { 694 for _, test := range parseErrorTests { 695 _, err := Parse(test.format, test.value) 696 if err == nil { 697 t.Errorf("expected error for %q %q", test.format, test.value) 698 } else if !strings.Contains(err.Error(), test.expect) { 699 t.Errorf("expected error with %q for %q %q; got %s", test.expect, test.format, test.value, err) 700 } 701 } 702} 703 704func TestNoonIs12PM(t *testing.T) { 705 noon := Date(0, January, 1, 12, 0, 0, 0, UTC) 706 const expect = "12:00PM" 707 got := noon.Format("3:04PM") 708 if got != expect { 709 t.Errorf("got %q; expect %q", got, expect) 710 } 711 got = noon.Format("03:04PM") 712 if got != expect { 713 t.Errorf("got %q; expect %q", got, expect) 714 } 715} 716 717func TestMidnightIs12AM(t *testing.T) { 718 midnight := Date(0, January, 1, 0, 0, 0, 0, UTC) 719 expect := "12:00AM" 720 got := midnight.Format("3:04PM") 721 if got != expect { 722 t.Errorf("got %q; expect %q", got, expect) 723 } 724 got = midnight.Format("03:04PM") 725 if got != expect { 726 t.Errorf("got %q; expect %q", got, expect) 727 } 728} 729 730func Test12PMIsNoon(t *testing.T) { 731 noon, err := Parse("3:04PM", "12:00PM") 732 if err != nil { 733 t.Fatal("error parsing date:", err) 734 } 735 if noon.Hour() != 12 { 736 t.Errorf("got %d; expect 12", noon.Hour()) 737 } 738 noon, err = Parse("03:04PM", "12:00PM") 739 if err != nil { 740 t.Fatal("error parsing date:", err) 741 } 742 if noon.Hour() != 12 { 743 t.Errorf("got %d; expect 12", noon.Hour()) 744 } 745} 746 747func Test12AMIsMidnight(t *testing.T) { 748 midnight, err := Parse("3:04PM", "12:00AM") 749 if err != nil { 750 t.Fatal("error parsing date:", err) 751 } 752 if midnight.Hour() != 0 { 753 t.Errorf("got %d; expect 0", midnight.Hour()) 754 } 755 midnight, err = Parse("03:04PM", "12:00AM") 756 if err != nil { 757 t.Fatal("error parsing date:", err) 758 } 759 if midnight.Hour() != 0 { 760 t.Errorf("got %d; expect 0", midnight.Hour()) 761 } 762} 763 764// Check that a time without a Zone still produces a (numeric) time zone 765// when formatted with MST as a requested zone. 766func TestMissingZone(t *testing.T) { 767 time, err := Parse(RubyDate, "Thu Feb 02 16:10:03 -0500 2006") 768 if err != nil { 769 t.Fatal("error parsing date:", err) 770 } 771 expect := "Thu Feb 2 16:10:03 -0500 2006" // -0500 not EST 772 str := time.Format(UnixDate) // uses MST as its time zone 773 if str != expect { 774 t.Errorf("got %s; expect %s", str, expect) 775 } 776} 777 778func TestMinutesInTimeZone(t *testing.T) { 779 time, err := Parse(RubyDate, "Mon Jan 02 15:04:05 +0123 2006") 780 if err != nil { 781 t.Fatal("error parsing date:", err) 782 } 783 expected := (1*60 + 23) * 60 784 _, offset := time.Zone() 785 if offset != expected { 786 t.Errorf("ZoneOffset = %d, want %d", offset, expected) 787 } 788} 789 790type SecondsTimeZoneOffsetTest struct { 791 format string 792 value string 793 expectedoffset int 794} 795 796var secondsTimeZoneOffsetTests = []SecondsTimeZoneOffsetTest{ 797 {"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)}, 798 {"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02-00:34:08", -(34*60 + 8)}, 799 {"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02+003408", 34*60 + 8}, 800 {"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8}, 801 {"2006-01-02T15:04:05Z070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)}, 802 {"2006-01-02T15:04:05Z07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8}, 803 {"2006-01-02T15:04:05-07", "1871-01-01T05:33:02+01", 1 * 60 * 60}, 804 {"2006-01-02T15:04:05-07", "1871-01-01T05:33:02-02", -2 * 60 * 60}, 805 {"2006-01-02T15:04:05Z07", "1871-01-01T05:33:02-02", -2 * 60 * 60}, 806} 807 808func TestParseSecondsInTimeZone(t *testing.T) { 809 // should accept timezone offsets with seconds like: Zone America/New_York -4:56:02 - LMT 1883 Nov 18 12:03:58 810 for _, test := range secondsTimeZoneOffsetTests { 811 time, err := Parse(test.format, test.value) 812 if err != nil { 813 t.Fatal("error parsing date:", err) 814 } 815 _, offset := time.Zone() 816 if offset != test.expectedoffset { 817 t.Errorf("ZoneOffset = %d, want %d", offset, test.expectedoffset) 818 } 819 } 820} 821 822func TestFormatSecondsInTimeZone(t *testing.T) { 823 for _, test := range secondsTimeZoneOffsetTests { 824 d := Date(1871, 1, 1, 5, 33, 2, 0, FixedZone("LMT", test.expectedoffset)) 825 timestr := d.Format(test.format) 826 if timestr != test.value { 827 t.Errorf("Format = %s, want %s", timestr, test.value) 828 } 829 } 830} 831 832// Issue 11334. 833func TestUnderscoreTwoThousand(t *testing.T) { 834 format := "15:04_20060102" 835 input := "14:38_20150618" 836 time, err := Parse(format, input) 837 if err != nil { 838 t.Error(err) 839 } 840 if y, m, d := time.Date(); y != 2015 || m != 6 || d != 18 { 841 t.Errorf("Incorrect y/m/d, got %d/%d/%d", y, m, d) 842 } 843 if h := time.Hour(); h != 14 { 844 t.Errorf("Incorrect hour, got %d", h) 845 } 846 if m := time.Minute(); m != 38 { 847 t.Errorf("Incorrect minute, got %d", m) 848 } 849} 850 851// Issue 29918, 29916 852func TestStd0xParseError(t *testing.T) { 853 tests := []struct { 854 format, value, valueElemPrefix string 855 }{ 856 {"01 MST", "0 MST", "0"}, 857 {"01 MST", "1 MST", "1"}, 858 {RFC850, "Thursday, 04-Feb-1 21:00:57 PST", "1"}, 859 } 860 for _, tt := range tests { 861 _, err := Parse(tt.format, tt.value) 862 if err == nil { 863 t.Errorf("Parse(%q, %q) did not fail as expected", tt.format, tt.value) 864 } else if perr, ok := err.(*ParseError); !ok { 865 t.Errorf("Parse(%q, %q) returned error type %T, expected ParseError", tt.format, tt.value, perr) 866 } else if !strings.Contains(perr.Error(), "cannot parse") || !strings.HasPrefix(perr.ValueElem, tt.valueElemPrefix) { 867 t.Errorf("Parse(%q, %q) returned wrong parsing error message: %v", tt.format, tt.value, perr) 868 } 869 } 870} 871 872var monthOutOfRangeTests = []struct { 873 value string 874 ok bool 875}{ 876 {"00-01", false}, 877 {"13-01", false}, 878 {"01-01", true}, 879} 880 881func TestParseMonthOutOfRange(t *testing.T) { 882 for _, test := range monthOutOfRangeTests { 883 _, err := Parse("01-02", test.value) 884 switch { 885 case !test.ok && err != nil: 886 if !strings.Contains(err.Error(), "month out of range") { 887 t.Errorf("%q: expected 'month' error, got %v", test.value, err) 888 } 889 case test.ok && err != nil: 890 t.Errorf("%q: unexpected error: %v", test.value, err) 891 case !test.ok && err == nil: 892 t.Errorf("%q: expected 'month' error, got none", test.value) 893 } 894 } 895} 896 897// Issue 37387. 898func TestParseYday(t *testing.T) { 899 t.Parallel() 900 for i := 1; i <= 365; i++ { 901 d := fmt.Sprintf("2020-%03d", i) 902 tm, err := Parse("2006-002", d) 903 if err != nil { 904 t.Errorf("unexpected error for %s: %v", d, err) 905 } else if tm.Year() != 2020 || tm.YearDay() != i { 906 t.Errorf("got year %d yearday %d, want %d %d", tm.Year(), tm.YearDay(), 2020, i) 907 } 908 } 909} 910 911// Issue 45391. 912func TestQuote(t *testing.T) { 913 tests := []struct { 914 s, want string 915 }{ 916 {`"`, `"\""`}, 917 {`abc"xyz"`, `"abc\"xyz\""`}, 918 {"", `""`}, 919 {"abc", `"abc"`}, 920 {`☺`, `"\xe2\x98\xba"`}, 921 {`☺ hello ☺ hello`, `"\xe2\x98\xba hello \xe2\x98\xba hello"`}, 922 {"\x04", `"\x04"`}, 923 } 924 for _, tt := range tests { 925 if q := Quote(tt.s); q != tt.want { 926 t.Errorf("Quote(%q) = got %q, want %q", tt.s, q, tt.want) 927 } 928 } 929 930} 931 932// Issue 48037 933func TestFormatFractionalSecondSeparators(t *testing.T) { 934 tests := []struct { 935 s, want string 936 }{ 937 {`15:04:05.000`, `21:00:57.012`}, 938 {`15:04:05.999`, `21:00:57.012`}, 939 {`15:04:05,000`, `21:00:57,012`}, 940 {`15:04:05,999`, `21:00:57,012`}, 941 } 942 943 // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2009 944 time := Unix(0, 1233810057012345600) 945 for _, tt := range tests { 946 if q := time.Format(tt.s); q != tt.want { 947 t.Errorf("Format(%q) = got %q, want %q", tt.s, q, tt.want) 948 } 949 } 950} 951 952var longFractionalDigitsTests = []struct { 953 value string 954 want int 955}{ 956 // 9 digits 957 {"2021-09-29T16:04:33.000000000Z", 0}, 958 {"2021-09-29T16:04:33.000000001Z", 1}, 959 {"2021-09-29T16:04:33.100000000Z", 100_000_000}, 960 {"2021-09-29T16:04:33.100000001Z", 100_000_001}, 961 {"2021-09-29T16:04:33.999999999Z", 999_999_999}, 962 {"2021-09-29T16:04:33.012345678Z", 12_345_678}, 963 // 10 digits, truncates 964 {"2021-09-29T16:04:33.0000000000Z", 0}, 965 {"2021-09-29T16:04:33.0000000001Z", 0}, 966 {"2021-09-29T16:04:33.1000000000Z", 100_000_000}, 967 {"2021-09-29T16:04:33.1000000009Z", 100_000_000}, 968 {"2021-09-29T16:04:33.9999999999Z", 999_999_999}, 969 {"2021-09-29T16:04:33.0123456789Z", 12_345_678}, 970 // 11 digits, truncates 971 {"2021-09-29T16:04:33.10000000000Z", 100_000_000}, 972 {"2021-09-29T16:04:33.00123456789Z", 1_234_567}, 973 // 12 digits, truncates 974 {"2021-09-29T16:04:33.000123456789Z", 123_456}, 975 // 15 digits, truncates 976 {"2021-09-29T16:04:33.9999999999999999Z", 999_999_999}, 977} 978 979// Issue 48685 and 54567. 980func TestParseFractionalSecondsLongerThanNineDigits(t *testing.T) { 981 for _, tt := range longFractionalDigitsTests { 982 for _, format := range []string{RFC3339, RFC3339Nano} { 983 tm, err := Parse(format, tt.value) 984 if err != nil { 985 t.Errorf("Parse(%q, %q) error: %v", format, tt.value, err) 986 continue 987 } 988 if got := tm.Nanosecond(); got != tt.want { 989 t.Errorf("Parse(%q, %q) = got %d, want %d", format, tt.value, got, tt.want) 990 } 991 } 992 } 993} 994 995func FuzzFormatRFC3339(f *testing.F) { 996 for _, ts := range [][2]int64{ 997 {math.MinInt64, math.MinInt64}, // 292277026304-08-26T15:42:51Z 998 {-62167219200, 0}, // 0000-01-01T00:00:00Z 999 {1661201140, 676836973}, // 2022-08-22T20:45:40.676836973Z 1000 {253402300799, 999999999}, // 9999-12-31T23:59:59.999999999Z 1001 {math.MaxInt64, math.MaxInt64}, // -292277022365-05-08T08:17:07Z 1002 } { 1003 f.Add(ts[0], ts[1], true, false, 0) 1004 f.Add(ts[0], ts[1], false, true, 0) 1005 for _, offset := range []int{0, 60, 60 * 60, 99*60*60 + 99*60, 123456789} { 1006 f.Add(ts[0], ts[1], false, false, -offset) 1007 f.Add(ts[0], ts[1], false, false, +offset) 1008 } 1009 } 1010 1011 f.Fuzz(func(t *testing.T, sec, nsec int64, useUTC, useLocal bool, tzOffset int) { 1012 var loc *Location 1013 switch { 1014 case useUTC: 1015 loc = UTC 1016 case useLocal: 1017 loc = Local 1018 default: 1019 loc = FixedZone("", tzOffset) 1020 } 1021 ts := Unix(sec, nsec).In(loc) 1022 1023 got := AppendFormatRFC3339(ts, nil, false) 1024 want := AppendFormatAny(ts, nil, RFC3339) 1025 if !bytes.Equal(got, want) { 1026 t.Errorf("Format(%s, RFC3339) mismatch:\n\tgot: %s\n\twant: %s", ts, got, want) 1027 } 1028 1029 gotNanos := AppendFormatRFC3339(ts, nil, true) 1030 wantNanos := AppendFormatAny(ts, nil, RFC3339Nano) 1031 if !bytes.Equal(gotNanos, wantNanos) { 1032 t.Errorf("Format(%s, RFC3339Nano) mismatch:\n\tgot: %s\n\twant: %s", ts, gotNanos, wantNanos) 1033 } 1034 }) 1035} 1036 1037func FuzzParseRFC3339(f *testing.F) { 1038 for _, tt := range formatTests { 1039 f.Add(tt.result) 1040 } 1041 for _, tt := range parseTests { 1042 f.Add(tt.value) 1043 } 1044 for _, tt := range parseErrorTests { 1045 f.Add(tt.value) 1046 } 1047 for _, tt := range longFractionalDigitsTests { 1048 f.Add(tt.value) 1049 } 1050 1051 f.Fuzz(func(t *testing.T, s string) { 1052 // equalTime is like time.Time.Equal, but also compares the time zone. 1053 equalTime := func(t1, t2 Time) bool { 1054 name1, offset1 := t1.Zone() 1055 name2, offset2 := t2.Zone() 1056 return t1.Equal(t2) && name1 == name2 && offset1 == offset2 1057 } 1058 1059 for _, tz := range []*Location{UTC, Local} { 1060 // Parsing as RFC3339 or RFC3339Nano should be identical. 1061 t1, err1 := ParseAny(RFC3339, s, UTC, tz) 1062 t2, err2 := ParseAny(RFC3339Nano, s, UTC, tz) 1063 switch { 1064 case (err1 == nil) != (err2 == nil): 1065 t.Fatalf("ParseAny(%q) error mismatch:\n\tgot: %v\n\twant: %v", s, err1, err2) 1066 case !equalTime(t1, t2): 1067 t.Fatalf("ParseAny(%q) value mismatch:\n\tgot: %v\n\twant: %v", s, t1, t2) 1068 } 1069 1070 // TODO(https://go.dev/issue/54580): 1071 // Remove these checks after ParseAny rejects all invalid RFC 3339. 1072 if err1 == nil { 1073 num2 := func(s string) byte { return 10*(s[0]-'0') + (s[1] - '0') } 1074 switch { 1075 case len(s) > 12 && s[12] == ':': 1076 t.Skipf("ParseAny(%q) incorrectly allows single-digit hour fields", s) 1077 case len(s) > 19 && s[19] == ',': 1078 t.Skipf("ParseAny(%q) incorrectly allows comma as sub-second separator", s) 1079 case !strings.HasSuffix(s, "Z") && len(s) > 4 && (num2(s[len(s)-5:]) >= 24 || num2(s[len(s)-2:]) >= 60): 1080 t.Skipf("ParseAny(%q) incorrectly allows out-of-range zone offset", s) 1081 } 1082 } 1083 1084 // Customized parser should be identical to general parser. 1085 switch got, ok := ParseRFC3339(s, tz); { 1086 case ok != (err1 == nil): 1087 t.Fatalf("ParseRFC3339(%q) error mismatch:\n\tgot: %v\n\twant: %v", s, ok, err1 == nil) 1088 case !equalTime(got, t1): 1089 t.Fatalf("ParseRFC3339(%q) value mismatch:\n\tgot: %v\n\twant: %v", s, got, t2) 1090 } 1091 } 1092 }) 1093} 1094