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  }