Home > java > Yet another bug

Yet another bug

I came across another silly little bug today. Take a look at the code below (assume ‘days’ is a parameter and that this snippet is part of a larger function)

Date now = new Date();
long nowMillis = now.getTime();
Timestamp nowTimestamp = new Timestamp(nowMillis);
long future = 3600 * 24 * days * 1000;
Timestamp expiryTimestamp = new Timestamp(nowMillis + future);
System.out.println(nowTimestamp);
System.out.println(expiryTimestamp);

Can you tell what is wrong with it ? What the code intends to do is to set an expiry timestamp to an element – X days from today. A caller called this code snippet with the value 5. Well there is nothing wrong so far. Here is the output

2010-04-27 10:37:10.497
2010-05-02 10:37:10.497

So then came along an element that needed an expiry of around 40 days. The caller calls with the value 40. Guess what the output is

2010-04-27 10:38:43.372
2010-04-17 17:35:56.076

Umm… so the expiry date is *before* the current date. Super. So what is going wrong ? The value of future in this case is -838967296. Although future is a long type, the data type that is being calculated to assign a value to it, is int. An int cannot take anything more than Integer.MAX_VALUE (2147483647).

If future had been calculated correctly, its value would be 3456000000. Instead what is being presented is the wrapped around value obtained after adding the remainder to Integer.MIN_VALUE.

I guess that just goes to show that if it aint broke you may still need to fix it later. Of course that depends on what your definition of ‘broke’ is :)





Categories: java Tags: ,
  1. Fabricio Tuosto
    April 30th, 2010 at 22:48 | #1

    A way to solve dates math is with calendar more or less like this
    Date date = new Date(); // received as parameter
    int days = 5; //
    Calendar expiry = Calendar.getIntance();
    expiry.setTime(date);
    expiry.add(Calendar.DATE,days);
    return new Timestamp(expiry.getTimeInMillis());

  2. Nov
    April 30th, 2010 at 23:36 | #2

    Here is the bug:
    long future = 3600 * 24 * days * 1000;
    Simple fix would be:
    long future = 3600 * 24 * days * 1000L;

  3. steven
    May 1st, 2010 at 00:20 | #3

    so it’s this part?: 3600 * 24 * days * 1000
    where each factor is an integer

  4. Anonymous Coward
    May 1st, 2010 at 09:04 | #4

    Can you tell what is wrong with it?

    Yes, you are not using Joda Time http://joda-time.sourceforge.net/ for your date and time calculations :D

  5. DD605
    May 1st, 2010 at 17:43 | #5

    no bug.
    but if your parameter days is a int the calculation is executed as a (correct) multiplication of 4 int’s and the result is put in a variable of type long.
    if you want to do the calculation as long’s at least one of the numbers should be of the type long.
    so long future = 3600l * 24 * days * 1000; will do the job as you wish

  6. Michal R.
    May 2nd, 2010 at 10:34 | #6

    Why not just write it this way:
    long future = 3600L * 24L * days * 1000L;

  7. knalli
    May 2nd, 2010 at 17:53 | #7

    Actually, this is not a bug, but a little bit the fault of owned developer. Even C has this issue.

    The developer assumes that
    long future = 3600 * 24 * days * 1000;

    will compute as long, but that is not true. Because every component of “3600 * 24 * days * 1000″ is int – the result is int, too. The type of “future” does not matter (because it is irrelevant for the computing part).

    If you want a calculating “on long level” at least one component must be long. If this is not so, you must at least one of them. Option 1 use no “int days” but “long days” or option 2 cast one of the factors, like “3600L”.

    The issue, other story:
    float d = 1/2

    The result is not 0.5 because int/int has no decimals.

    Anyway, the usage of calendar and “real” 40 days would be better. The next 40 days can be 3456000001 seconds, too. :)

  8. May 3rd, 2010 at 04:34 | #8

    @Anonymous Coward
    Never looked at JODA time. Ah one more java lib out there

    @Nov
    @steven
    @DD605
    @Michal R.

    Yep that is a solution one could use. If one of the participants in the operations is a long, the result would have to be a long.

    @knalli
    @Fabricio Tuosto

    Yep I agree that a calendar could be used here to provide a better solution.

    When I said bug, I meant it was a bug from the method and developer’s perspective. This component is supposed to return the expiry time but it does the opposite sometimes :)

  9. May 3rd, 2010 at 16:17 | #9

    Hi,
    using the calendar is tricky too, I wrote a post featuring a few calendar related puzzlers:
    http://javarizon.wordpress.com/2010/04/25/java-calendar-puzzlers/

  10. June 8th, 2010 at 14:34 | #10

    This bug cannot exist in Ada because, in Ada, integers do not overflow; instead they raise an exception (Constraint_Error, “overflow check failed”) that allows you to find the problem immediately. Combine this with the standard library types for manipulating dates, which ensure you cannot *possibly* have e.g. month numbers outside the range 1 ..12, etc:

    package Standard is
    [...]
    type Duration is delta implementation-defined range implementation-defined;
    end Standard;

    (Duration is a fixed-point type representing seconds with absolute accuracy, as opposed to a floating-point type that has relative accuracy).

    package Ada.Calendar is
    type Time is private;
    subtype Year_Number is Integer range 1901 .. 2399;
    subtype Month_Number is Integer range 1 .. 12;
    subtype Day_Number is Integer range 1 .. 31;
    subtype Day_Duration is Duration range 0.0 .. 86_400.0;
    function Clock return Time;
    function Year (Date : Time) return Year_Number;
    function Month (Date : Time) return Month_Number;
    function Day (Date : Time) return Day_Number;
    function Seconds(Date : Time) return Day_Duration;
    procedure Split (Date : in Time;
    Year : out Year_Number;
    Month : out Month_Number;
    Day : out Day_Number;
    Seconds : out Day_Duration);
    function Time_Of(Year : Year_Number;
    Month : Month_Number;
    Day : Day_Number;
    Seconds : Day_Duration := 0.0)
    return Time;
    function “+” (Left : Time; Right : Duration) return Time;
    function “+” (Left : Duration; Right : Time) return Time;
    function “-” (Left : Time; Right : Duration) return Time;
    function “-” (Left : Time; Right : Time) return Duration;
    function “<" (Left, Right : Time) return Boolean;
    function "” (Left, Right : Time) return Boolean;
    function “>=”(Left, Right : Time) return Boolean;
    Time_Error : exception;
    private
    … — not specified by the language
    end Ada.Calendar;

    Ada programmers consequently spend comparatively very little time debugging their software.

  11. June 8th, 2010 at 15:53 | #11

    @Ludovicc Brenta

    I do not know Ada, but you raise a good point. Thanks for sharing that.

    On a side note, this bug can actually be exploited in certain cases.

  1. No trackbacks yet.