rum-goggles/v1/vendor/github.com/robertkrimen/otto/builtin_date.go
2024-04-04 10:46:14 -04:00

620 lines
15 KiB
Go

package otto
import (
"math"
"time"
)
// Date
const (
// TODO Be like V8?
// builtinDateDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)".
builtinDateDateTimeLayout = time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"
builtinDateDateLayout = "Mon, 02 Jan 2006"
builtinDateTimeLayout = "15:04:05 MST"
)
// utcTimeZone is the time zone used for UTC calculations.
// It is GMT not UTC as that's what Javascript does because toUTCString is
// actually an alias to toGMTString.
var utcTimeZone = time.FixedZone("GMT", 0)
func builtinDate(call FunctionCall) Value {
date := &dateObject{}
date.Set(newDateTime([]Value{}, time.Local)) //nolint: gosmopolitan
return stringValue(date.Time().Format(builtinDateDateTimeLayout))
}
func builtinNewDate(obj *object, argumentList []Value) Value {
return objectValue(obj.runtime.newDate(newDateTime(argumentList, time.Local))) //nolint: gosmopolitan
}
func builtinDateToString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Local().Format(builtinDateDateTimeLayout)) //nolint: gosmopolitan
}
func builtinDateToDateString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Local().Format(builtinDateDateLayout)) //nolint: gosmopolitan
}
func builtinDateToTimeString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Local().Format(builtinDateTimeLayout)) //nolint: gosmopolitan
}
func builtinDateToUTCString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().In(utcTimeZone).Format(builtinDateDateTimeLayout))
}
func builtinDateToISOString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Format("2006-01-02T15:04:05.000Z"))
}
func builtinDateToJSON(call FunctionCall) Value {
obj := call.thisObject()
value := obj.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
// FIXME fv.isFinite
if fv := value.float64(); math.IsNaN(fv) || math.IsInf(fv, 0) {
return nullValue
}
toISOString := obj.get("toISOString")
if !toISOString.isCallable() {
// FIXME
panic(call.runtime.panicTypeError("Date.toJSON toISOString %q is not callable", toISOString))
}
return toISOString.call(call.runtime, objectValue(obj), []Value{})
}
func builtinDateToGMTString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
}
func builtinDateGetTime(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
// We do this (convert away from a float) so the user
// does not get something back in exponential notation
return int64Value(date.Epoch())
}
func builtinDateSetTime(call FunctionCall) Value {
obj := call.thisObject()
date := dateObjectOf(call.runtime, call.thisObject())
date.Set(call.Argument(0).float64())
obj.value = date
return date.Value()
}
func builtinDateBeforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*object, *dateObject, *ecmaTime, []int) {
obj := call.thisObject()
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return nil, nil, nil, nil
}
if argumentLimit > len(call.ArgumentList) {
argumentLimit = len(call.ArgumentList)
}
if argumentLimit == 0 {
obj.value = invalidDateObject
return nil, nil, nil, nil
}
valueList := make([]int, argumentLimit)
for index := 0; index < argumentLimit; index++ {
value := call.ArgumentList[index]
nm := value.number()
switch nm.kind {
case numberInteger, numberFloat:
default:
obj.value = invalidDateObject
return nil, nil, nil, nil
}
valueList[index] = int(nm.int64)
}
baseTime := date.Time()
if timeLocal {
baseTime = baseTime.Local() //nolint: gosmopolitan
}
ecmaTime := newEcmaTime(baseTime)
return obj, &date, &ecmaTime, valueList
}
func builtinDateParse(call FunctionCall) Value {
date := call.Argument(0).string()
return float64Value(dateParse(date))
}
func builtinDateUTC(call FunctionCall) Value {
return float64Value(newDateTime(call.ArgumentList, time.UTC))
}
func builtinDateNow(call FunctionCall) Value {
call.ArgumentList = []Value(nil)
return builtinDateUTC(call)
}
// This is a placeholder.
func builtinDateToLocaleString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Local().Format("2006-01-02 15:04:05")) //nolint: gosmopolitan
}
// This is a placeholder.
func builtinDateToLocaleDateString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Local().Format("2006-01-02")) //nolint: gosmopolitan
}
// This is a placeholder.
func builtinDateToLocaleTimeString(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return stringValue("Invalid Date")
}
return stringValue(date.Time().Local().Format("15:04:05")) //nolint: gosmopolitan
}
func builtinDateValueOf(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return date.Value()
}
func builtinDateGetYear(call FunctionCall) Value {
// Will throw a TypeError is ThisObject is nil or
// does not have Class of "Date"
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Year() - 1900) //nolint: gosmopolitan
}
func builtinDateGetFullYear(call FunctionCall) Value {
// Will throw a TypeError is ThisObject is nil or
// does not have Class of "Date"
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Year()) //nolint: gosmopolitan
}
func builtinDateGetUTCFullYear(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Year())
}
func builtinDateGetMonth(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(dateFromGoMonth(date.Time().Local().Month())) //nolint: gosmopolitan
}
func builtinDateGetUTCMonth(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(dateFromGoMonth(date.Time().Month()))
}
func builtinDateGetDate(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Day()) //nolint: gosmopolitan
}
func builtinDateGetUTCDate(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Day())
}
func builtinDateGetDay(call FunctionCall) Value {
// Actually day of the week
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(dateFromGoDay(date.Time().Local().Weekday())) //nolint: gosmopolitan
}
func builtinDateGetUTCDay(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(dateFromGoDay(date.Time().Weekday()))
}
func builtinDateGetHours(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Hour()) //nolint: gosmopolitan
}
func builtinDateGetUTCHours(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Hour())
}
func builtinDateGetMinutes(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Minute()) //nolint: gosmopolitan
}
func builtinDateGetUTCMinutes(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Minute())
}
func builtinDateGetSeconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Second()) //nolint: gosmopolitan
}
func builtinDateGetUTCSeconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Second())
}
func builtinDateGetMilliseconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Local().Nanosecond() / (100 * 100 * 100)) //nolint: gosmopolitan
}
func builtinDateGetUTCMilliseconds(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
return intValue(date.Time().Nanosecond() / (100 * 100 * 100))
}
func builtinDateGetTimezoneOffset(call FunctionCall) Value {
date := dateObjectOf(call.runtime, call.thisObject())
if date.isNaN {
return NaNValue()
}
timeLocal := date.Time().Local() //nolint: gosmopolitan
// Is this kosher?
timeLocalAsUTC := time.Date(
timeLocal.Year(),
timeLocal.Month(),
timeLocal.Day(),
timeLocal.Hour(),
timeLocal.Minute(),
timeLocal.Second(),
timeLocal.Nanosecond(),
time.UTC,
)
return float64Value(date.Time().Sub(timeLocalAsUTC).Seconds() / 60)
}
func builtinDateSetMilliseconds(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, true)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.millisecond = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCMilliseconds(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, false)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.millisecond = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetSeconds(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.millisecond = value[1]
}
ecmaTime.second = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCSeconds(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.millisecond = value[1]
}
ecmaTime.second = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetMinutes(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.millisecond = value[2]
ecmaTime.second = value[1]
} else if len(value) > 1 {
ecmaTime.second = value[1]
}
ecmaTime.minute = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCMinutes(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.millisecond = value[2]
ecmaTime.second = value[1]
} else if len(value) > 1 {
ecmaTime.second = value[1]
}
ecmaTime.minute = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetHours(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 4, true)
if ecmaTime == nil {
return NaNValue()
}
switch {
case len(value) > 3:
ecmaTime.millisecond = value[3]
fallthrough
case len(value) > 2:
ecmaTime.second = value[2]
fallthrough
case len(value) > 1:
ecmaTime.minute = value[1]
}
ecmaTime.hour = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCHours(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 4, false)
if ecmaTime == nil {
return NaNValue()
}
switch {
case len(value) > 3:
ecmaTime.millisecond = value[3]
fallthrough
case len(value) > 2:
ecmaTime.second = value[2]
fallthrough
case len(value) > 1:
ecmaTime.minute = value[1]
}
ecmaTime.hour = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetDate(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, true)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.day = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCDate(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, false)
if ecmaTime == nil {
return NaNValue()
}
ecmaTime.day = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetMonth(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.day = value[1]
}
ecmaTime.month = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCMonth(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 2, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 1 {
ecmaTime.day = value[1]
}
ecmaTime.month = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetYear(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 1, true)
if ecmaTime == nil {
return NaNValue()
}
year := value[0]
if 0 <= year && year <= 99 {
year += 1900
}
ecmaTime.year = year
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetFullYear(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, true)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.day = value[2]
ecmaTime.month = value[1]
} else if len(value) > 1 {
ecmaTime.month = value[1]
}
ecmaTime.year = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
func builtinDateSetUTCFullYear(call FunctionCall) Value {
obj, date, ecmaTime, value := builtinDateBeforeSet(call, 3, false)
if ecmaTime == nil {
return NaNValue()
}
if len(value) > 2 {
ecmaTime.day = value[2]
ecmaTime.month = value[1]
} else if len(value) > 1 {
ecmaTime.month = value[1]
}
ecmaTime.year = value[0]
date.SetTime(ecmaTime.goTime())
obj.value = *date
return date.Value()
}
// toUTCString
// toISOString
// toJSONString
// toJSON