Joda Time – Calculating the number of seconds between two dates throws an exception.
I use the following code to calculate the difference in seconds between two dates:
long secondsBetween = (Seconds.secondsBetween(new LocalDate("1901-01-01"), new LocalDate()).getSeconds());
But I get the following exception:
08-08 18:21:27.345: E/AndroidRuntime(6972): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.testbdr/com.testbdr.MainActivity}: java.lang.ArithmeticException: Value cannot fit in an int: 3584908800
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2189)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2216)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.ActivityThread.access$600(ActivityThread.java:149)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1305)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.os.Handler.dispatchMessage(Handler.java:99)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.os.Looper.loop(Looper.java:153)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.ActivityThread.main(ActivityThread.java:5000)
08-08 18:21:27.345: E/AndroidRuntime(6972): at java.lang.reflect.Method.invokeNative(Native Method)
08-08 18:21:27.345: E/AndroidRuntime(6972): at java.lang.reflect.Method.invoke(Method.java:511)
08-08 18:21:27.345: E/AndroidRuntime(6972): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:821)
08-08 18:21:27.345: E/AndroidRuntime(6972): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
08-08 18:21:27.345: E/AndroidRuntime(6972): at dalvik.system.NativeStart.main(Native Method)
08-08 18:21:27.345: E/AndroidRuntime(6972): Caused by: java.lang.ArithmeticException: Value cannot fit in an int: 3584908800
08-08 18:21:27.345: E/AndroidRuntime(6972): at org.joda.time.field.FieldUtils.safeToInt(FieldUtils.java:206)
08-08 18:21:27.345: E/AndroidRuntime(6972): at org.joda.time.field.BaseDurationField.getDifference(BaseDurationField.java:141)
08-08 18:21:27.345: E/AndroidRuntime(6972): at org.joda.time.chrono.BaseChronology.get(BaseChronology.java:260)
08-08 18:21:27.345: E/AndroidRuntime(6972): at org.joda.time.base.BaseSingleFieldPeriod.between(BaseSingleFieldPeriod.java:105)
08-08 18:21:27.345: E/AndroidRuntime(6972): at org.joda.time.Seconds.secondsBetween(Seconds.java:124)
08-08 18:21:27.345: E/AndroidRuntime(6972): at com.testbdr.MainActivity.onCreate(MainActivity.java:27)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.Activity.performCreate(Activity.java:5020)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
08-08 18:21:27.345: E/AndroidRuntime(6972): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2153)
08-08 18:21:27.345: E/AndroidRuntime(6972): ... 11 more
Solution
int
As the other answers correctly point out, the problem is that you and Joda-Time are using int
to handle seconds. A 32-bit int can only hold about 68 years of seconds.
If you insist on using seconds to track centuries, you must use 64-bit long
instead of 32-bit int
.
By the way, tracking time in seconds using a 32-bit int in Unix raises a real-world problem, knowing Year 2038 problem
Seconds are not used for long spans
As others have suggested, it is unusual to use seconds to track such a long span of time. If possible, you may want to reconsider that premise.
Another option: ISO 8601 standard provides Durations PnYnMnDTnHnMnMnS
format, representing year, month, day, etc. Joda-Time knows how to parse and generate such strings(Period and Duration class). Although Joda-Time can only handle int
numbers of seconds, it can handle larger seconds when rendered as strings in this ISO 8601 format, as shown in the following code example (PT3584908800S
).
Milliseconds
Joda-Time tracks a count internally – epoch using milliseconds. Joda-Time provides a way to access these milliseconds as long
values.
I usually recommend doing datetime work in milliseconds again. But in your case, it makes sense to convert to seconds as needed.
The beginning of the day
To calculate milliseconds, we need to use DateTime instead LocalDate .
Get into the habit of calling the method withTimeAtStartOfDay
to get the first moment of the day. This time is usually 00:00:00
, but not always because of daylight saving time or other unusual circumstances.
Time zone
Even for LocalDate
, the time zone is critical. The date (and the first minute of the day) is determined by the time zone. A new day in Paris comes earlier than Montreal.
If you omit the time zone, the JVM’s current default time zone is used. It is usually best to clearly state and specify the desired time zone. I suspect it makes sense to use UTC for your purposes.
Duration
Joda-Time provides Duration class representing a span of time that is not related to the timeline (history of the universe).
Sample code
Sample code using Joda-Time 2.4.
DateTime history = new DateTime( "1901-01-01", DateTimeZone.UTC ).withTimeAtStartOfDay(); Technically, the call to withTimeAtStartOfDay is not necessary here as Joda-Time defaults to that for parsing a date-only string. But the call is a good habit and makes clear out intention.
DateTime today = new DateTime( DateTimeZone.UTC ).withTimeAtStartOfDay();
Duration duration = new Duration( history, today );
long millis = duration.getMillis(); Use a long, not an int.
long seconds = ( millis / 1000L ); Use a long, not an int. Maybe use BigDecimal or BigInteger if you want rounding.
Dump to the console.
System.out.println( "history: " + history );
System.out.println( "today: " + today );
System.out.println( "duration: " + duration );
System.out.println( "millis: " + millis );
System.out.println( "seconds: " + seconds );
Runtime.
history: 1901-01-01T00:00:00.000Z
today: 2014-08-08T00:00:00.000Z
duration: PT3584908800S
millis: 3584908800000
seconds: 3584908800
When turning in the other direction, either:
- Pass long seconds to a static method constructor of Duration .