Insidious error in date time arithmetics

Insidious error in date time arithmetics

Suppose that we need to write a program to get the date which is exactly 50 days before now. What we can do is that we get the epoch time of now, and shift backward for 50 days. The code is shown in the following:

  1. package me.shanhe.java;
  2.  
  3. import java.util.Date;
  4.  
  5. public class FiftyDaysAgo {
  6.   public static void main(String[] args) {
  7.     final long now = new Date().getTime();
  8.     final long then = now - 50 * 24 * 3600 * 1000;
  9.     System.out.println(new Date(now));
  10.     System.out.println(new Date(then));
  11.   }
  12. }

Simple as it is, we use a multiplication to express the milliseconds of 50 days and let the compiler figure out the actual value during constant folding so that the code is more readable, and less error-prone. Ironically, the multiplication is erring. Look at the output:

Sun Aug 28 12:22:57 PDT 2011
Sun Aug 28 05:25:44 PDT 2011

Fifty days ago is still today?

As you may have realized now, there was arithmetic overflow happened somewhere. Indeed, overflow happened during the multiplication of the integer literals in line 8. According to the Java Language Specification 3.0 (JLS3), §3.10.1, "An integer literal is of type long if it is suffixed with an ASCII letter L or l (the lowercase of the letter L); otherwise it is of type int (from –2147483648 to 2147483647, inclusive).", the operands of the multiplication are all int's, and so is the result of the multiplication. However, the result is too large and it overflows (50 * 24 * 3600 * 1000 = 4320000000 > 2147483647). Even though later the multiplication result becomes a long type through numeric promotion during the subtraction, the overflow has already happened, and the end result cannot be correct. Note that, you won't see any error at compile-time or any exception at runtime, because "The built-in integer operators do not indicate overflow or underflow in any way. " (JLS3, §4.2.3). Such overflow could be insidious, and causes unexpected results that are difficult to debug.

To fix the code, we append L to each of the integer literals in line 8 (Do not use the lowercase l, as it resembles the digit 1.):

final long then = now - 50L * 24L * 3600L * 1000L;

Even though we can append L to only the first operand and rely on the numeric promotion, e.g.,

final long then = now - 50L * 24 * 3600 * 1000;

appending to all the literals is still recommended, in case the suffix is missing during some casual modifications.

It concludes here that we should be aware of the types of integer literals in arithmetic constant expressions, especially the suffix for the long type. Though the compiler does give an error if a single integer literal is out of range, e.g.,

final long value = 2147483648; // compile error.

the compiler does not give such an error when the results of arithmetic constant expressions are out of range, as in this case. For floating-point literals, it is less likely to make such mistakes, because the wider double type is the default type, and as a rule of thumb, the double type is always preferred over the float type.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

More information about formatting options

To prevent automated spam submissions leave this field empty.