Repository navigation
feat(wasm): java.time formatting, ofPattern and region zones on Wasm components - #48
Draft
sideeffffect wants to merge 6 commits into
Draft
sideeffffect wants to merge 6 commits into
sideeffffect wants to merge 6 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dapr4s-wasm now depends on scala-java-time 2.8.0 (default zone UTC without a JS host) and scala-java-locales 1.6.0 (Locale.getDefault no longer reads `window` without a JS host, cquiroz/scala-java-locales#640). Both are on Central, so no source build. counter gets a `java-time` route (parse/toString, ISO and RFC formatters, localized day name, default zone, UTC/offset zones, Duration); e2e.sh and the consumer check assert its answer under daprd. Still blocked, documented in AGENTS.md with how each retires: the wall-clock (scala-wasm javalib js.Date.now; also reached by every DateTimeFormatter.ofPattern via TimeZone) and tzdb region zones (@EnableReflectiveInstantiation -> Reflect js.Array). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…onents Both need an unreleased scala-java-time. scripts/wasm/build-scala-java-time.sh builds it from source (main + cquiroz/scala-java-time#618, tzdb compiled with a Scala 3 nightly) and publishLocal's it as 2.8.1-dapr4s-local; CI runs it with a JDK 17, cached by the script's hash. - ofPattern: any pattern kept ZoneTextPrinterParser reachable, whose TimeZone.getTimeZone computed the raw offset at Instant.now (the wall clock, absent without a JS host). #618 computes it lazily. - Region zones: the tzdb provider's @EnableReflectiveInstantiation registration is a static initializer (always linked). Scala 3 <= 3.10.0 emits it with the JS-backed Reflect.registerInstantiatableClass; scala/scala3@3bb676972 (3.10.1) emits the Scala-typed V2. Only counter and the consumer check use the source build; dapr4s-wasm's POM stays on scala-java-time 2.8.0 (Central must have it). Counter's java-time route now converts to Europe/Prague and New York and formats/parses with ofPattern; e2e asserts the exact answer. The wall clock stays blocked (scala-wasm javalib). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…failed invokes counter now carries the tz database (~4 MB). On the CI runner the first invocation after "up and running" failed, and set -e ended the script without a message. Warm up until an invocation succeeds, reset the counter, and let every failed invoke reach fail() with dapr's answer and the logs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cquiroz/scala-java-time#618 is merged (48cf7a6). The script now builds that main commit, without a merge step. It waits for a scala-java-time release > 2.8.0; region zones also need that release built with Scala 3 >= 3.10.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #44 (base
feat/wasm-component).What
Shared code on the Wasm-component path can now use java.time formatting and parsing. With an unreleased scala-java-time (built from source), also
DateTimeFormatter.ofPatternand region zones.dapr4s-wasm(wallWasm) depends on scala-java-time 2.8.0 (version from the rootbuild.sbt) and scala-java-locales 1.6.0. Both are in its POM, so every app gets them, in source and release mode.scripts/wasm/build-scala-java-time.shbuilds scala-java-time from source and publishLocal's it as2.8.1-dapr4s-local(scalaJavaTimeWasminwasm/build.sbt). It is cquiroz/scala-java-timemainat 48cf7a6, the merge commit of Compute the raw offset of TimeZone.getTimeZone lazily cquiroz/scala-java-time#618 (merged 2026-10-17; no merge step in the script), with the tzdb module compiled by a Scala 3 nightly. CI runs it with a JDK 17 and caches~/.ivy2/local/io.github.cquirozby the script's hash.counterand the consumer check depend onscala-java-time+scala-java-time-tzdb2.8.1-dapr4s-local. Thedapr4s-wasmPOM stays on 2.8.0, because Central must have every version it names (wasm publishing is ON).test/wasm/counterhas ajava-timeroute.scripts/wasm/e2e.shasserts its exact answer under daprd (source mode). The consumer check asserts it too (release mode).scripts/wasm/e2e.shwarms the component up until an invocation succeeds (counter is now ~4 MB with the tz database; on the CI runner the first call after "up and running" failed), and every failedinvokenow reachesfail()with dapr's answer instead of a silentset -eexit.docs/WASM-COMPONENTS-PROPOSAL.md,docs/DESIGN.mdand the wiki (article,index.md,log.md) state the new rule.The two scala-java-time gaps and their fixes
DateTimeFormatter.ofPattern(any pattern) did not link. Any pattern can containz, so the linker keepsZoneTextPrinterParser. Its print/parse callTimeZone.getTimeZone, and the JSTimeZone.getTimeZone(zoneId)computed the raw offset atInstant.now(the wall clock,js.Date.now). Fix: Compute the raw offset of TimeZone.getTimeZone lazily cquiroz/scala-java-time#618 computes the raw offset lazily (merged 2026-10-17, waits for a scala-java-time release > 2.8.0).ZoneId.of("Europe/Prague"),scala-java-time-tzdb) did not link. The provider's@EnableReflectiveInstantiationregistration is a static initializer, and the linker always keeps static initializers. Scala 3 <= 3.10.0 emits it with the JS-backedReflect.registerInstantiatableClass(..., js.Array(...)). This is a compiler gap, not a scala-java-time one: Scala 3 emits the Scala-typedregisterInstantiatableClassV2since scala/scala3@3bb676972 (onrelease-3.10.1, not in 3.10.0). So the script compiles only the tzdb module with nightly3.10.2-RC1-bin-20261017-ae374e9-NIGHTLY(core stays on 3.9.0). It rewrites the nightly scala3-library version in the tzdb POM back to 3.9.0, so it does not evict the app's scala3-library.Both failures are reproduced here with the released 2.8.0 (link errors:
Instant$.nowcalled fromTimeZone$.getTimeZonecalled fromZoneTextPrinterParser.parse;Reflect$.registerInstantiatableClasscalled fromTzdbZoneRulesProvider.<stinit>, "called from core module analyzer").Retires with a scala-java-time release > 2.8.0 (has #618); region zones also need that release built with Scala 3 >= 3.10.1. Then pin it in
dapr4s-wasmand dropscalaJavaTimeWasm, the script and its CI steps.Earlier, without a source build
scala-java-locales 1.6.0 (released 2026-10-17) contains cquiroz/scala-java-locales#640. scala-java-time 2.8.0 contains cquiroz/scala-java-time#616 and cquiroz/scala-java-time#617. With scala-java-locales 1.5.4 the base java.time code fails to link (
DefaultLocale.scala: global:window, throughDateTimeFormatter.ISO_INSTANT).What works on the wasm path (link AND run)
Answer from the component under wasmtime + daprd (the same as on the JVM):
instant=2026-10-17T10:15:30Z;date=2026-10-18;zoned=2026-10-17T12:15:30+02:00;same=true;iso=2026-10-17T05:15:30-05:00;rfc=Sat, 17 Oct 2026 12:15:30 +0200;day=Saturday;default=UTC;utc=2026-10-17T10:15:30Z[UTC];duration=PT1H45M;prague=2026-10-17T12:15:30+02:00[Europe/Prague];winter=2026-12-24T18:30+01:00[Europe/Prague];newyork=-04:00;pattern=17.10.2026 12:15 Europe/Prague;parsed=2026-12-24T18:30+01:00[Europe/Prague];text=Saturday 17 October 2026toString:Instant,LocalDate,ZonedDateTime,Duration.ISO_OFFSET_DATE_TIME,RFC_1123_DATE_TIME.ofPatternformat and parse, also with a region zone (VV) and month/day names (source build).DayOfWeek.getDisplayName(FULL, ENGLISH).ZoneId.systemDefault()= UTC,ZoneId.of("UTC"),ZoneOffsets;Europe/Prague(summer and winter time) andAmerica/New_York(source build).What still does not link
These are documented in AGENTS.md, with how each one goes away:
Instant.now,Clock.system*,currentTimeMillis,TimeZone.getRawOffset): the scala-wasm javalib callsjs.Date.now. This is the same on 1.22.1-wasm.6 and on itsscala-wasmbranch today. Out of reach for dapr4s.ofPatternand region zones (see above).printlnandUUID.randomUUID: not re-tested, so the old rule stays.How verified (locally, as CI does)
scripts/wasm/build-scala-java-time.sh(fresh clone, JDK 17): OK. The tzdb IR callsregisterInstantiatableClassV2; its POM names scala3-library 3.9.0.sbt scalafmtCheckAll scalafmtSbtCheck: OK.wasm/:scalafmtCheckAll scalafmtSbtCheck -Ddapr4s.wasm.core=source wallWasm/test: 15/15.SBT_ARGS=-Ddapr4s.wasm.core=source scripts/wasm/e2e.sh: OK (counterRaw, counter incl.java-time, importsProbe).sbt wallWasm/test publishLocal+scripts/wasm/consumer-check.sh: OK. Thedapr4s-wasmPOM lists scala-java-time 2.8.0 and scala-java-locales 1.6.0.wasm/publishLocalwith the source build removed from~/.ivy2/local: OK, so the publish job does not need it.🤖 Generated with Claude Code