fix(api-core): properly implement Clock.withZone() in MonotonicClock - #12753
fix(api-core): properly implement Clock.withZone() in MonotonicClock#12753poliglots wants to merge 2 commits into
Conversation
Fix issue apache#12608: MonotonicClock.withZone() was ignoring the zone parameter and returning 'this', violating the java.time.Clock contract. Changes: - Add a 'zone' field to track timezone per instance - Implement withZone() to create a new clock variant with the requested timezone, preserving monotonic timing - Update getZone() to return the instance's actual zone - Throw IllegalArgumentException on null zone (per Clock spec) - Add tests verifying withZone() returns a new instance with correct zone and that monotonic timing is preserved The singleton instance continues to use UTC. Creating timezone variants does not compromise monotonicity since Instant is timezone-agnostic and monotonicity derives from System.nanoTime(). Signed-off-by: Shatrughan Rai <polyglot.dev@outlook.com>
gnodet
left a comment
There was a problem hiding this comment.
Review — Good fix for the Clock.withZone() contract, a few documentation nits
The core approach is sound: since Instant is timezone-agnostic and monotonicity derives from System.nanoTime(), creating timezone variants via withZone() doesn't compromise monotonic behavior. Good test coverage for the new behavior.
Findings
-
🔴 [high] Contradictory Javadoc on
withZone()— The@paramtag says "or {@code null} to use UTC" (implying null is accepted), while the@throwstag says "IllegalArgumentException if zone is {@code null}" (rejecting null). The implementation rejects null, so the@paramtext is misleading — should read@param zone the target timezone, not null. -
[medium] Exception type — The JDK
Clock.withZone()contract specifies@param zone ... not nulland all standard JDK Clock implementations useObjects.requireNonNull(zone, "zone")which throwsNullPointerException. Consider usingObjects.requireNonNullfor consistency with the JDK contract. -
[medium] Outdated class-level Javadoc — Lines 35-36 still say "This implementation is singleton-based and always uses UTC timezone. The clock cannot be adjusted to different timezones" — this is no longer accurate after this PR.
-
[low] Same-zone optimization —
withZone()always creates a new instance, even when the requested zone equals the current zone. The JDK'sClock.SystemClock.withZone()returnsthiswhenzone.equals(this.zone). Adding this short-circuit avoids unnecessary allocations.
This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
| * @return this clock instance | ||
| * @param zone the target timezone, or {@code null} to use UTC | ||
| * @return a new clock with the specified timezone | ||
| * @throws IllegalArgumentException if zone is {@code null} |
There was a problem hiding this comment.
Contradictory Javadoc + wrong exception type: The @param says "or {@code null} to use UTC" but the code throws on null — the @param should say @param zone the target timezone, not null.
Also, the JDK Clock.withZone() convention uses NullPointerException (via Objects.requireNonNull), not IllegalArgumentException. Consider:
| * @throws IllegalArgumentException if zone is {@code null} | |
| public Clock withZone(ZoneId zone) { | |
| java.util.Objects.requireNonNull(zone, "zone"); | |
| if (zone.equals(this.zone)) { | |
| return this; | |
| } | |
| return new MonotonicClock(startNanos, startInstant, zone); | |
| } |
This also adds the same-zone optimization (returns this when the zone matches, matching JDK convention).
- Use NullPointerException (via Objects.requireNonNull) matching the JDK Clock.withZone() contract instead of IllegalArgumentException - Fix contradictory Javadoc: @PARAM now correctly says 'not null' and @throws specifies NullPointerException - Update class-level Javadoc to reflect that withZone() supports timezone variants - Add same-zone optimization returning 'this' when zone matches - Update tests to expect NullPointerException and verify same-zone behavior Signed-off-by: Shatrughan Rai <polyglot.dev@outlook.com>
gnodet
left a comment
There was a problem hiding this comment.
All four findings from the previous review have been thoroughly addressed — great work, @poliglots! 👍
Previous findings → resolution:
- ✅ Contradictory Javadoc → Fixed.
@paramnow says "not null" and@throws NullPointerExceptionis documented. - ✅ Wrong exception type → No longer applicable —
withZone()now properly creates timezone variants instead of ignoring the parameter. - ✅ Stale class-level Javadoc → Fixed. Now correctly describes that timezone variants can be created via
withZone(). - ✅ Missing short-circuit optimization → Implemented with
zone.equals(this.zone).
The new implementation is well-designed — sharing the monotonic timing base across zone variants via a private constructor is the right approach. The test coverage is comprehensive (new zone, same zone, null zone, instant consistency).
One very minor style note: java.util.Objects.requireNonNull(zone, "zone") uses a fully qualified class name rather than an import — most Maven code imports java.util.Objects. This should be caught by the formatter, so not blocking.
This review was generated by an AI agent (Claude Code) and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
Fix issue #12608: MonotonicClock.withZone() was ignoring the zone parameter and returning 'this', violating the java.time.Clock contract.
Changes:
The singleton instance continues to use UTC. Creating timezone variants does not compromise monotonicity since Instant is timezone-agnostic and monotonicity derives from System.nanoTime().