123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- package goja
- // This is a slightly modified version of the standard Go parser to make it more compatible with ECMAScript 5.1
- // Changes:
- // - 6-digit extended years are supported in place of long year (2006) in the form of +123456
- // - Timezone formats tolerate colons, e.g. -0700 will parse -07:00
- // - Short week day will also parse long week day
- // - Short month ("Jan") will also parse long month ("January")
- // - Long day ("02") will also parse short day ("2").
- // - Timezone in brackets, "(MST)", will match any string in brackets (e.g. "(GMT Standard Time)")
- // - If offset is not set and timezone name is unknown, an error is returned
- // - If offset and timezone name are both set the offset takes precedence and the resulting Location will be FixedZone("", offset)
- // Original copyright message:
- // Copyright 2010 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.
- import (
- "errors"
- "time"
- )
- const (
- _ = iota
- stdLongMonth = iota + stdNeedDate // "January"
- stdMonth // "Jan"
- stdNumMonth // "1"
- stdZeroMonth // "01"
- stdLongWeekDay // "Monday"
- stdWeekDay // "Mon"
- stdDay // "2"
- stdUnderDay // "_2"
- stdZeroDay // "02"
- stdHour = iota + stdNeedClock // "15"
- stdHour12 // "3"
- stdZeroHour12 // "03"
- stdMinute // "4"
- stdZeroMinute // "04"
- stdSecond // "5"
- stdZeroSecond // "05"
- stdLongYear = iota + stdNeedDate // "2006"
- stdYear // "06"
- stdPM = iota + stdNeedClock // "PM"
- stdpm // "pm"
- stdTZ = iota // "MST"
- stdBracketTZ // "(MST)"
- stdISO8601TZ // "Z0700" // prints Z for UTC
- stdISO8601SecondsTZ // "Z070000"
- stdISO8601ShortTZ // "Z07"
- stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
- stdISO8601ColonSecondsTZ // "Z07:00:00"
- stdNumTZ // "-0700" // always numeric
- stdNumSecondsTz // "-070000"
- stdNumShortTZ // "-07" // always numeric
- stdNumColonTZ // "-07:00" // always numeric
- stdNumColonSecondsTZ // "-07:00:00"
- stdFracSecond0 // ".0", ".00", ... , trailing zeros included
- stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
- stdNeedDate = 1 << 8 // need month, day, year
- stdNeedClock = 2 << 8 // need hour, minute, second
- stdArgShift = 16 // extra argument in high bits, above low stdArgShift
- stdMask = 1<<stdArgShift - 1 // mask out argument
- )
- var errBad = errors.New("bad value for field") // placeholder not passed to user
- func parseDate(layout, value string, defaultLocation *time.Location) (time.Time, error) {
- alayout, avalue := layout, value
- rangeErrString := "" // set if a value is out of range
- amSet := false // do we need to subtract 12 from the hour for midnight?
- pmSet := false // do we need to add 12 to the hour?
- // Time being constructed.
- var (
- year int
- month int = 1 // January
- day int = 1
- hour int
- min int
- sec int
- nsec int
- z *time.Location
- zoneOffset int = -1
- zoneName string
- )
- // Each iteration processes one std value.
- for {
- var err error
- prefix, std, suffix := nextStdChunk(layout)
- stdstr := layout[len(prefix) : len(layout)-len(suffix)]
- value, err = skip(value, prefix)
- if err != nil {
- return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: prefix, ValueElem: value}
- }
- if std == 0 {
- if len(value) != 0 {
- return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": extra text: " + value}
- }
- break
- }
- layout = suffix
- var p string
- switch std & stdMask {
- case stdYear:
- if len(value) < 2 {
- err = errBad
- break
- }
- p, value = value[0:2], value[2:]
- year, err = atoi(p)
- if year >= 69 { // Unix time starts Dec 31 1969 in some time zones
- year += 1900
- } else {
- year += 2000
- }
- case stdLongYear:
- if len(value) >= 7 && (value[0] == '-' || value[0] == '+') { // extended year
- neg := value[0] == '-'
- p, value = value[1:7], value[7:]
- year, err = atoi(p)
- if neg {
- year = -year
- }
- } else {
- if len(value) < 4 || !isDigit(value, 0) {
- err = errBad
- break
- }
- p, value = value[0:4], value[4:]
- year, err = atoi(p)
- }
- case stdMonth:
- month, value, err = lookup(longMonthNames, value)
- if err != nil {
- month, value, err = lookup(shortMonthNames, value)
- }
- month++
- case stdLongMonth:
- month, value, err = lookup(longMonthNames, value)
- month++
- case stdNumMonth, stdZeroMonth:
- month, value, err = getnum(value, std == stdZeroMonth)
- if month <= 0 || 12 < month {
- rangeErrString = "month"
- }
- case stdWeekDay:
- // Ignore weekday except for error checking.
- _, value, err = lookup(longDayNames, value)
- if err != nil {
- _, value, err = lookup(shortDayNames, value)
- }
- case stdLongWeekDay:
- _, value, err = lookup(longDayNames, value)
- case stdDay, stdUnderDay, stdZeroDay:
- if std == stdUnderDay && len(value) > 0 && value[0] == ' ' {
- value = value[1:]
- }
- day, value, err = getnum(value, false)
- if day < 0 {
- // Note that we allow any one- or two-digit day here.
- rangeErrString = "day"
- }
- case stdHour:
- hour, value, err = getnum(value, false)
- if hour < 0 || 24 <= hour {
- rangeErrString = "hour"
- }
- case stdHour12, stdZeroHour12:
- hour, value, err = getnum(value, std == stdZeroHour12)
- if hour < 0 || 12 < hour {
- rangeErrString = "hour"
- }
- case stdMinute, stdZeroMinute:
- min, value, err = getnum(value, std == stdZeroMinute)
- if min < 0 || 60 <= min {
- rangeErrString = "minute"
- }
- case stdSecond, stdZeroSecond:
- sec, value, err = getnum(value, std == stdZeroSecond)
- if sec < 0 || 60 <= sec {
- rangeErrString = "second"
- break
- }
- // Special case: do we have a fractional second but no
- // fractional second in the format?
- if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
- _, std, _ = nextStdChunk(layout)
- std &= stdMask
- if std == stdFracSecond0 || std == stdFracSecond9 {
- // Fractional second in the layout; proceed normally
- break
- }
- // No fractional second in the layout but we have one in the input.
- n := 2
- for ; n < len(value) && isDigit(value, n); n++ {
- }
- nsec, rangeErrString, err = parseNanoseconds(value, n)
- value = value[n:]
- }
- case stdPM:
- if len(value) < 2 {
- err = errBad
- break
- }
- p, value = value[0:2], value[2:]
- switch p {
- case "PM":
- pmSet = true
- case "AM":
- amSet = true
- default:
- err = errBad
- }
- case stdpm:
- if len(value) < 2 {
- err = errBad
- break
- }
- p, value = value[0:2], value[2:]
- switch p {
- case "pm":
- pmSet = true
- case "am":
- amSet = true
- default:
- err = errBad
- }
- case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
- if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ ||
- std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) && len(value) >= 1 && value[0] == 'Z' {
- value = value[1:]
- z = time.UTC
- break
- }
- var sign, hour, min, seconds string
- if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdNumTZ || std == stdISO8601TZ {
- if len(value) < 4 {
- err = errBad
- break
- }
- if value[3] != ':' {
- if std == stdNumColonTZ || std == stdISO8601ColonTZ || len(value) < 5 {
- err = errBad
- break
- }
- sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:]
- } else {
- if len(value) < 6 {
- err = errBad
- break
- }
- sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:]
- }
- } else if std == stdNumShortTZ || std == stdISO8601ShortTZ {
- if len(value) < 3 {
- err = errBad
- break
- }
- sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:]
- } else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ || std == stdISO8601SecondsTZ || std == stdNumSecondsTz {
- if len(value) < 7 {
- err = errBad
- break
- }
- if value[3] != ':' || value[6] != ':' {
- if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ || len(value) < 7 {
- err = errBad
- break
- }
- sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:]
- } else {
- if len(value) < 9 {
- err = errBad
- break
- }
- sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:]
- }
- }
- var hr, mm, ss int
- hr, err = atoi(hour)
- if err == nil {
- mm, err = atoi(min)
- }
- if err == nil {
- ss, err = atoi(seconds)
- }
- zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds
- switch sign[0] {
- case '+':
- case '-':
- zoneOffset = -zoneOffset
- default:
- err = errBad
- }
- case stdTZ:
- // Does it look like a time zone?
- if len(value) >= 3 && value[0:3] == "UTC" {
- z = time.UTC
- value = value[3:]
- break
- }
- n, ok := parseTimeZone(value)
- if !ok {
- err = errBad
- break
- }
- zoneName, value = value[:n], value[n:]
- case stdBracketTZ:
- if len(value) < 3 || value[0] != '(' {
- err = errBad
- break
- }
- i := 1
- for ; ; i++ {
- if i >= len(value) {
- err = errBad
- break
- }
- if value[i] == ')' {
- zoneName, value = value[1:i], value[i+1:]
- break
- }
- }
- case stdFracSecond0:
- // stdFracSecond0 requires the exact number of digits as specified in
- // the layout.
- ndigit := 1 + (std >> stdArgShift)
- if len(value) < ndigit {
- err = errBad
- break
- }
- nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
- value = value[ndigit:]
- case stdFracSecond9:
- if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
- // Fractional second omitted.
- break
- }
- // Take any number of digits, even more than asked for,
- // because it is what the stdSecond case would do.
- i := 0
- for i < 9 && i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' {
- i++
- }
- nsec, rangeErrString, err = parseNanoseconds(value, 1+i)
- value = value[1+i:]
- }
- if rangeErrString != "" {
- return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: stdstr, ValueElem: value, Message: ": " + rangeErrString + " out of range"}
- }
- if err != nil {
- return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, LayoutElem: stdstr, ValueElem: value}
- }
- }
- if pmSet && hour < 12 {
- hour += 12
- } else if amSet && hour == 12 {
- hour = 0
- }
- // Validate the day of the month.
- if day < 1 || day > daysIn(time.Month(month), year) {
- return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": day out of range"}
- }
- if z == nil {
- if zoneOffset == -1 {
- if zoneName != "" {
- if z1, err := time.LoadLocation(zoneName); err == nil {
- z = z1
- } else {
- return time.Time{}, &time.ParseError{Layout: alayout, Value: avalue, ValueElem: value, Message: ": unknown timezone"}
- }
- } else {
- z = defaultLocation
- }
- } else if zoneOffset == 0 {
- z = time.UTC
- } else {
- z = time.FixedZone("", zoneOffset)
- }
- }
- return time.Date(year, time.Month(month), day, hour, min, sec, nsec, z), nil
- }
- var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
- func signedLeadingInt(s string) (x int64, rem string, err error) {
- neg := false
- if s != "" && (s[0] == '-' || s[0] == '+') {
- neg = s[0] == '-'
- s = s[1:]
- }
- x, rem, err = leadingInt(s)
- if err != nil {
- return
- }
- if neg {
- x = -x
- }
- return
- }
- // leadingInt consumes the leading [0-9]* from s.
- func leadingInt(s string) (x int64, rem string, err error) {
- i := 0
- for ; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- break
- }
- if x > (1<<63-1)/10 {
- // overflow
- return 0, "", errLeadingInt
- }
- x = x*10 + int64(c) - '0'
- if x < 0 {
- // overflow
- return 0, "", errLeadingInt
- }
- }
- return x, s[i:], nil
- }
- // nextStdChunk finds the first occurrence of a std string in
- // layout and returns the text before, the std string, and the text after.
- func nextStdChunk(layout string) (prefix string, std int, suffix string) {
- for i := 0; i < len(layout); i++ {
- switch c := int(layout[i]); c {
- case 'J': // January, Jan
- if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
- if len(layout) >= i+7 && layout[i:i+7] == "January" {
- return layout[0:i], stdLongMonth, layout[i+7:]
- }
- if !startsWithLowerCase(layout[i+3:]) {
- return layout[0:i], stdMonth, layout[i+3:]
- }
- }
- case 'M': // Monday, Mon, MST
- if len(layout) >= i+3 {
- if layout[i:i+3] == "Mon" {
- if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
- return layout[0:i], stdLongWeekDay, layout[i+6:]
- }
- if !startsWithLowerCase(layout[i+3:]) {
- return layout[0:i], stdWeekDay, layout[i+3:]
- }
- }
- if layout[i:i+3] == "MST" {
- return layout[0:i], stdTZ, layout[i+3:]
- }
- }
- case '0': // 01, 02, 03, 04, 05, 06
- if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
- return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
- }
- case '1': // 15, 1
- if len(layout) >= i+2 && layout[i+1] == '5' {
- return layout[0:i], stdHour, layout[i+2:]
- }
- return layout[0:i], stdNumMonth, layout[i+1:]
- case '2': // 2006, 2
- if len(layout) >= i+4 && layout[i:i+4] == "2006" {
- return layout[0:i], stdLongYear, layout[i+4:]
- }
- return layout[0:i], stdDay, layout[i+1:]
- case '_': // _2, _2006
- if len(layout) >= i+2 && layout[i+1] == '2' {
- //_2006 is really a literal _, followed by stdLongYear
- if len(layout) >= i+5 && layout[i+1:i+5] == "2006" {
- return layout[0 : i+1], stdLongYear, layout[i+5:]
- }
- return layout[0:i], stdUnderDay, layout[i+2:]
- }
- case '3':
- return layout[0:i], stdHour12, layout[i+1:]
- case '4':
- return layout[0:i], stdMinute, layout[i+1:]
- case '5':
- return layout[0:i], stdSecond, layout[i+1:]
- case 'P': // PM
- if len(layout) >= i+2 && layout[i+1] == 'M' {
- return layout[0:i], stdPM, layout[i+2:]
- }
- case 'p': // pm
- if len(layout) >= i+2 && layout[i+1] == 'm' {
- return layout[0:i], stdpm, layout[i+2:]
- }
- case '-': // -070000, -07:00:00, -0700, -07:00, -07
- if len(layout) >= i+7 && layout[i:i+7] == "-070000" {
- return layout[0:i], stdNumSecondsTz, layout[i+7:]
- }
- if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" {
- return layout[0:i], stdNumColonSecondsTZ, layout[i+9:]
- }
- if len(layout) >= i+5 && layout[i:i+5] == "-0700" {
- return layout[0:i], stdNumTZ, layout[i+5:]
- }
- if len(layout) >= i+6 && layout[i:i+6] == "-07:00" {
- return layout[0:i], stdNumColonTZ, layout[i+6:]
- }
- if len(layout) >= i+3 && layout[i:i+3] == "-07" {
- return layout[0:i], stdNumShortTZ, layout[i+3:]
- }
- case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00,
- if len(layout) >= i+7 && layout[i:i+7] == "Z070000" {
- return layout[0:i], stdISO8601SecondsTZ, layout[i+7:]
- }
- if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" {
- return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:]
- }
- if len(layout) >= i+5 && layout[i:i+5] == "Z0700" {
- return layout[0:i], stdISO8601TZ, layout[i+5:]
- }
- if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" {
- return layout[0:i], stdISO8601ColonTZ, layout[i+6:]
- }
- if len(layout) >= i+3 && layout[i:i+3] == "Z07" {
- return layout[0:i], stdISO8601ShortTZ, layout[i+3:]
- }
- case '.': // .000 or .999 - repeated digits for fractional seconds.
- if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
- ch := layout[i+1]
- j := i + 1
- for j < len(layout) && layout[j] == ch {
- j++
- }
- // String of digits must end here - only fractional second is all digits.
- if !isDigit(layout, j) {
- std := stdFracSecond0
- if layout[i+1] == '9' {
- std = stdFracSecond9
- }
- std |= (j - (i + 1)) << stdArgShift
- return layout[0:i], std, layout[j:]
- }
- }
- case '(':
- if len(layout) >= i+5 && layout[i:i+5] == "(MST)" {
- return layout[0:i], stdBracketTZ, layout[i+5:]
- }
- }
- }
- return layout, 0, ""
- }
- var longDayNames = []string{
- "Sunday",
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday",
- }
- var shortDayNames = []string{
- "Sun",
- "Mon",
- "Tue",
- "Wed",
- "Thu",
- "Fri",
- "Sat",
- }
- var shortMonthNames = []string{
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- }
- var longMonthNames = []string{
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December",
- }
- // isDigit reports whether s[i] is in range and is a decimal digit.
- func isDigit(s string, i int) bool {
- if len(s) <= i {
- return false
- }
- c := s[i]
- return '0' <= c && c <= '9'
- }
- // getnum parses s[0:1] or s[0:2] (fixed forces the latter)
- // as a decimal integer and returns the integer and the
- // remainder of the string.
- func getnum(s string, fixed bool) (int, string, error) {
- if !isDigit(s, 0) {
- return 0, s, errBad
- }
- if !isDigit(s, 1) {
- if fixed {
- return 0, s, errBad
- }
- return int(s[0] - '0'), s[1:], nil
- }
- return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil
- }
- func cutspace(s string) string {
- for len(s) > 0 && s[0] == ' ' {
- s = s[1:]
- }
- return s
- }
- // skip removes the given prefix from value,
- // treating runs of space characters as equivalent.
- func skip(value, prefix string) (string, error) {
- for len(prefix) > 0 {
- if prefix[0] == ' ' {
- if len(value) > 0 && value[0] != ' ' {
- return value, errBad
- }
- prefix = cutspace(prefix)
- value = cutspace(value)
- continue
- }
- if len(value) == 0 || value[0] != prefix[0] {
- return value, errBad
- }
- prefix = prefix[1:]
- value = value[1:]
- }
- return value, nil
- }
- // Never printed, just needs to be non-nil for return by atoi.
- var atoiError = errors.New("time: invalid number")
- // Duplicates functionality in strconv, but avoids dependency.
- func atoi(s string) (x int, err error) {
- q, rem, err := signedLeadingInt(s)
- x = int(q)
- if err != nil || rem != "" {
- return 0, atoiError
- }
- return x, nil
- }
- // match reports whether s1 and s2 match ignoring case.
- // It is assumed s1 and s2 are the same length.
- func match(s1, s2 string) bool {
- for i := 0; i < len(s1); i++ {
- c1 := s1[i]
- c2 := s2[i]
- if c1 != c2 {
- // Switch to lower-case; 'a'-'A' is known to be a single bit.
- c1 |= 'a' - 'A'
- c2 |= 'a' - 'A'
- if c1 != c2 || c1 < 'a' || c1 > 'z' {
- return false
- }
- }
- }
- return true
- }
- func lookup(tab []string, val string) (int, string, error) {
- for i, v := range tab {
- if len(val) >= len(v) && match(val[0:len(v)], v) {
- return i, val[len(v):], nil
- }
- }
- return -1, val, errBad
- }
- // daysBefore[m] counts the number of days in a non-leap year
- // before month m begins. There is an entry for m=12, counting
- // the number of days before January of next year (365).
- var daysBefore = [...]int32{
- 0,
- 31,
- 31 + 28,
- 31 + 28 + 31,
- 31 + 28 + 31 + 30,
- 31 + 28 + 31 + 30 + 31,
- 31 + 28 + 31 + 30 + 31 + 30,
- 31 + 28 + 31 + 30 + 31 + 30 + 31,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
- }
- func isLeap(year int) bool {
- return year%4 == 0 && (year%100 != 0 || year%400 == 0)
- }
- func daysIn(m time.Month, year int) int {
- if m == time.February && isLeap(year) {
- return 29
- }
- return int(daysBefore[m] - daysBefore[m-1])
- }
- // parseTimeZone parses a time zone string and returns its length. Time zones
- // are human-generated and unpredictable. We can't do precise error checking.
- // On the other hand, for a correct parse there must be a time zone at the
- // beginning of the string, so it's almost always true that there's one
- // there. We look at the beginning of the string for a run of upper-case letters.
- // If there are more than 5, it's an error.
- // If there are 4 or 5 and the last is a T, it's a time zone.
- // If there are 3, it's a time zone.
- // Otherwise, other than special cases, it's not a time zone.
- // GMT is special because it can have an hour offset.
- func parseTimeZone(value string) (length int, ok bool) {
- if len(value) < 3 {
- return 0, false
- }
- // Special case 1: ChST and MeST are the only zones with a lower-case letter.
- if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
- return 4, true
- }
- // Special case 2: GMT may have an hour offset; treat it specially.
- if value[:3] == "GMT" {
- length = parseGMT(value)
- return length, true
- }
- // Special Case 3: Some time zones are not named, but have +/-00 format
- if value[0] == '+' || value[0] == '-' {
- length = parseSignedOffset(value)
- return length, true
- }
- // How many upper-case letters are there? Need at least three, at most five.
- var nUpper int
- for nUpper = 0; nUpper < 6; nUpper++ {
- if nUpper >= len(value) {
- break
- }
- if c := value[nUpper]; c < 'A' || 'Z' < c {
- break
- }
- }
- switch nUpper {
- case 0, 1, 2, 6:
- return 0, false
- case 5: // Must end in T to match.
- if value[4] == 'T' {
- return 5, true
- }
- case 4:
- // Must end in T, except one special case.
- if value[3] == 'T' || value[:4] == "WITA" {
- return 4, true
- }
- case 3:
- return 3, true
- }
- return 0, false
- }
- // parseGMT parses a GMT time zone. The input string is known to start "GMT".
- // The function checks whether that is followed by a sign and a number in the
- // range -14 through 12 excluding zero.
- func parseGMT(value string) int {
- value = value[3:]
- if len(value) == 0 {
- return 3
- }
- return 3 + parseSignedOffset(value)
- }
- // parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").
- // The function checks for a signed number in the range -14 through +12 excluding zero.
- // Returns length of the found offset string or 0 otherwise
- func parseSignedOffset(value string) int {
- sign := value[0]
- if sign != '-' && sign != '+' {
- return 0
- }
- x, rem, err := leadingInt(value[1:])
- if err != nil {
- return 0
- }
- if sign == '-' {
- x = -x
- }
- if x == 0 || x < -14 || 12 < x {
- return 0
- }
- return len(value) - len(rem)
- }
- func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
- if value[0] != '.' {
- err = errBad
- return
- }
- if ns, err = atoi(value[1:nbytes]); err != nil {
- return
- }
- if ns < 0 || 1e9 <= ns {
- rangeErrString = "fractional second"
- return
- }
- // We need nanoseconds, which means scaling by the number
- // of missing digits in the format, maximum length 10. If it's
- // longer than 10, we won't scale.
- scaleDigits := 10 - nbytes
- for i := 0; i < scaleDigits; i++ {
- ns *= 10
- }
- return
- }
- // std0x records the std values for "01", "02", ..., "06".
- var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
- // startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
- // Its purpose is to prevent matching strings like "Month" when looking for "Mon".
- func startsWithLowerCase(str string) bool {
- if len(str) == 0 {
- return false
- }
- c := str[0]
- return 'a' <= c && c <= 'z'
- }
|