Skip to content

SONARJAVA-6786: S9346 - Integer values should not be cast to long for use as timestamps - #6027

Open
romainbrenguier wants to merge 1 commit into
masterfrom
new-rule/SONARJAVA-6786-S9346
Open

SONARJAVA-6786: S9346 - Integer values should not be cast to long for use as timestamps#6027
romainbrenguier wants to merge 1 commit into
masterfrom
new-rule/SONARJAVA-6786-S9346

Conversation

@romainbrenguier

Copy link
Copy Markdown
Contributor

Summary

  • Updated S9346 rule HTML description with clearer explanations of the Year 2038 problem, potential impact, and how to fix it
  • Improved code examples in the rule description
  • Updated S9346 JSON metadata: changed scope to "Main", reformatted for consistency

Test plan

  • Verify rule metadata loads correctly in SonarQube
  • Verify HTML description renders properly

🤖 Generated with Claude Code

Improve the rule description with clearer explanations of the Year 2038
problem, code examples, and potential impact. Update JSON metadata to
use "Main" scope and reformat for consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6786

@sonarqube-next

Copy link
Copy Markdown
Contributor

Quality Gate passed Quality Gate passed

Issues
0 New issues
0 Fixed issues
0 Accepted issues

Measures
0 Security Hotspots
0 Dependency risks
No data about Coverage
No data about Duplication

See analysis details on SonarQube

@romainbrenguier
romainbrenguier marked this pull request as ready for review August 26, 2026 13:57
Comment on lines 40 to 50
<pre data-diff-id="1" data-diff-type="noncompliant">
int timestamp = 1234567890;
Date date = new Date(timestamp); // Noncompliant — int implicitly widened
Date date2 = new Date((long) timestamp); // Noncompliant — cast doesn't fix overflow
long epochMillis = (long) timestamp; // Noncompliant
Date date = new Date(epochMillis);
</pre>
<h3>Compliant solution</h3>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
long timestamp = 1234567890L;
Date date = new Date(timestamp);
Date date2 = new Date(timestamp);
long epochMillis = timestamp;
Date date = new Date(epochMillis);
</pre>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: S9346 noncompliant example shows a case the rule never reports

The new noncompliant snippet marks long epochMillis = (long) timestamp; as the issue line, but IntegerToLongTimestampCastCheck only reports on arguments of Date/Timestamp constructors and Instant.ofEpochSecond/ofEpochMilli/Calendar.setTimeInMillis (checkArgument is reached only from onMethodInvocationFound/onConstructorFound). A standalone cast in a variable initializer is explicitly asserted compliant in the test sample (compliantNonTimestampCast: long result = (long) intVar;), and new Date(epochMillis) passes a long, so this example raises no issue at all — the previous example (new Date(timestamp) / new Date((long) timestamp)) did. Restore examples where the int-typed value is passed directly to the timestamp API, which is where the rule actually reports.

Use examples where the narrow-typed value reaches a timestamp API, matching the patterns the check reports.:

<pre data-diff-id="1" data-diff-type="noncompliant">
int timestamp = 1234567890;
Date date = new Date(timestamp);         // Noncompliant, int implicitly widened
Date date2 = new Date((long) timestamp); // Noncompliant, the cast does not fix the overflow
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
long timestamp = 1234567890L;
Date date = new Date(timestamp);
Date date2 = new Date(timestamp);
</pre>
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed by running the analyzer locally against the exact documentation example. I temporarily added this method to IntegerToLongTimestampCastCheckSample.java:

void compliantPrDocumentationExample() {
  int timestamp = 1234567890;
  long epochMillis = (long) timestamp;
  new Date(epochMillis);
}

I left it without a Noncompliant marker, so CheckVerifier.verifyIssues() would fail if S9346 reported anything on it. I then ran:

mvn -pl java-checks -Dtest=IntegerToLongTimestampCastCheckTest test

Result: Tests run: 1, Failures: 0, Errors: 0 / BUILD SUCCESS, confirming that the analyzer does not raise an issue for the PR example. I removed the temporary test addition afterward.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the thorough verification — that confirms the noncompliant example in S9346.html doesn't actually trigger the rule.

Comment on lines +1 to +2
<p>This is an issue when a smaller integer type value is widened to a larger integer type and then used as an absolute timestamp, such as milliseconds
or seconds since the Unix epoch (January 1, 1970).</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Rule description omits the narrowing-cast pattern the check reports

The intro now scopes the rule to "a smaller integer type value is widened to a larger integer type", and "How to fix it" only says to change the variable type from int to long. The check also reports narrowing casts of already-correct long values (new Date((int) longVar) is asserted noncompliant in the test sample), for which the documented fix (changing the declared type) does not apply — a user hitting that issue gets no applicable guidance. Mention removing the narrowing cast as a second fix case.

Document the narrowing-cast case in the "How to fix it" section.:

<p>Change the variable type from <code>int</code> to <code>long</code> to properly represent the timestamp. This ensures the value can hold the full
range of possible timestamp values without overflow. When the value is already a <code>long</code>, remove any narrowing cast to <code>int</code>,
<code>short</code>, <code>byte</code> or <code>char</code> that truncates it before it is used as a timestamp.</p>
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 resolved / 2 findings

Updates rule S9346 metadata and description for the Year 2038 problem, but the noncompliant example depicts a case the rule never reports and omits the narrowing-cast pattern.

⚠️ Bug: S9346 noncompliant example shows a case the rule never reports

📄 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9346.html:40-50

The new noncompliant snippet marks long epochMillis = (long) timestamp; as the issue line, but IntegerToLongTimestampCastCheck only reports on arguments of Date/Timestamp constructors and Instant.ofEpochSecond/ofEpochMilli/Calendar.setTimeInMillis (checkArgument is reached only from onMethodInvocationFound/onConstructorFound). A standalone cast in a variable initializer is explicitly asserted compliant in the test sample (compliantNonTimestampCast: long result = (long) intVar;), and new Date(epochMillis) passes a long, so this example raises no issue at all — the previous example (new Date(timestamp) / new Date((long) timestamp)) did. Restore examples where the int-typed value is passed directly to the timestamp API, which is where the rule actually reports.

Use examples where the narrow-typed value reaches a timestamp API, matching the patterns the check reports.
<pre data-diff-id="1" data-diff-type="noncompliant">
int timestamp = 1234567890;
Date date = new Date(timestamp);         // Noncompliant, int implicitly widened
Date date2 = new Date((long) timestamp); // Noncompliant, the cast does not fix the overflow
</pre>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
long timestamp = 1234567890L;
Date date = new Date(timestamp);
Date date2 = new Date(timestamp);
</pre>
💡 Quality: Rule description omits the narrowing-cast pattern the check reports

📄 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9346.html:1-2 📄 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9346.html:35-37

The intro now scopes the rule to "a smaller integer type value is widened to a larger integer type", and "How to fix it" only says to change the variable type from int to long. The check also reports narrowing casts of already-correct long values (new Date((int) longVar) is asserted noncompliant in the test sample), for which the documented fix (changing the declared type) does not apply — a user hitting that issue gets no applicable guidance. Mention removing the narrowing cast as a second fix case.

Document the narrowing-cast case in the "How to fix it" section.
<p>Change the variable type from <code>int</code> to <code>long</code> to properly represent the timestamp. This ensures the value can hold the full
range of possible timestamp values without overflow. When the value is already a <code>long</code>, remove any narrowing cast to <code>int</code>,
<code>short</code>, <code>byte</code> or <code>char</code> that truncates it before it is used as a timestamp.</p>
🤖 Prompt for agents
Code Review: Updates rule S9346 metadata and description for the Year 2038 problem, but the noncompliant example depicts a case the rule never reports and omits the narrowing-cast pattern.

1. ⚠️ Bug: S9346 noncompliant example shows a case the rule never reports
   Files: sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9346.html:40-50

   The new noncompliant snippet marks `long epochMillis = (long) timestamp;` as the issue line, but `IntegerToLongTimestampCastCheck` only reports on arguments of `Date`/`Timestamp` constructors and `Instant.ofEpochSecond`/`ofEpochMilli`/`Calendar.setTimeInMillis` (checkArgument is reached only from onMethodInvocationFound/onConstructorFound). A standalone cast in a variable initializer is explicitly asserted compliant in the test sample (`compliantNonTimestampCast`: `long result = (long) intVar;`), and `new Date(epochMillis)` passes a `long`, so this example raises no issue at all — the previous example (`new Date(timestamp)` / `new Date((long) timestamp)`) did. Restore examples where the int-typed value is passed directly to the timestamp API, which is where the rule actually reports.

   Fix (Use examples where the narrow-typed value reaches a timestamp API, matching the patterns the check reports.):
   <pre data-diff-id="1" data-diff-type="noncompliant">
   int timestamp = 1234567890;
   Date date = new Date(timestamp);         // Noncompliant, int implicitly widened
   Date date2 = new Date((long) timestamp); // Noncompliant, the cast does not fix the overflow
   </pre>
   <h4>Compliant solution</h4>
   <pre data-diff-id="1" data-diff-type="compliant">
   long timestamp = 1234567890L;
   Date date = new Date(timestamp);
   Date date2 = new Date(timestamp);
   </pre>

2. 💡 Quality: Rule description omits the narrowing-cast pattern the check reports
   Files: sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9346.html:1-2, sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9346.html:35-37

   The intro now scopes the rule to "a smaller integer type value is widened to a larger integer type", and "How to fix it" only says to change the variable type from `int` to `long`. The check also reports narrowing casts of already-correct `long` values (`new Date((int) longVar)` is asserted noncompliant in the test sample), for which the documented fix (changing the declared type) does not apply — a user hitting that issue gets no applicable guidance. Mention removing the narrowing cast as a second fix case.

   Fix (Document the narrowing-cast case in the "How to fix it" section.):
   <p>Change the variable type from <code>int</code> to <code>long</code> to properly represent the timestamp. This ensures the value can hold the full
   range of possible timestamp values without overflow. When the value is already a <code>long</code>, remove any narrowing cast to <code>int</code>,
   <code>short</code>, <code>byte</code> or <code>char</code> that truncates it before it is used as a timestamp.</p>

Implementation Status ✅ 1 of 1 objectives covered
SONARJAVA-6786 - 1 of 1 objectives covered

This PR updates the metadata and description documentation for rule S9346.

✅ 1 covered here
  • ✅ Create rule S9346: Integer values should not be cast to long for use as timestamps
Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.
Unblock → Override a blocking verdict and allow merging.

Comment with these commands to change the behavior for this request:

Auto-apply Compact Unblock
gitar auto-apply:on         
gitar display:verbose         
gitar unblock         

Was this helpful? React with 👍 / 👎 | Gitar

@nathsou nathsou left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review Gitar's findings as I they seem valid, apart from that, it looks good to me.

@romainbrenguier

Copy link
Copy Markdown
Contributor Author

The update where made on the rspec side: https://github.com/SonarSource/rspec/pull/7997

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.

2 participants