Skip to content

fix: preserve Instant precision when cloning a Value - #2071

Open
RaphaelFakhri wants to merge 1 commit into
open-feature:mainfrom
RaphaelFakhri:fix/instant-clone-precision
Open

RaphaelFakhri wants to merge 1 commit into
open-feature:mainfrom
RaphaelFakhri:fix/instant-clone-precision

Conversation

@RaphaelFakhri

Copy link
Copy Markdown

This PR

  • Value.clone() copied Instant values with Instant.ofEpochMilli(...), which drops sub-millisecond precision. ImmutableStructure (and so ImmutableContext) clones values on construction and on every read, so getValue(key).asInstant() returned a different instant than the one stored.
  • Instant is immutable, so clone() now reuses the original instance.
  • Adds ImmutableStructureTest.getValueShouldPreserveInstantPrecision, which checks getValue, asMap and asObjectMap with a nanosecond-precision instant.

Related Issues

Fixes #2070

Notes

The existing MutatingGetInstantValueShouldNotChangeOriginalValue test truncates Instant.now() to milliseconds to avoid this behavior. It still passes and is left unchanged.

How to test

./mvnw test -Dtest=ImmutableStructureTest

Without the change in Value.java, the new test fails with expected: <2023-11-14T22:13:20.123456789Z> but was: <2023-11-14T22:13:20.123Z>.

Value.clone() rebuilt Instant values with Instant.ofEpochMilli, which drops
sub-millisecond precision. ImmutableStructure and ImmutableContext clone
values on construction and on every read, so getValue() returned a different
instant than the one stored. Instant is immutable, so the original instance
is reused instead.

Signed-off-by: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com>
@RaphaelFakhri
RaphaelFakhri requested review from a team as code owners November 4, 2026 06:36
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Value.clone() now retains the full precision of wrapped Instant values. A test checks that ImmutableStructure access paths return the original nanosecond precision.

Changes: Instant precision

Layer / File(s) Summary
Preserve precision during cloning
src/main/java/dev/openfeature/sdk/Value.java, src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java
Value.clone() reuses the immutable Instant instead of reconstructing it from epoch milliseconds. A test checks precision through getValue(...).asInstant(), asMap(), and asObjectMap().

Estimated code review effort: 2 (Simple) | ~5 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: thiyagu06, toddbaert

Merge Risk: Minimal · up to 9f35f

The change preserves Instant precision and adds coverage for the affected access paths.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title states the main change: Value.clone() now keeps the full precision of an Instant instead of truncating it to milliseconds. The raw summary confirms that clone() reuses the wrapped Instant, a…
Description check Passed The description matches the changeset. It explains that Value.clone() used Instant.ofEpochMilli(...) and dropped sub-millisecond precision, and that clone() now reuses the immutable Instant. It names …
Linked Issues check Passed Issue #2070 requires that a stored Instant be returned unchanged through getValue(key).asInstant(), asMap(), and asObjectMap(), with no millisecond truncation. The reviewed head reads src/main/java/de…
Out of Scope Changes check Passed The PR changes two files, as summarized: Value.java (+2/-2) and ImmutableStructureTest.java (+13/-0). The Value.java change is limited to the Instant branch of Value.clone(). It removes the millisecon…
Docstring Coverage Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Full details: Title check

Explanation

The title states the main change: Value.clone() now keeps the full precision of an Instant instead of truncating it to milliseconds. The raw summary confirms that clone() reuses the wrapped Instant, and the PR objectives describe the same fix. The title uses a conventional commit prefix and fits in one short sentence.

Full details: Description check

Explanation

The description matches the changeset. It explains that Value.clone() used Instant.ofEpochMilli(...) and dropped sub-millisecond precision, and that clone() now reuses the immutable Instant. It names the new test ImmutableStructureTest.getValueShouldPreserveInstantPrecision, gives a test command, and links issue #2070. This agrees with the raw summary and the objectives.

Full details: Linked Issues check

Explanation

Issue #2070 requires that a stored Instant be returned unchanged through getValue(key).asInstant(), asMap(), and asObjectMap(), with no millisecond truncation. The reviewed head reads src/main/java/dev/openfeature/sdk/Value.java. Value.clone() now returns new Value(this.asInstant()) for Instant values, so the original instance is reused. The comment states that Instant is immutable and that sharing is safe. ImmutableStructure.getValue() and copyAttributes() call Value.clone(), so the fix covers getValue() and asMap(). Those paths now preserve full precision. The PR adds ImmutableStructureTest.getValueShouldPreserveInstantPrecision, which checks all three read paths with a nanosecond instant, as described in the PR description. The test file was not read because the diff tool was unavailable, so the test content rests on the PR description. The code fix alone satisfies the stated requirement. The existing MutatingGetInstantValueShouldNotChangeOriginalValue test is unchanged and remains valid. The requirement has no non-coding parts.

Full details: Out of Scope Changes check

Explanation

The PR changes two files, as summarized: Value.java (+2/-2) and ImmutableStructureTest.java (+13/-0). The Value.java change is limited to the Instant branch of Value.clone(). It removes the millisecond-truncating copy and reuses the instance. This directly implements #2070. The test addition checks the same requirement through getValue, asMap, and asObjectMap. No unrelated production behavior, API declarations, or other files are reported in the summary. The base-to-head text diff could not be read, so the before-state of Value.java is taken from the PR summary and description, not from a direct diff.

Full details: Docstring Coverage

Explanation

Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 2 files.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: af657e7c-f0c6-4694-937b-2318261ce619
Commits

Reviewing files that changed from the base of the PR and between b2ff3cd and 9f35f7d.

Files selected for processing: 2
  • src/main/java/dev/openfeature/sdk/Value.java
  • src/test/java/dev/openfeature/sdk/ImmutableStructureTest.java

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.

@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ImmutableContext and ImmutableStructure truncate Instant values to milliseconds

2 participants