6

I am using JodaTime jar with Oracle Java 8. I am receiving packets from a firmware device and its beginning of time is beginning of day of January 1, 2000. I need to get the number of second since January 1, 2000. The math seems simple but for some reason it is giving me a negative value, which comes out to a time in the year 1999, rather than current time:

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;


public class TimeIssue {

    private DateTime currentTime = DateTime.now(DateTimeZone.UTC);
    private DateTime zeroPointTime = new DateTime(2000, 1, 1, 0, 0, DateTimeZone.UTC);

    private void checkTime(){
        System.out.println("current time: " + currentTime);     
        System.out.println("current time to secs: " + timetos(currentTime));
    }

    private int timetos(DateTime time){
        return (int)(time.getMillis() - zeroPointTime.getMillis())/1000;
    }

    public static void main(String[] args) {
        TimeIssue issue = new TimeIssue();
        issue.checkTime();
    }

}

output:

current time: 2014-07-09T21:28:46.435Z
current time in seconds: -1304974
current time from seconds: 1999-12-16T21:30:26.000Z

I would assume subtracting current time in milliseconds from year 2000 time in milliseconds and dividing by 1000 would give me the current time in seconds since 2000, but it is giving me a negative number. What might I be doing wrong?

jmj
  • 237,923
  • 42
  • 401
  • 438
JohnMerlino
  • 3,900
  • 4
  • 57
  • 89
  • 2
    Just a tangential comment: why are you using the joda time jar if your target platform is java 8? Java 8 includes a time package (java.time) which has a very similar api to joda time without requiring the external dependency. – Jules Jul 09 '14 at 21:52

5 Answers5

7

As others have said, this is due to integer overflow. You can just add brackets:

return (int)((time.getMillis() - zeroPointTime.getMillis())/1000);

But it would be cleaner to use Duration:

Duration duration = new Duration(zeroPointTime, currentTime);
return (int) duration.getStandardSeconds();
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • +1 for the `Duration` anyways, but keeping it to `int` will make it work upto `19 Jan 2068` only – jmj Jul 09 '14 at 22:15
  • @Jigar: Yes, but I'm assuming that's a requirement. I suspect most code written now will be replaced by then anyway :) – Jon Skeet Jul 09 '14 at 22:20
2

In

return (int)(time.getMillis() - zeroPointTime.getMillis())/1000;

the cast to int is applied to the subtraction before the division by 1000.

The conversion from long to int overflows in the int range, giving a negative value. Apply the cast after the division

return (int) ((time.getMillis() - zeroPointTime.getMillis()) / 1000);

If you really need int values.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
2

A note from Joda-Time Home page:

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

In Mar 2014, java.time API, the modern date-time API, was released as part of the Java 8 standard library.

Solution using java.time:

ChronoUnit.SECONDS.between(zeroPointTime, currentTime)

where zeroPointTime and currentTime are instances of OffsetDateTime representing 2000-01-01T00:00Z and current date-time at UTC respectively.

Demo:

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;

class Main {
    public static void main(String[] args) {
        OffsetDateTime currentTime = OffsetDateTime.now(ZoneOffset.UTC);
        OffsetDateTime zeroPointTime = OffsetDateTime.of(LocalDate.of(2000, 1, 1), LocalTime.of(0, 0), ZoneOffset.UTC);
        System.out.println(ChronoUnit.SECONDS.between(zeroPointTime, currentTime));
    }
}

Result from a sample run:

727133121

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

This is because of int overflow

remove casting and change return type from int to long

Current time in millis is ~1404942594081
Start time in millis is 946713600000

difference is ~

458229057398

casting it to integer value would overflow to

-1332420848

because max value for int is 2147483647

Just keep it in long

if you want to keep it to int this code will work upto Jan 19 2068

jmj
  • 237,923
  • 42
  • 401
  • 438
1

Jan 1, 2000 was at roughly 946,684,800,000 milliseconds, which is about 458,257,821,000 millis ago. This number is larger than the number of milliseconds an int can hold (2,147,483,647). This means your cast to int will result in integer overflow, which is what you're seeing.

Community
  • 1
  • 1
yshavit
  • 42,327
  • 7
  • 87
  • 124