mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-11-14 08:17:26 +01:00
318 lines
7.6 KiB
Go
318 lines
7.6 KiB
Go
// Copyright 2011 the V8 project authors. All rights reserved.
|
|
// Adapted date parsing rules; BSD-3-Clause terms in ../../licenses/V8-LICENSE.
|
|
|
|
package server
|
|
|
|
// The parsing rules follow V8's dateparser (BSD-3-Clause); see
|
|
// https://github.com/v8/v8/tree/main/src/date. Runtime parsing remains in Go.
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
)
|
|
|
|
type dateToken struct {
|
|
kind byte
|
|
text string
|
|
n int
|
|
}
|
|
|
|
func dateSpace(r rune) bool { return unicode.IsSpace(r) && r != '\u0085' || r == '\ufeff' }
|
|
func dateTokens(value string) []dateToken {
|
|
runes := []rune(value)
|
|
out := []dateToken{}
|
|
for i := 0; i < len(runes) && runes[i] != 0; {
|
|
start := i
|
|
r := runes[i]
|
|
i++
|
|
switch {
|
|
case r >= '0' && r <= '9':
|
|
for i < len(runes) && runes[i] >= '0' && runes[i] <= '9' {
|
|
i++
|
|
}
|
|
text := string(runes[start:i])
|
|
significant := strings.TrimLeft(text, "0")
|
|
if len(significant) > 9 {
|
|
significant = significant[:9]
|
|
}
|
|
n, _ := strconv.Atoi(significant)
|
|
out = append(out, dateToken{'n', text, n})
|
|
case strings.ContainsRune(":-+.)", r):
|
|
out = append(out, dateToken{byte(r), string(r), 0})
|
|
case dateSpace(r):
|
|
out = append(out, dateToken{'s', "", 0})
|
|
case r >= 'A':
|
|
for i < len(runes) && runes[i] >= 'A' && !dateSpace(runes[i]) {
|
|
i++
|
|
}
|
|
out = append(out, dateToken{'w', strings.ToLower(string(runes[start:i])), 0})
|
|
case r == '(':
|
|
depth := 1
|
|
for i < len(runes) && runes[i] != 0 && depth > 0 {
|
|
if runes[i] == '(' {
|
|
depth++
|
|
}
|
|
if runes[i] == ')' {
|
|
depth--
|
|
}
|
|
i++
|
|
}
|
|
out = append(out, dateToken{'?', "", 0})
|
|
default:
|
|
out = append(out, dateToken{'?', "", 0})
|
|
}
|
|
}
|
|
return append(out, dateToken{})
|
|
}
|
|
|
|
func javascriptLegacyDate(value string) (time.Time, bool) {
|
|
tokens := dateTokens(value)
|
|
pos := 0
|
|
peek := func() dateToken { return tokens[pos] }
|
|
next := func() dateToken {
|
|
t := tokens[pos]
|
|
if t.kind != 0 {
|
|
pos++
|
|
}
|
|
return t
|
|
}
|
|
skip := func(kind byte) bool {
|
|
if tokens[pos].kind == kind {
|
|
pos++
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
dates := []int{}
|
|
// Retain the consumed ISO prefix when legacy parsing takes over. Once an
|
|
// ISO time separator is reached, invalid time syntax cannot fall back.
|
|
token := dateToken{}
|
|
if peek().kind == '+' || peek().kind == '-' {
|
|
sign := next()
|
|
token = sign
|
|
if peek().kind == 'n' && len(peek().text) == 6 {
|
|
year := next().n
|
|
if sign.kind != '-' || year != 0 {
|
|
if sign.kind == '-' {
|
|
year = -year
|
|
}
|
|
dates = append(dates, year)
|
|
token = dateToken{kind: 'i'}
|
|
}
|
|
}
|
|
} else if peek().kind == 'n' && len(peek().text) == 4 {
|
|
dates = append(dates, next().n)
|
|
token.kind = 'i'
|
|
} else {
|
|
token = next()
|
|
}
|
|
if token.kind == 'i' {
|
|
validPrefix := true
|
|
if skip('-') {
|
|
t := next()
|
|
if t.kind != 'n' || len(t.text) != 2 || t.n < 1 || t.n > 12 {
|
|
token = t
|
|
validPrefix = false
|
|
} else {
|
|
dates = append(dates, t.n)
|
|
if skip('-') {
|
|
t = next()
|
|
if t.kind != 'n' || len(t.text) != 2 || t.n < 1 || t.n > 31 {
|
|
token = t
|
|
validPrefix = false
|
|
} else {
|
|
dates = append(dates, t.n)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if validPrefix {
|
|
if peek().kind == 'w' && peek().text == "t" {
|
|
return time.Time{}, false
|
|
}
|
|
token = next()
|
|
}
|
|
}
|
|
clock := []int{}
|
|
month := 0
|
|
ampm := -1
|
|
tzHour, tzMinute, tzSign := -1, -1, 1
|
|
readNumber := len(dates) > 0
|
|
timeExpect := func(n int) bool { return (len(clock) == 1 || len(clock) == 2) && n < 60 || len(clock) == 3 && n < 1000 }
|
|
addTime := func(n int) bool {
|
|
if len(clock) >= 4 {
|
|
return false
|
|
}
|
|
clock = append(clock, n)
|
|
return true
|
|
}
|
|
finishTime := func(n int) bool {
|
|
if !addTime(n) {
|
|
return false
|
|
}
|
|
for len(clock) < 4 {
|
|
clock = append(clock, 0)
|
|
}
|
|
return true
|
|
}
|
|
months := []string{"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"}
|
|
zones := map[string]int{"ut": 0, "utc": 0, "z": 0, "gmt": 0, "cdt": -5, "cst": -6, "edt": -4, "est": -5, "mdt": -6, "mst": -7, "pdt": -7, "pst": -8}
|
|
for ; token.kind != 0; token = next() {
|
|
switch token.kind {
|
|
case 'n':
|
|
readNumber = true
|
|
n := token.n
|
|
if skip(':') {
|
|
if skip(':') {
|
|
if len(clock) != 0 {
|
|
return time.Time{}, false
|
|
}
|
|
clock = append(clock, n, 0)
|
|
} else {
|
|
if !addTime(n) {
|
|
return time.Time{}, false
|
|
}
|
|
skip('.')
|
|
}
|
|
} else if skip('.') && timeExpect(n) {
|
|
if !addTime(n) || peek().kind != 'n' {
|
|
return time.Time{}, false
|
|
}
|
|
fraction := next().text + "000"
|
|
ms, _ := strconv.Atoi(fraction[:3])
|
|
if !finishTime(ms) {
|
|
return time.Time{}, false
|
|
}
|
|
} else if tzHour >= 0 && tzMinute < 0 && n < 60 {
|
|
tzMinute = n
|
|
} else if timeExpect(n) {
|
|
if !finishTime(n) {
|
|
return time.Time{}, false
|
|
}
|
|
t := peek()
|
|
if t.kind != 0 && t.kind != 's' && t.kind != '+' && t.kind != '-' && !(t.kind == 'w' && t.text == "z") {
|
|
return time.Time{}, false
|
|
}
|
|
} else {
|
|
if len(dates) >= 3 {
|
|
return time.Time{}, false
|
|
}
|
|
dates = append(dates, n)
|
|
skip('-')
|
|
}
|
|
case 'w':
|
|
named := 0
|
|
if len(token.text) >= 3 {
|
|
for i, name := range months {
|
|
if token.text[:3] == name {
|
|
named = i + 1
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if (token.text == "am" || token.text == "pm") && len(clock) > 0 {
|
|
ampm = 0
|
|
if token.text == "pm" {
|
|
ampm = 12
|
|
}
|
|
} else if named > 0 {
|
|
month = named
|
|
skip('-')
|
|
} else if zone, ok := zones[token.text]; ok && readNumber {
|
|
tzHour = zone
|
|
tzSign = 1
|
|
if zone < 0 {
|
|
tzSign = -1
|
|
tzHour = -zone
|
|
}
|
|
tzMinute = 0
|
|
} else if readNumber || peek().kind == 'n' {
|
|
return time.Time{}, false
|
|
}
|
|
case '+', '-':
|
|
if tzHour == 0 && tzMinute == 0 || len(clock) > 0 {
|
|
tzSign = 1
|
|
if token.kind == '-' {
|
|
tzSign = -1
|
|
}
|
|
n, width := 0, 0
|
|
if peek().kind == 'n' {
|
|
t := next()
|
|
n, width = t.n, len(t.text)
|
|
}
|
|
readNumber = true
|
|
if peek().kind == ':' {
|
|
tzHour, tzMinute = n, -1
|
|
} else if width == 1 || width == 2 {
|
|
tzHour, tzMinute = n, 0
|
|
} else if width == 3 || width == 4 {
|
|
tzHour, tzMinute = n/100, n%100
|
|
} else {
|
|
return time.Time{}, false
|
|
}
|
|
} else if readNumber {
|
|
return time.Time{}, false
|
|
}
|
|
case ')':
|
|
if readNumber {
|
|
return time.Time{}, false
|
|
}
|
|
}
|
|
}
|
|
if len(dates) == 0 {
|
|
return time.Time{}, false
|
|
}
|
|
for len(dates) < 3 {
|
|
dates = append(dates, 1)
|
|
}
|
|
year, day := 0, 0
|
|
if month == 0 {
|
|
if dates[0] < 1 || dates[0] > 31 {
|
|
year, month, day = dates[0], dates[1], dates[2]
|
|
} else {
|
|
month, day, year = dates[0], dates[1], dates[2]
|
|
}
|
|
} else if dates[0] < 1 || dates[0] > 31 {
|
|
year, day = dates[0], dates[1]
|
|
} else {
|
|
day, year = dates[0], dates[1]
|
|
}
|
|
if year >= 0 && year <= 49 {
|
|
year += 2000
|
|
} else if year >= 50 && year <= 99 {
|
|
year += 1900
|
|
}
|
|
if year < -300000 || year > 300000 || month < 1 || month > 12 || day < 1 || day > 31 {
|
|
return time.Time{}, false
|
|
}
|
|
for len(clock) < 4 {
|
|
clock = append(clock, 0)
|
|
}
|
|
if ampm >= 0 {
|
|
if clock[0] > 12 {
|
|
return time.Time{}, false
|
|
}
|
|
clock[0] = clock[0]%12 + ampm
|
|
}
|
|
if clock[0] > 24 || clock[1] > 59 || clock[2] > 59 || clock[3] > 999 || clock[0] == 24 && (clock[1] != 0 || clock[2] != 0 || clock[3] != 0) {
|
|
return time.Time{}, false
|
|
}
|
|
offset := 0
|
|
if tzHour >= 0 {
|
|
if tzMinute < 0 {
|
|
tzMinute = 0
|
|
}
|
|
offset = (tzHour*60 + tzMinute) * 60 * tzSign
|
|
}
|
|
if math.Abs(float64(offset)) > math.MaxInt32 {
|
|
return time.Time{}, false
|
|
}
|
|
parsed := time.Date(year, time.Month(month), day, clock[0], clock[1], clock[2], clock[3]*int(time.Millisecond), time.UTC).Add(-time.Duration(offset) * time.Second)
|
|
if math.Abs(float64(parsed.UnixMilli())) > 8.64e15 {
|
|
return time.Time{}, false
|
|
}
|
|
return parsed, true
|
|
}
|