简体   繁体   中英

Joda Time - Calculation of seconds between two dates throws an exception.

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 .

Seconds Not Used For Long Spans Of Time

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 ).

Milliseconds

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.

Start of Day

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

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.

Duration

Joda-Time offers the Duration class to represent a span of time untied to the timeline (the history of the Universe).

Example Code

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:

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.

getSeconds is defined as returning an int :

public int getSeconds()

so, even if you store its result in a long, it can only return a quantity < MAXINT or roughly 64 years. So in short, if you really need to calculate the number of seconds in century-sized time periods, this method is not for you.

As your example measures the distance in seconds between two dates , why not use Hours.getHours() and multiply the result by 3600? The result should be pretty close to what you want (except for leap seconds I think), and give you a usable range that's big enough for your needs.

Use the Seconds class

DateTime now = DateTime.now();
DateTime dateTime = now.plusMinutes(10);
Seconds seconds = Seconds.secondsBetween(now, dateTime);
System.out.println(seconds.getSeconds());

EDIT:

Int type ranges from –2,147,483,648 to 2,147,483,647. Your value give you something like 3,565,987,200. So this method will not work for you

Workaround:

Date d1 = ...;
Date d2 = ...;
long seconds = (d2.getTime()-d1.getTime())/1000;

Have a look at http://joda-time.sourceforge.net/apidocs/org/joda/time/Seconds.html#secondsBetween(org.joda.time.ReadableInstant , org.joda.time.ReadableInstant)

long secondsBetween = (Seconds.secondsBetween(new LocalDate("1901-01-01"), new LocalDate()).getSeconds());

I think LocalDate()).getSeconds() is wrong, because you want to get the seconds between from two ReadableInstant. Try Seconds.secondsBetween with 2 LocalDates

Look here: http://www.leveluplunch.com/java/examples/number-of-seconds-between-two-dates/

  // start day is 1 day in the past
DateTime startDate = new DateTime().minusDays(1);
DateTime endDate = new DateTime();

Seconds seconds = Seconds.secondsBetween(startDate, endDate);

int secondsInDay = seconds.getSeconds();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM