Um die Komplexität des Themas mal klar zu machen, hier die Antwort von Claude:
⏺ Good question — this is a recurring pattern for international apps. Let me lay out the principle, the three layers where it bites, and a phased path.
The principle
Internally: every moment is a UTC instant. Externally: render in the viewer's timezone. The JVM TZ should be "irrelevant" — code that consults it (Calendar.getInstance(),
default SimpleDateFormat, JSP <fmt:formatDate> without timeZone=, Quartz triggers without explicit TZ) is leaking implementation detail into product behavior. That's exactly what bit you here.
Three layers, three different decisions
1. Storage layer — easy. MySQL DATETIME columns store wall-clock strings with no TZ. Treat them as UTC by convention. With Connector/J 8 + serverTimezone=UTC and a UTC JVM, READ/WRITE round-trips are clean. Already done.
2. Scheduling layer — needs an "anchor TZ" decision. Cron jobs encode an intent. Two flavors:
- Maintenance jobs ("clean up every hour", "midnight reset") — should be UTC. They're operational, not user-facing. Anchor in UTC, never debate it again.
- User-facing scheduled events ("auto-create tournaments at 10am") — should be pinned to a single canonical TZ that matches the brand's "home". For chessmail that's clearly Europe/Berlin. The fact that some users are in Tokyo doesn't change when the tournament gets created — it changes how they see it. Pin via <property
name="timeZone" value="Europe/Berlin"/> on the trigger; JVM stays UTC.
3. Presentation layer — the actual hard problem. Three viable approaches, ranked by sophistication:
- (a) Browser-side rendering. Server emits ISO-8601 UTC strings (<time datetime="2026-05-22T08:00:00Z">); a small JS helper formats with Intl.DateTimeFormat() in the user's browser TZ. Zero server-side TZ knowledge needed. Best for the v3 frontend and any new pages.
- (b) User TZ preference + server rendering. Add User.timezone (string, IANA zone like "Europe/Berlin"). Auto-detect on signup from Intl.DateTimeFormat().resolvedOptions().timeZone, settable in profile. cm:smartDate and equivalents take TZ from the user session. Right call for legacy JSP pages where
you can't easily move to client-side.
- (c) Status quo (UTC everywhere on display). Confusing for everyone. Don't.