1package 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
20import (
21	"errors"
22	"time"
23)
24
25const (
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
68var errBad = errors.New("bad value for field") // placeholder not passed to user
69
70func 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
395var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
396
397func 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.
415func 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.
437func 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
570var longDayNames = []string{
571	"Sunday",
572	"Monday",
573	"Tuesday",
574	"Wednesday",
575	"Thursday",
576	"Friday",
577	"Saturday",
578}
579
580var shortDayNames = []string{
581	"Sun",
582	"Mon",
583	"Tue",
584	"Wed",
585	"Thu",
586	"Fri",
587	"Sat",
588}
589
590var 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
605var 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.
621func 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.
632func 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
645func 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.
654func 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.
674var atoiError = errors.New("time: invalid number")
675
676// Duplicates functionality in strconv, but avoids dependency.
677func 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.
688func 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
704func 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).
716var 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
732func isLeap(year int) bool {
733	return year%4 == 0 && (year%100 != 0 || year%400 == 0)
734}
735
736func 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.
753func 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.
802func 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
814func 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
832func 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".
855var 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".
859func startsWithLowerCase(str string) bool {
860	if len(str) == 0 {
861		return false
862	}
863	c := str[0]
864	return 'a' <= c && c <= 'z'
865}
866