Format Temporal values in console.log, util.inspect, and test pretty-format
What
Temporal values now print as a single token, Temporal.<Class> <toString()>, in all three formatters:
Temporal.PlainTime 10:20:23
Temporal.Instant 1970-01-01T00:00:00Z
Temporal.ZonedDateTime 2020-01-01T00:00:00-10:00[Pacific/Honolulu]
Temporal.Duration PT1H30M
{ hi: Temporal.PlainTime 10:20:23 }
Before this change, Temporal cells were classified as generic objects everywhere:
console.log/Bun.inspectdumped prototype methods and slot getters as if they were own properties (33 lines for aPlainDate, 50 for aZonedDateTime)util.inspectprintedPlainDate [Temporal.PlainDate] {}, with the value invisible- the test runner's pretty-format printed
PlainDate {}, so every instance of a class produced the same snapshot text, and a failingtoEqualdiffed two identical sides
How
The text is the spec toString() with default options: lossless, round-trips through Temporal.X.from(), keeps [Zone]/[u-ca=] annotations. It is computed from internal slots (ClassInfo detection, internal toString implementations), so inspection never calls user-observable toString or Symbol.toStringTag.
src/jsc/bindings/bindings.cpp+ newsrc/jsc/bindings/Temporal.{h,cpp}: one shared native helper for all 8 types.Bun__JSValue__temporalObjectTypeclassifies a value (0 = not Temporal, 1-8 = type),Bun__Temporal__toDisplayString/Bun::temporalDisplayStringproduce the text. Seven types use the classes' internaltoString();ZonedDateTimereplicates the spec recipe (temporalZonedDateTimeToStringis file-static in JSC) fromgetLocalDateTime, the minute-rounded offset, the zone id, and the calendar annotation. Two host functions expose the helper to internal JS.src/jsc/ConsoleObject.rs: aTemporaltag next to theJSDate -> JSONpath. Atomic at any depth, own properties and subclass names ignored (the existing Date/Map behavior), label uncolored, value in Date's magenta.src/runtime/test_runner/pretty_format.rs: same tag and the same text, so expect diffs and snapshots show the value. The format arm is hoisted to#[inline(never)]like the console arms so deep recursion frames stay small.src/js/internal/util/inspect.js: follows nodejs/node#63154 (the open Node PR for the same feature):getPrefixhandling for subclasses and null prototypes,'date'style, extra own properties appended. One adaptation: JSC names the constructorsPlainDatewhere V8 usesTemporal.PlainDate, so direct instances map onto the full label to produce the same output as Node (Temporal.PlainDate 2020-01-02, subclassX [Temporal.PlainDate] 2020-01-02).
Temporal.Now, the prototypes, and Intl objects are unaffected (the classifier rejects them).
Verification
test/js/bun/util/inspect.test.js: all 8 types, annotations, nesting, depth, own-property/subclass behavior, colors,console.logvia spawntest/js/node/util/node-inspect-tests/parallel/util-inspect-temporal.test.js: port of the test from nodejs/node#63154; all 48 cases produce byte-identical output to Node's expectationstest/js/bun/bun-object/deep-equals-temporal.test.ts:toEqualfailure messages now showExpected: Temporal.PlainDate 1999-12-31/Received: Temporal.PlainDate 2020-01-02test/js/bun/test/snapshot-tests/bun-snapshots.test.ts: snapshot serialization for all 8 types (committed.snap)
All new tests fail on the released bun and pass with this change. Also verified against the engine itself: for historic sub-minute offsets (Africa/Monrovia 1950), extreme years, and non-ISO calendars, the inspected text is byte-identical to the real toString(). Existing suites (inspect.test.js, expect.test.js 417 tests, console, snapshot, util, deep-equals-temporal) pass.
Note for #37018
#37018 (TOML Temporal) adds the same Bun__JSValue__temporalObjectType classifier (same name, same discriminants, same location in bindings.cpp). Whichever lands second will hit a small merge conflict there; resolve by keeping one copy of the classifier. The TOML-specific formatter in that PR stays separate since TOML needs annotation-free output.
no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/bun/util/inspect.test.js