1 package goja
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import (
21 "errors"
22 "time"
23 )
24
25 const (
26 _ = iota
27 stdLongMonth = iota + stdNeedDate
28 stdMonth
29 stdNumMonth
30 stdZeroMonth
31 stdLongWeekDay
32 stdWeekDay
33 stdDay
34 stdUnderDay
35 stdZeroDay
36 stdHour = iota + stdNeedClock
37 stdHour12
38 stdZeroHour12
39 stdMinute
40 stdZeroMinute
41 stdSecond
42 stdZeroSecond
43 stdLongYear = iota + stdNeedDate
44 stdYear
45 stdPM = iota + stdNeedClock
46 stdpm
47 stdTZ = iota
48 stdBracketTZ
49 stdISO8601TZ
50 stdISO8601SecondsTZ
51 stdISO8601ShortTZ
52 stdISO8601ColonTZ
53 stdISO8601ColonSecondsTZ
54 stdNumTZ
55 stdNumSecondsTz
56 stdNumShortTZ
57 stdNumColonTZ
58 stdNumColonSecondsTZ
59 stdFracSecond0
60 stdFracSecond9
61
62 stdNeedDate = 1 << 8
63 stdNeedClock = 2 << 8
64 stdArgShift = 16
65 stdMask = 1<<stdArgShift - 1
66 )
67
68 var errBad = errors.New("bad value for field")
69
70 func parseDate(layout, value string, defaultLocation *time.Location) (time.Time, error) {
71 alayout, avalue := layout, value
72 rangeErrString := ""
73 amSet := false
74 pmSet := false
75
76
77 var (
78 year int
79 month int = 1
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
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 {
116 year += 1900
117 } else {
118 year += 2000
119 }
120 case stdLongYear:
121 if len(value) >= 7 && (value[0] == '-' || value[0] == '+') {
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
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
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
190
191 if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
192 _, std, _ = nextStdChunk(layout)
193 std &= stdMask
194 if std == stdFracSecond0 || std == stdFracSecond9 {
195
196 break
197 }
198
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
294 switch sign[0] {
295 case '+':
296 case '-':
297 zoneOffset = -zoneOffset
298 default:
299 err = errBad
300 }
301 case stdTZ:
302
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
333
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
345 break
346 }
347
348
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
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]*")
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
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
424 return 0, "", errLeadingInt
425 }
426 x = x*10 + int64(c) - '0'
427 if x < 0 {
428
429 return 0, "", errLeadingInt
430 }
431 }
432 return x, s[i:], nil
433 }
434
435
436
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':
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':
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':
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':
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':
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 '_':
483 if len(layout) >= i+2 && layout[i+1] == '2' {
484
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':
501 if len(layout) >= i+2 && layout[i+1] == 'M' {
502 return layout[0:i], stdPM, layout[i+2:]
503 }
504
505 case 'p':
506 if len(layout) >= i+2 && layout[i+1] == 'm' {
507 return layout[0:i], stdpm, layout[i+2:]
508 }
509
510 case '-':
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':
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 '.':
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
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
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
630
631
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
653
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
674 var atoiError = errors.New("time: invalid number")
675
676
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
687
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
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
714
715
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
744
745
746
747
748
749
750
751
752
753 func parseTimeZone(value string) (length int, ok bool) {
754 if len(value) < 3 {
755 return 0, false
756 }
757
758 if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
759 return 4, true
760 }
761
762 if value[:3] == "GMT" {
763 length = parseGMT(value)
764 return length, true
765 }
766
767 if value[0] == '+' || value[0] == '-' {
768 length = parseSignedOffset(value)
769 return length, true
770 }
771
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:
785 if value[4] == 'T' {
786 return 5, true
787 }
788 case 4:
789
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
800
801
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
812
813
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
845
846
847 scaleDigits := 10 - nbytes
848 for i := 0; i < scaleDigits; i++ {
849 ns *= 10
850 }
851 return
852 }
853
854
855 var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
856
857
858
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 }
866
View as plain text