Skip to content

[go1.20] Added temporary generics override for time formatting #1280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions compiler/natives/src/time/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//go:build js
// +build js

package time

// copied and replaced for go1.20 temporarily without generics.
func atoi(sAny any) (x int, err error) {
s := asBytes(sAny)
neg := false
if len(s) > 0 && (s[0] == '-' || s[0] == '+') {
neg = s[0] == '-'
s = s[1:]
}
q, remStr, err := leadingInt(s)
rem := []byte(remStr)
x = int(q)
if err != nil || len(rem) > 0 {
return 0, atoiError
}
if neg {
x = -x
}
return x, nil
}

// copied and replaced for go1.20 temporarily without generics.
func isDigit(sAny any, i int) bool {
s := asBytes(sAny)
if len(s) <= i {
return false
}
c := s[i]
return '0' <= c && c <= '9'
}

// copied and replaced for go1.20 temporarily without generics.
func parseNanoseconds(sAny any, nbytes int) (ns int, rangeErrString string, err error) {
value := asBytes(sAny)
if !commaOrPeriod(value[0]) {
err = errBad
return
}
if nbytes > 10 {
value = value[:10]
nbytes = 10
}
if ns, err = atoi(value[1:nbytes]); err != nil {
return
}
if ns < 0 {
rangeErrString = "fractional second"
return
}
scaleDigits := 10 - nbytes
for i := 0; i < scaleDigits; i++ {
ns *= 10
}
return
}

// copied and replaced for go1.20 temporarily without generics.
func leadingInt(sAny any) (x uint64, rem string, err error) {
s := asBytes(sAny)
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x > 1<<63/10 {
return 0, rem, errLeadingInt
}
x = x*10 + uint64(c) - '0'
if x > 1<<63 {
return 0, rem, errLeadingInt
}
}
return x, string(s[i:]), nil
}
85 changes: 85 additions & 0 deletions compiler/natives/src/time/format_rfc3339.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//go:build js
// +build js

package time

import "errors"

// added for go1.20 temporarily without generics.
func asBytes(s any) []byte {
switch t := s.(type) {
case []byte:
return t
case string:
return []byte(t)
default:
panic(errors.New(`unexpected type passed to asBytes, expected string or []bytes`))
}
}

// copied and replaced for go1.20 temporarily without generics.
func parseRFC3339(sAny any, local *Location) (Time, bool) {
s := asBytes(sAny)
ok := true
parseUint := func(s []byte, min, max int) (x int) {
for _, c := range s {
if c < '0' || '9' < c {
ok = false
return min
}
x = x*10 + int(c) - '0'
}
if x < min || max < x {
ok = false
return min
}
return x
}

if len(s) < len("2006-01-02T15:04:05") {
return Time{}, false
}
year := parseUint(s[0:4], 0, 9999)
month := parseUint(s[5:7], 1, 12)
day := parseUint(s[8:10], 1, daysIn(Month(month), year))
hour := parseUint(s[11:13], 0, 23)
min := parseUint(s[14:16], 0, 59)
sec := parseUint(s[17:19], 0, 59)
if !ok || !(s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':') {
return Time{}, false
}
s = s[19:]

var nsec int
if len(s) >= 2 && s[0] == '.' && isDigit(s, 1) {
n := 2
for ; n < len(s) && isDigit(s, n); n++ {
}
nsec, _, _ = parseNanoseconds(s, n)
s = s[n:]
}

t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
if len(s) != 1 || s[0] != 'Z' {
if len(s) != len("-07:00") {
return Time{}, false
}
hr := parseUint(s[1:3], 0, 23)
mm := parseUint(s[4:6], 0, 59)
if !ok || !((s[0] == '-' || s[0] == '+') && s[3] == ':') {
return Time{}, false
}
zoneOffset := (hr*60 + mm) * 60
if s[0] == '-' {
zoneOffset *= -1
}
t.addSec(-int64(zoneOffset))

if _, offset, _, _, _ := local.lookup(t.unixSec()); offset == zoneOffset {
t.setLoc(local)
} else {
t.setLoc(FixedZone("", zoneOffset))
}
}
return t, true
}