js-on-sx: real Date construction + getters via Howard-Hinnant civil arithmetic
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
This commit is contained in:
@@ -1062,6 +1062,49 @@
|
||||
(js-new-call URIError (js-args (js-string-slice e 10 (len e)))))
|
||||
(else e))))
|
||||
|
||||
(define
|
||||
js-date-time-value
|
||||
(fn
|
||||
(d)
|
||||
(cond
|
||||
((or (not (dict? d)) (not (contains? (keys d) "__js_is_date__")))
|
||||
(raise (js-new-call TypeError (js-args "this is not a Date object"))))
|
||||
(else (get d "__date_value__")))))
|
||||
|
||||
(define
|
||||
js-date-getter
|
||||
(fn
|
||||
(d field)
|
||||
(let
|
||||
((ms-raw (js-date-time-value d)))
|
||||
(let
|
||||
((ms (if (= (type-of ms-raw) "rational") (exact->inexact ms-raw) ms-raw)))
|
||||
(cond
|
||||
((or (= ms nil) (js-undefined? ms) (not (number? ms)))
|
||||
(js-nan-value))
|
||||
((js-number-is-nan ms) (js-nan-value))
|
||||
(else
|
||||
(let
|
||||
((days (floor (/ ms 86400000)))
|
||||
(tod
|
||||
(let ((m (modulo (js-num-to-int ms) 86400000)))
|
||||
(if (< m 0) (+ m 86400000) m))))
|
||||
(cond
|
||||
((= field "ms") (modulo tod 1000))
|
||||
((= field "seconds") (js-math-trunc (/ (modulo tod 60000) 1000)))
|
||||
((= field "minutes") (js-math-trunc (/ (modulo tod 3600000) 60000)))
|
||||
((= field "hours") (js-math-trunc (/ tod 3600000)))
|
||||
((= field "day")
|
||||
(let ((dow (modulo (+ days 4) 7)))
|
||||
(if (< dow 0) (+ dow 7) dow)))
|
||||
(else
|
||||
(let ((ymd (js-date-days-to-ymd days)))
|
||||
(cond
|
||||
((= field "year") (nth ymd 0))
|
||||
((= field "month") (- (nth ymd 1) 1))
|
||||
((= field "date") (nth ymd 2))
|
||||
(else (js-nan-value)))))))))))))
|
||||
|
||||
(define
|
||||
js-date-from-one
|
||||
(fn
|
||||
@@ -1092,15 +1135,53 @@
|
||||
(fn
|
||||
(args)
|
||||
(let
|
||||
((year (js-num-to-int (js-to-number (nth args 0))))
|
||||
(month
|
||||
(if (>= (len args) 2) (js-num-to-int (js-to-number (nth args 1))) 0))
|
||||
(day
|
||||
(if (>= (len args) 3) (js-num-to-int (js-to-number (nth args 2))) 1)))
|
||||
(+
|
||||
(* (- year 1970) 31557600000)
|
||||
(* month 2629800000)
|
||||
(* (- day 1) 86400000)))))
|
||||
((year-raw (js-num-to-int (js-to-number (nth args 0)))))
|
||||
(let
|
||||
((year (if (and (>= year-raw 0) (<= year-raw 99)) (+ year-raw 1900) year-raw))
|
||||
(month
|
||||
(if (>= (len args) 2) (js-num-to-int (js-to-number (nth args 1))) 0))
|
||||
(day
|
||||
(if (>= (len args) 3) (js-num-to-int (js-to-number (nth args 2))) 1))
|
||||
(hour
|
||||
(if (>= (len args) 4) (js-num-to-int (js-to-number (nth args 3))) 0))
|
||||
(mins
|
||||
(if (>= (len args) 5) (js-num-to-int (js-to-number (nth args 4))) 0))
|
||||
(secs
|
||||
(if (>= (len args) 6) (js-num-to-int (js-to-number (nth args 5))) 0))
|
||||
(ms
|
||||
(if (>= (len args) 7) (js-num-to-int (js-to-number (nth args 6))) 0)))
|
||||
(let
|
||||
((days (js-date-civil-to-days year (+ month 1) day)))
|
||||
(+
|
||||
(* days 86400000)
|
||||
(* hour 3600000)
|
||||
(* mins 60000)
|
||||
(* secs 1000)
|
||||
ms))))))
|
||||
|
||||
(define
|
||||
js-date-civil-to-days
|
||||
(fn
|
||||
(y m d)
|
||||
(let
|
||||
((y2 (if (<= m 2) (- y 1) y)))
|
||||
(let
|
||||
((era (if (>= y2 0) (js-math-trunc (/ y2 400)) (js-math-trunc (/ (- y2 399) 400)))))
|
||||
(let
|
||||
((yoe (- y2 (* era 400))))
|
||||
(let
|
||||
((doy
|
||||
(+
|
||||
(js-math-trunc (/ (+ (* 153 (if (> m 2) (- m 3) (+ m 9))) 2) 5))
|
||||
(- d 1))))
|
||||
(let
|
||||
((doe
|
||||
(+
|
||||
(* yoe 365)
|
||||
(+
|
||||
(- (js-math-trunc (/ yoe 4)) (js-math-trunc (/ yoe 100)))
|
||||
doy))))
|
||||
(+ (* era 146097) (- doe 719468)))))))))
|
||||
|
||||
(define
|
||||
js-date-format-now
|
||||
@@ -1150,30 +1231,24 @@
|
||||
((= (len args) 0) 0)
|
||||
(else (js-date-from-parts args))))
|
||||
:prototype
|
||||
{:getTime (fn () (let ((t (js-this))) (get t "__date_value__")))
|
||||
:valueOf (fn () (let ((t (js-this))) (get t "__date_value__")))
|
||||
:getFullYear
|
||||
(fn ()
|
||||
(let ((t (js-this)))
|
||||
(+ 1970 (js-math-trunc (/ (get t "__date_value__") 31557600000)))))
|
||||
:getUTCFullYear
|
||||
(fn ()
|
||||
(let ((t (js-this)))
|
||||
(+ 1970 (js-math-trunc (/ (get t "__date_value__") 31557600000)))))
|
||||
:getMonth (fn () 0)
|
||||
:getUTCMonth (fn () 0)
|
||||
:getDate (fn () 1)
|
||||
:getUTCDate (fn () 1)
|
||||
:getDay (fn () 0)
|
||||
:getUTCDay (fn () 0)
|
||||
:getHours (fn () 0)
|
||||
:getUTCHours (fn () 0)
|
||||
:getMinutes (fn () 0)
|
||||
:getUTCMinutes (fn () 0)
|
||||
:getSeconds (fn () 0)
|
||||
:getUTCSeconds (fn () 0)
|
||||
:getMilliseconds (fn () 0)
|
||||
:getUTCMilliseconds (fn () 0)
|
||||
{:getTime (fn () (js-date-time-value (js-this)))
|
||||
:valueOf (fn () (js-date-time-value (js-this)))
|
||||
:getFullYear (fn () (js-date-getter (js-this) "year"))
|
||||
:getUTCFullYear (fn () (js-date-getter (js-this) "year"))
|
||||
:getMonth (fn () (js-date-getter (js-this) "month"))
|
||||
:getUTCMonth (fn () (js-date-getter (js-this) "month"))
|
||||
:getDate (fn () (js-date-getter (js-this) "date"))
|
||||
:getUTCDate (fn () (js-date-getter (js-this) "date"))
|
||||
:getDay (fn () (js-date-getter (js-this) "day"))
|
||||
:getUTCDay (fn () (js-date-getter (js-this) "day"))
|
||||
:getHours (fn () (js-date-getter (js-this) "hours"))
|
||||
:getUTCHours (fn () (js-date-getter (js-this) "hours"))
|
||||
:getMinutes (fn () (js-date-getter (js-this) "minutes"))
|
||||
:getUTCMinutes (fn () (js-date-getter (js-this) "minutes"))
|
||||
:getSeconds (fn () (js-date-getter (js-this) "seconds"))
|
||||
:getUTCSeconds (fn () (js-date-getter (js-this) "seconds"))
|
||||
:getMilliseconds (fn () (js-date-getter (js-this) "ms"))
|
||||
:getUTCMilliseconds (fn () (js-date-getter (js-this) "ms"))
|
||||
:getTimezoneOffset (fn () 0)
|
||||
:setTime
|
||||
(fn (v)
|
||||
@@ -6617,6 +6692,7 @@
|
||||
(dict-set! (get Map "prototype") "__proto__" (get Object "prototype"))
|
||||
(dict-set! (get Set "prototype") "__proto__" (get Object "prototype"))
|
||||
(dict-set! (get Date "prototype") "__proto__" (get Object "prototype"))
|
||||
(dict-set! (get Date "prototype") "constructor" Date)
|
||||
(dict-set! (get RegExp "prototype") "__proto__" (get Object "prototype"))
|
||||
(dict-set! (get RegExp "prototype") "constructor" RegExp)
|
||||
(dict-set! (get js-function-global "prototype") "__proto__" (get Object "prototype"))
|
||||
|
||||
@@ -158,6 +158,8 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green.
|
||||
|
||||
Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta.
|
||||
|
||||
- 2026-05-10 — **Real `Date` construction + getters via Howard-Hinnant civil-day arithmetic.** `js-date-from-parts` now computes a true ms-since-epoch from `(year, month, day, hour, min, sec, ms)` via `js-date-civil-to-days` (the inverse of last iteration's `days-to-ymd`), with the legacy 2-digit-year coercion (0..99 → 1900+y). `getFullYear/Month/Date/Day/Hours/Minutes/Seconds/Milliseconds` (UTC + non-UTC) all share a new `js-date-getter`: TypeErrors on non-Date this, returns NaN on invalid time, otherwise decomposes ms into y/m/d/h/m/s/ms/dow. Plus added `Date.prototype.constructor = Date` (was missing). Result: each of the 8 Date getter categories went 2/6 → 5/6 (+3 each, +24 total). Date toISOString 11/16 → 13/16. Some Date construction-loop tests now exceed the 15s per-test timeout — the new civil math is heavier than the old (year-1970)*ms-per-year approximation, but correctness wins. conformance.sh: 148/148.
|
||||
|
||||
- 2026-05-10 — **`Date.prototype.toISOString` produces real `YYYY-MM-DDTHH:mm:ss.sssZ` and validates input.** Old `js-date-iso` only computed the year and hardcoded the rest as `01-01T00:00:00.000Z`. Added: (1) TypeError when this isn't a Date (no `__js_is_date__` slot); (2) RangeError when ms is NaN, undefined, or |ms| > 8.64e15; (3) full date breakdown via Howard-Hinnant `days_to_civil` algorithm (`js-date-days-to-ymd`) → year/month/day, plus modular hours/min/sec/ms; (4) extended-year format `±YYYYYY` for years outside 0..9999. Result: built-ins/Date/prototype/toISOString 7/16 → 11/16 (+4). Date 21/30. conformance.sh: 148/148.
|
||||
|
||||
- 2026-05-10 — **`JSON.stringify` honours `replacer` (function + array forms), `space`, and `toJSON`.** Previous impl ignored the second/third arguments entirely and never called `toJSON`. Rewrote around a `js-json-serialize-property(key, holder, rep-fn, rep-keys, gap, indent)` core: walks `toJSON` first, then replacer-fn (with `holder` as `this`); arrays-as-replacer become a property-name allowlist; numeric `space` clamped to 0..10 spaces, string `space` truncated to 10 chars, non-empty gap activates indented output with `:` → `: ` separator. Number wrapper / String wrapper / Boolean wrapper unwrap before serialization; non-finite numbers serialize as `"null"`; functions serialize as `undefined`. Result: built-ins/JSON/stringify 6/30 → 14/30 (+8). conformance.sh: 148/148.
|
||||
|
||||
Reference in New Issue
Block a user