github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/date_parser.go (about) 1 package goja 2 3 // This is a slightly modified version of the standard Go parser to make it more compatible with ECMAScript 5.1 4 // Changes: 5 // - 6-digit extended years are supported in place of long year (2006) in the form of +123456 6 // - Timezone formats tolerate colons, e.g. -0700 will parse -07:00 7 // - Short week day will also parse long week day 8 // - Short month ("Jan") will also parse long month ("January") 9 // - Long day ("02") will also parse short day ("2"). 10 // - Timezone in brackets, "(MST)", will match any string in brackets (e.g. "(GMT Standard Time)") 11 // - If offset is not set and timezone name is unknown, an error is returned 12 // - If offset and timezone name are both set the offset takes precedence and the resulting Location will be FixedZone("", offset) 13 14 // Original copyright message: 15 16 // Copyright 2010 The Go Authors. All rights reserved. 17 // Use of this source code is governed by a BSD-style 18 // license that can be found in the LICENSE file. 19 20 import ( 21 "errors" 22 "time" 23 ) 24 25 const ( 26 _ = iota 27 stdLongMonth = iota + stdNeedDate // "January" 28 stdMonth // "Jan" 29 stdNumMonth // "1" 30 stdZeroMonth // "01" 31 stdLongWeekDay // "Monday" 32 stdWeekDay // "Mon" 33 stdDay // "2" 34 stdUnderDay // "_2" 35 stdZeroDay // "02" 36 stdHour = iota + stdNeedClock // "15" 37 stdHour12 // "3" 38 stdZeroHour12 // "03" 39 stdMinute // "4" 40 stdZeroMinute // "04" 41 stdSecond // "5" 42 stdZeroSecond // "05" 43 stdLongYear = iota + stdNeedDate // "2006" 44 stdYear // "06" 45 stdPM = iota + stdNeedClock // "PM" 46 stdpm // "pm" 47 stdTZ = iota // "MST" 48 stdBracketTZ // "(MST)" 49 stdISO8601TZ // "Z0700" // prints Z for UTC 50 stdISO8601SecondsTZ // "Z070000" 51 stdISO8601ShortTZ // "Z07" 52 stdISO8601ColonTZ // "Z07:00" // prints Z for UTC 53 stdISO8601ColonSecondsTZ // "Z07:00:00" 54 stdNumTZ // "-0700" // always numeric 55 stdNumSecondsTz // "-070000" 56 stdNumShortTZ // "-07" // always numeric 57 stdNumColonTZ // "-07:00" // always numeric 58 stdNumColonSecondsTZ // "-07:00:00" 59 stdFracSecond0 // ".0", ".00", ... , trailing zeros included 60 stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted 61 62 stdNeedDate = 1 << 8 // need month, day, year 63 stdNeedClock = 2 << 8 // need hour, minute, second 64 stdArgShift = 16 // extra argument in high bits, above low stdArgShift 65 stdMask = 1<<stdArgShift - 1 // mask out argument 66 ) 67 68 var errBad = errors.New("bad value for field") // placeholder not passed to user 69 70 func parseDate(layout, value string, defaultLocation *time.Location) (time.Time, error) { 71 alayout, avalue := layout, value 72 rangeErrString := "" // set if a value is out of range 73 amSet := false // do we need to subtract 12 from the hour for midnight? 74 pmSet := false // do we need to add 12 to the hour? 75 76 // Time being constructed. 77 var ( 78 year int 79 month int = 1 // January 80 day int = 1 81 hour int 82 min int 83 sec int 84 nsec int 85 z *time.Location 86 zoneOffset int = -1 87 zoneName string 88 ) 89 90 // Each iteration processes one std value. 91 for { 92 var err error 93 prefix, std, suffix := nextStdChunk(layout) 94 stdstr := layout[len(prefix) : len(layout)-len(suffix)] 95 value, err = skip(value, prefix) 96 if err != nil { 97 return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: prefix, ValueElem: value} 98 } 99 if std == 0 { 100 if len(value) != 0 { 101 return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": extra text: " + value} 102 } 103 break 104 } 105 layout = suffix 106 var p string 107 switch std & stdMask { 108 case stdYear: 109 if len(value) < 2 { 110 err = errBad 111 break 112 } 113 p, value = value[0:2], value[2:] 114 year, err = atoi(p) 115 if year >= 69 { // Unix time starts Dec 31 1969 in some time zones 116 year += 1900 117 } else { 118 year += 2000 119 } 120 case stdLongYear: 121 if len(value) >= 7 && (value[0] == '-' || value[0] == '+') { // extended year 122 neg := value[0] == '-' 123 p, value = value[1:7], value[7:] 124 year, err = atoi(p) 125 if neg { 126 year = -year 127 } 128 } else { 129 if len(value) < 4 || !isDigit(value, 0) { 130 err = errBad 131 break 132 } 133 p, value = value[0:4], value[4:] 134 year, err = atoi(p) 135 } 136 137 case stdMonth: 138 month, value, err = lookup(longMonthNames, value) 139 if err != nil { 140 month, value, err = lookup(shortMonthNames, value) 141 } 142 month++ 143 case stdLongMonth: 144 month, value, err = lookup(longMonthNames, value) 145 month++ 146 case stdNumMonth, stdZeroMonth: 147 month, value, err = getnum(value, std == stdZeroMonth) 148 if month <= 0 || 12 < month { 149 rangeErrString = "month" 150 } 151 case stdWeekDay: 152 // Ignore weekday except for error checking. 153 _, value, err = lookup(longDayNames, value) 154 if err != nil { 155 _, value, err = lookup(shortDayNames, value) 156 } 157 case stdLongWeekDay: 158 _, value, err = lookup(longDayNames, value) 159 case stdDay, stdUnderDay, stdZeroDay: 160 if std == stdUnderDay && len(value) > 0 && value[0] == ' ' { 161 value = value[1:] 162 } 163 day, value, err = getnum(value, false) 164 if day < 0 { 165 // Note that we allow any one- or two-digit day here. 166 rangeErrString = "day" 167 } 168 case stdHour: 169 hour, value, err = getnum(value, false) 170 if hour < 0 || 24 <= hour { 171 rangeErrString = "hour" 172 } 173 case stdHour12, stdZeroHour12: 174 hour, value, err = getnum(value, std == stdZeroHour12) 175 if hour < 0 || 12 < hour { 176 rangeErrString = "hour" 177 } 178 case stdMinute, stdZeroMinute: 179 min, value, err = getnum(value, std == stdZeroMinute) 180 if min < 0 || 60 <= min { 181 rangeErrString = "minute" 182 } 183 case stdSecond, stdZeroSecond: 184 sec, value, err = getnum(value, std == stdZeroSecond) 185 if sec < 0 || 60 <= sec { 186 rangeErrString = "second" 187 break 188 } 189 // Special case: do we have a fractional second but no 190 // fractional second in the format? 191 if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) { 192 _, std, _ = nextStdChunk(layout) 193 std &= stdMask 194 if std == stdFracSecond0 || std == stdFracSecond9 { 195 // Fractional second in the layout; proceed normally 196 break 197 } 198 // No fractional second in the layout but we have one in the input. 199 n := 2 200 for ; n < len(value) && isDigit(value, n); n++ { 201 } 202 nsec, rangeErrString, err = parseNanoseconds(value, n) 203 value = value[n:] 204 } 205 case stdPM: 206 if len(value) < 2 { 207 err = errBad 208 break 209 } 210 p, value = value[0:2], value[2:] 211 switch p { 212 case "PM": 213 pmSet = true 214 case "AM": 215 amSet = true 216 default: 217 err = errBad 218 } 219 case stdpm: 220 if len(value) < 2 { 221 err = errBad 222 break 223 } 224 p, value = value[0:2], value[2:] 225 switch p { 226 case "pm": 227 pmSet = true 228 case "am": 229 amSet = true 230 default: 231 err = errBad 232 } 233 case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: 234 if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ || 235 std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) && len(value) >= 1 && value[0] == 'Z' { 236 237 value = value[1:] 238 z = time.UTC 239 break 240 } 241 var sign, hour, min, seconds string 242 if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdNumTZ || std == stdISO8601TZ { 243 if len(value) < 4 { 244 err = errBad 245 break 246 } 247 if value[3] != ':' { 248 if std == stdNumColonTZ || std == stdISO8601ColonTZ || len(value) < 5 { 249 err = errBad 250 break 251 } 252 sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:] 253 } else { 254 if len(value) < 6 { 255 err = errBad 256 break 257 } 258 sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:] 259 } 260 } else if std == stdNumShortTZ || std == stdISO8601ShortTZ { 261 if len(value) < 3 { 262 err = errBad 263 break 264 } 265 sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:] 266 } else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ || std == stdISO8601SecondsTZ || std == stdNumSecondsTz { 267 if len(value) < 7 { 268 err = errBad 269 break 270 } 271 if value[3] != ':' || value[6] != ':' { 272 if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ || len(value) < 7 { 273 err = errBad 274 break 275 } 276 sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:] 277 } else { 278 if len(value) < 9 { 279 err = errBad 280 break 281 } 282 sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:] 283 } 284 } 285 var hr, mm, ss int 286 hr, err = atoi(hour) 287 if err == nil { 288 mm, err = atoi(min) 289 } 290 if err == nil { 291 ss, err = atoi(seconds) 292 } 293 zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds 294 switch sign[0] { 295 case '+': 296 case '-': 297 zoneOffset = -zoneOffset 298 default: 299 err = errBad 300 } 301 case stdTZ: 302 // Does it look like a time zone? 303 if len(value) >= 3 && value[0:3] == "UTC" { 304 z = time.UTC 305 value = value[3:] 306 break 307 } 308 n, ok := parseTimeZone(value) 309 if !ok { 310 err = errBad 311 break 312 } 313 zoneName, value = value[:n], value[n:] 314 case stdBracketTZ: 315 if len(value) < 3 || value[0] != '(' { 316 err = errBad 317 break 318 } 319 i := 1 320 for ; ; i++ { 321 if i >= len(value) { 322 err = errBad 323 break 324 } 325 if value[i] == ')' { 326 zoneName, value = value[1:i], value[i+1:] 327 break 328 } 329 } 330 331 case stdFracSecond0: 332 // stdFracSecond0 requires the exact number of digits as specified in 333 // the layout. 334 ndigit := 1 + (std >> stdArgShift) 335 if len(value) < ndigit { 336 err = errBad 337 break 338 } 339 nsec, rangeErrString, err = parseNanoseconds(value, ndigit) 340 value = value[ndigit:] 341 342 case stdFracSecond9: 343 if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] { 344 // Fractional second omitted. 345 break 346 } 347 // Take any number of digits, even more than asked for, 348 // because it is what the stdSecond case would do. 349 i := 0 350 for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { 351 i++ 352 } 353 nsec, rangeErrString, err = parseNanoseconds(value, 1+i) 354 value = value[1+i:] 355 } 356 if rangeErrString != "" { 357 return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: stdstr, ValueElem: value, Message: ": " + rangeErrString + " out of range"} 358 } 359 if err != nil { 360 return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: stdstr, ValueElem: value} 361 } 362 } 363 if pmSet && hour < 12 { 364 hour += 12 365 } else if amSet && hour == 12 { 366 hour = 0 367 } 368 369 // Validate the day of the month. 370 if day < 1 || day > daysIn(time.Month(month), year) { 371 return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": day out of range"} 372 } 373 374 if z == nil { 375 if zoneOffset == -1 { 376 if zoneName != "" { 377 if z1, err := time.LoadLocation(zoneName); err == nil { 378 z = z1 379 } else { 380 return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": unknown timezone"} 381 } 382 } else { 383 z = defaultLocation 384 } 385 } else if zoneOffset == 0 { 386 z = time.UTC 387 } else { 388 z = time.FixedZone("", zoneOffset) 389 } 390 } 391 392 return time.Date(year, time.Month(month), day, hour, min, sec, nsec, z), nil 393 } 394 395 var errLeadingInt = errors.New("time: bad [0-9]*") // never printed 396 397 func signedLeadingInt(s string) (x int64, rem string, err error) { 398 neg := false 399 if s != "" && (s[0] == '-' || s[0] == '+') { 400 neg = s[0] == '-' 401 s = s[1:] 402 } 403 x, rem, err = leadingInt(s) 404 if err != nil { 405 return 406 } 407 408 if neg { 409 x = -x 410 } 411 return 412 } 413 414 // leadingInt consumes the leading [0-9]* from s. 415 func leadingInt(s string) (x int64, rem string, err error) { 416 i := 0 417 for ; i < len(s); i++ { 418 c := s[i] 419 if c < '0' || c > '9' { 420 break 421 } 422 if x > (1<<63-1)/10 { 423 // overflow 424 return 0, "", errLeadingInt 425 } 426 x = x*10 + int64(c) - '0' 427 if x < 0 { 428 // overflow 429 return 0, "", errLeadingInt 430 } 431 } 432 return x, s[i:], nil 433 } 434 435 // nextStdChunk finds the first occurrence of a std string in 436 // layout and returns the text before, the std string, and the text after. 437 func nextStdChunk(layout string) (prefix string, std int, suffix string) { 438 for i := 0; i < len(layout); i++ { 439 switch c := int(layout[i]); c { 440 case 'J': // January, Jan 441 if len(layout) >= i+3 && layout[i:i+3] == "Jan" { 442 if len(layout) >= i+7 && layout[i:i+7] == "January" { 443 return layout[0:i], stdLongMonth, layout[i+7:] 444 } 445 if !startsWithLowerCase(layout[i+3:]) { 446 return layout[0:i], stdMonth, layout[i+3:] 447 } 448 } 449 450 case 'M': // Monday, Mon, MST 451 if len(layout) >= i+3 { 452 if layout[i:i+3] == "Mon" { 453 if len(layout) >= i+6 && layout[i:i+6] == "Monday" { 454 return layout[0:i], stdLongWeekDay, layout[i+6:] 455 } 456 if !startsWithLowerCase(layout[i+3:]) { 457 return layout[0:i], stdWeekDay, layout[i+3:] 458 } 459 } 460 if layout[i:i+3] == "MST" { 461 return layout[0:i], stdTZ, layout[i+3:] 462 } 463 } 464 465 case '0': // 01, 02, 03, 04, 05, 06 466 if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' { 467 return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:] 468 } 469 470 case '1': // 15, 1 471 if len(layout) >= i+2 && layout[i+1] == '5' { 472 return layout[0:i], stdHour, layout[i+2:] 473 } 474 return layout[0:i], stdNumMonth, layout[i+1:] 475 476 case '2': // 2006, 2 477 if len(layout) >= i+4 && layout[i:i+4] == "2006" { 478 return layout[0:i], stdLongYear, layout[i+4:] 479 } 480 return layout[0:i], stdDay, layout[i+1:] 481 482 case '_': // _2, _2006 483 if len(layout) >= i+2 && layout[i+1] == '2' { 484 //_2006 is really a literal _, followed by stdLongYear 485 if len(layout) >= i+5 && layout[i+1:i+5] == "2006" { 486 return layout[0 : i+1], stdLongYear, layout[i+5:] 487 } 488 return layout[0:i], stdUnderDay, layout[i+2:] 489 } 490 491 case '3': 492 return layout[0:i], stdHour12, layout[i+1:] 493 494 case '4': 495 return layout[0:i], stdMinute, layout[i+1:] 496 497 case '5': 498 return layout[0:i], stdSecond, layout[i+1:] 499 500 case 'P': // PM 501 if len(layout) >= i+2 && layout[i+1] == 'M' { 502 return layout[0:i], stdPM, layout[i+2:] 503 } 504 505 case 'p': // pm 506 if len(layout) >= i+2 && layout[i+1] == 'm' { 507 return layout[0:i], stdpm, layout[i+2:] 508 } 509 510 case '-': // -070000, -07:00:00, -0700, -07:00, -07 511 if len(layout) >= i+7 && layout[i:i+7] == "-070000" { 512 return layout[0:i], stdNumSecondsTz, layout[i+7:] 513 } 514 if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" { 515 return layout[0:i], stdNumColonSecondsTZ, layout[i+9:] 516 } 517 if len(layout) >= i+5 && layout[i:i+5] == "-0700" { 518 return layout[0:i], stdNumTZ, layout[i+5:] 519 } 520 if len(layout) >= i+6 && layout[i:i+6] == "-07:00" { 521 return layout[0:i], stdNumColonTZ, layout[i+6:] 522 } 523 if len(layout) >= i+3 && layout[i:i+3] == "-07" { 524 return layout[0:i], stdNumShortTZ, layout[i+3:] 525 } 526 527 case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00, 528 if len(layout) >= i+7 && layout[i:i+7] == "Z070000" { 529 return layout[0:i], stdISO8601SecondsTZ, layout[i+7:] 530 } 531 if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" { 532 return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:] 533 } 534 if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { 535 return layout[0:i], stdISO8601TZ, layout[i+5:] 536 } 537 if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { 538 return layout[0:i], stdISO8601ColonTZ, layout[i+6:] 539 } 540 if len(layout) >= i+3 && layout[i:i+3] == "Z07" { 541 return layout[0:i], stdISO8601ShortTZ, layout[i+3:] 542 } 543 544 case '.': // .000 or .999 - repeated digits for fractional seconds. 545 if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { 546 ch := layout[i+1] 547 j := i + 1 548 for j < len(layout) && layout[j] == ch { 549 j++ 550 } 551 // String of digits must end here - only fractional second is all digits. 552 if !isDigit(layout, j) { 553 std := stdFracSecond0 554 if layout[i+1] == '9' { 555 std = stdFracSecond9 556 } 557 std |= (j - (i + 1)) << stdArgShift 558 return layout[0:i], std, layout[j:] 559 } 560 } 561 case '(': 562 if len(layout) >= i+5 && layout[i:i+5] == "(MST)" { 563 return layout[0:i], stdBracketTZ, layout[i+5:] 564 } 565 } 566 } 567 return layout, 0, "" 568 } 569 570 var longDayNames = []string{ 571 "Sunday", 572 "Monday", 573 "Tuesday", 574 "Wednesday", 575 "Thursday", 576 "Friday", 577 "Saturday", 578 } 579 580 var shortDayNames = []string{ 581 "Sun", 582 "Mon", 583 "Tue", 584 "Wed", 585 "Thu", 586 "Fri", 587 "Sat", 588 } 589 590 var shortMonthNames = []string{ 591 "Jan", 592 "Feb", 593 "Mar", 594 "Apr", 595 "May", 596 "Jun", 597 "Jul", 598 "Aug", 599 "Sep", 600 "Oct", 601 "Nov", 602 "Dec", 603 } 604 605 var longMonthNames = []string{ 606 "January", 607 "February", 608 "March", 609 "April", 610 "May", 611 "June", 612 "July", 613 "August", 614 "September", 615 "October", 616 "November", 617 "December", 618 } 619 620 // isDigit reports whether s[i] is in range and is a decimal digit. 621 func isDigit(s string, i int) bool { 622 if len(s) <= i { 623 return false 624 } 625 c := s[i] 626 return '0' <= c && c <= '9' 627 } 628 629 // getnum parses s[0:1] or s[0:2] (fixed forces the latter) 630 // as a decimal integer and returns the integer and the 631 // remainder of the string. 632 func getnum(s string, fixed bool) (int, string, error) { 633 if !isDigit(s, 0) { 634 return 0, s, errBad 635 } 636 if !isDigit(s, 1) { 637 if fixed { 638 return 0, s, errBad 639 } 640 return int(s[0] - '0'), s[1:], nil 641 } 642 return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil 643 } 644 645 func cutspace(s string) string { 646 for len(s) > 0 && s[0] == ' ' { 647 s = s[1:] 648 } 649 return s 650 } 651 652 // skip removes the given prefix from value, 653 // treating runs of space characters as equivalent. 654 func skip(value, prefix string) (string, error) { 655 for len(prefix) > 0 { 656 if prefix[0] == ' ' { 657 if len(value) > 0 && value[0] != ' ' { 658 return value, errBad 659 } 660 prefix = cutspace(prefix) 661 value = cutspace(value) 662 continue 663 } 664 if len(value) == 0 || value[0] != prefix[0] { 665 return value, errBad 666 } 667 prefix = prefix[1:] 668 value = value[1:] 669 } 670 return value, nil 671 } 672 673 // Never printed, just needs to be non-nil for return by atoi. 674 var atoiError = errors.New("time: invalid number") 675 676 // Duplicates functionality in strconv, but avoids dependency. 677 func atoi(s string) (x int, err error) { 678 q, rem, err := signedLeadingInt(s) 679 x = int(q) 680 if err != nil || rem != "" { 681 return 0, atoiError 682 } 683 return x, nil 684 } 685 686 // match reports whether s1 and s2 match ignoring case. 687 // It is assumed s1 and s2 are the same length. 688 func match(s1, s2 string) bool { 689 for i := 0; i < len(s1); i++ { 690 c1 := s1[i] 691 c2 := s2[i] 692 if c1 != c2 { 693 // Switch to lower-case; 'a'-'A' is known to be a single bit. 694 c1 |= 'a' - 'A' 695 c2 |= 'a' - 'A' 696 if c1 != c2 || c1 < 'a' || c1 > 'z' { 697 return false 698 } 699 } 700 } 701 return true 702 } 703 704 func lookup(tab []string, val string) (int, string, error) { 705 for i, v := range tab { 706 if len(val) >= len(v) && match(val[0:len(v)], v) { 707 return i, val[len(v):], nil 708 } 709 } 710 return -1, val, errBad 711 } 712 713 // daysBefore[m] counts the number of days in a non-leap year 714 // before month m begins. There is an entry for m=12, counting 715 // the number of days before January of next year (365). 716 var daysBefore = [...]int32{ 717 0, 718 31, 719 31 + 28, 720 31 + 28 + 31, 721 31 + 28 + 31 + 30, 722 31 + 28 + 31 + 30 + 31, 723 31 + 28 + 31 + 30 + 31 + 30, 724 31 + 28 + 31 + 30 + 31 + 30 + 31, 725 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 726 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 727 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 728 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 729 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, 730 } 731 732 func isLeap(year int) bool { 733 return year%4 == 0 && (year%100 != 0 || year%400 == 0) 734 } 735 736 func daysIn(m time.Month, year int) int { 737 if m == time.February && isLeap(year) { 738 return 29 739 } 740 return int(daysBefore[m] - daysBefore[m-1]) 741 } 742 743 // parseTimeZone parses a time zone string and returns its length. Time zones 744 // are human-generated and unpredictable. We can't do precise error checking. 745 // On the other hand, for a correct parse there must be a time zone at the 746 // beginning of the string, so it's almost always true that there's one 747 // there. We look at the beginning of the string for a run of upper-case letters. 748 // If there are more than 5, it's an error. 749 // If there are 4 or 5 and the last is a T, it's a time zone. 750 // If there are 3, it's a time zone. 751 // Otherwise, other than special cases, it's not a time zone. 752 // GMT is special because it can have an hour offset. 753 func parseTimeZone(value string) (length int, ok bool) { 754 if len(value) < 3 { 755 return 0, false 756 } 757 // Special case 1: ChST and MeST are the only zones with a lower-case letter. 758 if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") { 759 return 4, true 760 } 761 // Special case 2: GMT may have an hour offset; treat it specially. 762 if value[:3] == "GMT" { 763 length = parseGMT(value) 764 return length, true 765 } 766 // Special Case 3: Some time zones are not named, but have +/-00 format 767 if value[0] == '+' || value[0] == '-' { 768 length = parseSignedOffset(value) 769 return length, true 770 } 771 // How many upper-case letters are there? Need at least three, at most five. 772 var nUpper int 773 for nUpper = 0; nUpper < 6; nUpper++ { 774 if nUpper >= len(value) { 775 break 776 } 777 if c := value[nUpper]; c < 'A' || 'Z' < c { 778 break 779 } 780 } 781 switch nUpper { 782 case 0, 1, 2, 6: 783 return 0, false 784 case 5: // Must end in T to match. 785 if value[4] == 'T' { 786 return 5, true 787 } 788 case 4: 789 // Must end in T, except one special case. 790 if value[3] == 'T' || value[:4] == "WITA" { 791 return 4, true 792 } 793 case 3: 794 return 3, true 795 } 796 return 0, false 797 } 798 799 // parseGMT parses a GMT time zone. The input string is known to start "GMT". 800 // The function checks whether that is followed by a sign and a number in the 801 // range -14 through 12 excluding zero. 802 func parseGMT(value string) int { 803 value = value[3:] 804 if len(value) == 0 { 805 return 3 806 } 807 808 return 3 + parseSignedOffset(value) 809 } 810 811 // parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04"). 812 // The function checks for a signed number in the range -14 through +12 excluding zero. 813 // Returns length of the found offset string or 0 otherwise 814 func parseSignedOffset(value string) int { 815 sign := value[0] 816 if sign != '-' && sign != '+' { 817 return 0 818 } 819 x, rem, err := leadingInt(value[1:]) 820 if err != nil { 821 return 0 822 } 823 if sign == '-' { 824 x = -x 825 } 826 if x == 0 || x < -14 || 12 < x { 827 return 0 828 } 829 return len(value) - len(rem) 830 } 831 832 func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) { 833 if value[0] != '.' { 834 err = errBad 835 return 836 } 837 if ns, err = atoi(value[1:nbytes]); err != nil { 838 return 839 } 840 if ns < 0 || 1e9 <= ns { 841 rangeErrString = "fractional second" 842 return 843 } 844 // We need nanoseconds, which means scaling by the number 845 // of missing digits in the format, maximum length 10. If it's 846 // longer than 10, we won't scale. 847 scaleDigits := 10 - nbytes 848 for i := 0; i < scaleDigits; i++ { 849 ns *= 10 850 } 851 return 852 } 853 854 // std0x records the std values for "01", "02", ..., "06". 855 var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear} 856 857 // startsWithLowerCase reports whether the string has a lower-case letter at the beginning. 858 // Its purpose is to prevent matching strings like "Month" when looking for "Mon". 859 func startsWithLowerCase(str string) bool { 860 if len(str) == 0 { 861 return false 862 } 863 c := str[0] 864 return 'a' <= c && c <= 'z' 865 }