Class BrowserTimeZone
ExtendedClientDetails during UI init and read back by
BrowserDateUtils.toLocalDate(Date) / BrowserDateUtils.toDate(LocalDate) on every
Date ↔ LocalDate round-trip.
Migrators MUST call fetch() from a UI init listener before
any view that uses SJSpinner(SpinnerDateModel) or
SJFormattedDatePicker is reached. get() throws
IllegalStateException if the cache is empty — a programming
error (R_match_swing_errors, D_gap_severity_triage case-1 family, SD_browser_timezone), not a soft incompleteness. Failing
loud here is much easier to diagnose than dates silently rendering an
hour off because the server JVM's time zone diverged from the user's
browser.
Slimmed Java port of
karibu-tools'
BrowserTimeZone.kt: we cache the derived ZoneId
rather than the whole ExtendedClientDetails, since the surrogate
layer only ever reads the zone, and tests install a deterministic zone
via setZoneId(ZoneId) without fabricating a fake ECD.
-
Method Summary
Modifier and TypeMethodDescriptionstatic voidfetch()Reads the current UI's cachedExtendedClientDetails; if already populated by Vaadin (placeholder values mean the browser hasn't responded yet — Vaadin usesscreenWidth == -1as the sentinel), derives theZoneIdsynchronously.static ZoneIdget()Returns the cached browserZoneId.static ZoneIdstatic voidStores the given zone in the current session.
-
Method Details
-
fetch
public static void fetch()Reads the current UI's cachedExtendedClientDetails; if already populated by Vaadin (placeholder values mean the browser hasn't responded yet — Vaadin usesscreenWidth == -1as the sentinel), derives theZoneIdsynchronously. Otherwise triggersExtendedClientDetails.refresh(SerializableConsumer)and stores the zone in the callback. No-op if the session already has a stored zone (typical on a refresh through@PreserveOnRefresh, or in tests wheresetZoneId(ZoneId)was called directly).Call this from a UI init listener — once per session is enough.
-
get
Returns the cached browserZoneId. Throws if the cache is empty —BrowserDateUtils.toLocalDate(Date)/BrowserDateUtils.toDate(LocalDate)trust this to be populated, so a missing zone is a programming error (forgot to callfetch()during UI init).Convert a
DatethroughBrowserDateUtils, notdate.toInstant().atZone(get()). The DatePicker peers convert withjava.util.TimeZone's arithmetic, which for pre-1900 dates differs from thisZoneId's local mean time by up to a day. Purejava.timeseams (e.g.DateTimeFormatter.withZone(ZoneId)over aLocalDate) can take this value directly. -
getOrNull
Lenient variant ofget(): returns the cached browserZoneId, ornullwhen none is stored or no session is current — never throws. For callers that can fall back to a default zone rather than failing, e.g.vaadinx.EmulatorContextsnapshotting the zone on a UI thread for background-thread propagation.Lock-safe. Reading
VaadinSession.getAttribute(String)requires the session lock, so on a session-associated thread that doesn't hold it (e.g. Vaadin's license checker on a Jetty request-pool thread, or aSwingWorkerreaching back through its session handle) this briefly acquires the lock, reads, and releases rather than throwing "Cannot access state … without locking" — honoring the never-throw contract and returning the real zone instead of a fallback. The session lock is reentrant, so thehasLock()fast path avoids a redundant re-lock when we already hold it. -
setZoneId
-