I am using following code to calculate difference in seconds between two dates:
long secondsBetween = (Seconds.secondsBetween(new LocalDate("1901-01-01"), new LocalDate()).getSeconds());
However I am getting 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
int
As the other answers correctly state, the problem is that you and Joda-Time are using an int
to handle seconds. A 32-bit int can hold only about 68 years worth of seconds.
If you insist on using seconds to track centuries of time, you must use a 64-bit long
rather than an 32-bit int
.
By the way, the use in Unix of a 32-bit int to track time by seconds presents a real-world problem knows an the Year 2038 problem.
As other suggest, using seconds to track such long spans of time is unusual. You may want to rethink that premise, if possible.
One alternative: the ISO 8601 standard offers a Durations format of PnYnMnDTnHnMnS
for number of years, months, days, and so on. Joda-Time knows hows to parse and generate such strings (Period and Duration classes). While Joda-Time can only handle int
numbers for seconds, it can handle larger number of seconds when presented as strings in this ISO 8601 format, as seen in code example below (PT3584908800S
).
Joda-Time internally tracks a count-from-epoch using milliseconds. Joda-Time offers methods to access those milliseconds as long
values.
I normally advise againt working in milliseconds for date-time work. But in your case it makes sense, converting to seconds as needed.
To calculate milliseconds, we'll need to use DateTime rather than LocalDate.
Make a 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 anomalies.
Time zone is crucial even for LocalDate
. The date (and first moment of day) is determined by the time zone. A new day dawns earlier in Paris than it does in Montréal.
If you omit the time zone, the JVM's current default time zone will be used. Generally better to be explicit and specify the desired time zone. I suspect for your purposes, using UTC make sense.
Joda-Time offers the Duration class to represent a span of time untied to the timeline (the history of the Universe).
Example 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 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 );
When run.
history: 1901-01-01T00:00:00.000Z
today: 2014-08-08T00:00:00.000Z
duration: PT3584908800S
millis: 3584908800000
seconds: 3584908800
When going the other direction, either:
long
number of seconds to the static method Duration.standardSeconds
.JodaTime has found the difference, which is 3584908800 seconds. But, it is unable to convert the same to an int, because int cannot hold that big a value.
Do you really have a practical use case for that old date (1-1-1901) ?
Try rerunning the same with a different date, which gives smaller difference. Guess we cannot achieve this using the Seconds.secondsBetween()
method.
Note: The date system of Java/Unix uses 1-1-1970 as starting point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With