Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,33 +1,59 @@
<p>Using 32-bit signed integer types for timestamps can lead to serious reliability issues such as incorrect time representation,
system failures, and the Year 2038 problem.</p>
<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>
Comment on lines +1 to +2

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 👍 / 👎

<h2>Why is this an issue?</h2>
<p>A 32-bit signed integer can hold values from -2,147,483,648 to 2,147,483,647. While this might seem like a large range, it's insufficient for
<p>A 32-bit signed integer can hold values from -2,147,483,648 to 2,147,483,647. While this might seem like a large range, its insufficient for
representing timestamps:</p>
<ul>
<li><strong>Milliseconds since epoch</strong>: A 32-bit integer can only represent approximately 24.8 days of milliseconds. Any timestamp beyond
this range will overflow.</li>
<li><strong>Seconds since epoch</strong>: A 32-bit integer can represent about 68 years, covering dates from 1970 to 2038.</li>
<li><strong>Seconds since epoch</strong>: A 32-bit integer can represent about 68 years, covering dates from 1970 to 2038. This is the famous "Year
2038 problem" for 32-bit systems.</li>
</ul>
<p>When you cast a 32-bit integer to a 64-bit integer for use as a timestamp, you're not fixing the underlying problem &mdash; the value is already
corrupted or limited by the 32-bit constraint before the cast happens.</p>
<h3>Noncompliant code example</h3>
<p>When you cast a 32-bit integer to a 64-bit integer for use as a timestamp, you’re not fixing the underlying problem — the value is already
corrupted or limited by the 32-bit constraint before the cast happens. The cast simply preserves the incorrect or overflowed value in a larger
container.</p>
<p>Timestamps should be stored as 64-bit integer values from the start to ensure they can represent dates far into the past and future without
overflow. A 64-bit integer type can represent timestamps for approximately 292 million years, which is more than sufficient for any practical
application.</p>
<p>Using a 32-bit integer for timestamps is almost always a bug that will cause:</p>
<ul>
<li>Incorrect date and time calculations</li>
<li>Application failures when processing dates outside the limited range</li>
<li>Data corruption when timestamps overflow</li>
<li>Difficult-to-diagnose issues in production systems</li>
</ul>
<p>In Java, 32-bit integers are represented by the <code>int</code> type and 64-bit integers by the <code>long</code> type.</p>
<h3>What is the potential impact?</h3>
<p>Using 32-bit signed integer types for timestamps can lead to serious reliability issues:</p>
<ul>
<li><strong>Incorrect time representation</strong>: Dates may be displayed incorrectly or calculations may produce wrong results</li>
<li><strong>System failures</strong>: Applications may crash or behave unpredictably when timestamps overflow</li>
<li><strong>Data integrity issues</strong>: Stored timestamps may be corrupted, leading to incorrect historical records</li>
<li><strong>Year 2038 problem</strong>: For second-based timestamps, the system will fail on January 19, 2038, when the maximum value representable
by a 32-bit signed integer is exceeded</li>
</ul>
<h2>How to fix it</h2>
<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.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<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>
Comment on lines 40 to 50

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.

<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li><a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Oracle Java Documentation - Primitive Data Types</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#currentTimeMillis()">Oracle Java Documentation -
System.currentTimeMillis()</a></li>
<li><a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Instant.html">Oracle Java Documentation - Class Instant</a>
</li>
<li>Java Documentation - <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Data Types</a></li>
<li>Java Documentation - <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/System.html#currentTimeMillis()">Class
System - currentTimeMillis()</a></li>
<li>Java Documentation - <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Instant.html">Class Instant</a></li>
</ul>

Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
{
"title": "Integer values should not be cast to long for use as timestamps",
"type": "BUG",
"code": {
"impacts": {
"RELIABILITY": "HIGH"
},
"attribute": "LOGICAL"
},
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
"constantCost": "5 min"
},
"tags": ["pitfall", "datetime"],
"tags": [
"pitfall",
"datetime"
],
"defaultSeverity": "Critical",
"ruleSpecification": "RSPEC-9346",
"sqKey": "S9346",
"scope": "All",
"quickfix": "unknown"
"scope": "Main",
"quickfix": "unknown",
"code": {
"impacts": {
"RELIABILITY": "HIGH"
},
"attribute": "LOGICAL"
}
}
Loading