0

I'm using Joda-Time Duration to get the duration between two DateTime:

DateTime startTimeDate = new DateTime(startTimeDateInLong, DateTimeZone.UTC);
DateTime endTimeDate = new DateTime(endTimeDateInLong, DateTimeZone.UTC);

Duration duration = new Duration(startTimeDate, endTimeDate);

I want to convert per following rules:

0-60 seconds --> 1 minute ..
1.5 - 1 hour --> 1 hour
1.6 hour - 2 hour --> 2 hour

I am using duration.toStandardHours(), but for 96 minutes it gives 1 hour instead I want 2 hours.

Novice User
  • 3,552
  • 6
  • 31
  • 56
  • Possible duplicate of [How to round DateTime of Joda library to the nearest X minutes?](https://stackoverflow.com/questions/11222316/how-to-round-datetime-of-joda-library-to-the-nearest-x-minutes) – Lucas Oliveira Oct 02 '17 at 23:32

2 Answers2

1

The Duration class doesn't round the values the way you want. Even if you get a duration of 1 hour, 59 minutes, 59 seconds and 999 milliseconds, toStandardHours() will return 1.

To get the results you want, you must get the total in seconds, and then manipulate this value accordingly. You can use the java.math.BigDecimal class, with a java.math.RoundingMode to control how the values are rounded:

// 96-minutes duration
Duration duration = new Duration(96 * 60 * 1000);
long secs = duration.toStandardSeconds().getSeconds();
if (secs >= 3600) { // more than 1 hour
    BigDecimal secondsPerHour = new BigDecimal(3600);
    int hours = new BigDecimal(secs).divide(secondsPerHour, RoundingMode.HALF_DOWN).intValue();

    System.out.println(hours + " hour" + (hours > 1 ? "s" : "")); // 2 hours
} else {
    int mins;
    if (secs == 0) { // round zero seconds to 1 minute
        mins = 1;
    } else {
        // always round up (1-59 seconds = 1 minute)
        BigDecimal secondsPerMin = new BigDecimal(60);
        mins = new BigDecimal(secs).divide(secondsPerMin, RoundingMode.UP).intValue();
    }
    System.out.println(mins + " minute" + (mins > 1 ? "s" : ""));
}

This will print 2 hours for a 96-minutes duration, 1 minute for durations between 0 and 60 seconds, and so on.

To get the difference in seconds, you can also use the org.joda.time.Seconds class:

long secs = Seconds.secondsBetween(startTimeDate, endTimeDate).getSeconds();

Java new Date/Time API

Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".

If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.

If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

First, to get the corresponding instant from an epoch milliseconds value, you can use the Instant class (no need to set timezone to UTC, as Instant represents an UTC instant). Then, to calculate the difference, you can use a Duration:

long startTimeDateInLong = // long millis value
long endTimeDateInLong = // long millis value

// get the corresponding Instant
Instant start = Instant.ofEpochMilli(startTimeDateInLong);
Instant end = Instant.ofEpochMilli(endTimeDateInLong);

// get the difference in seconds
Duration duration = Duration.between(start, end);
long secs = duration.getSeconds();

// perform the same calculations as above (with BigDecimal)

You can also use a ChronoUnit to get the difference in seconds:

long secs = ChronoUnit.SECONDS.between(start, end);
0

The only way I could find was to get the time in smaller unit first then convert to unit of desire and round it. So, for example, for the use case mentioned, the way to get rounded minutes would be something like this:

public Minutes getRoundedMinutes(DateTime dateTime1, DateTime dateTime2) {
    return Minutes.minutes(
            (int) round((double) secondsBetween(dateTime1, dateTime2).getSeconds() / Minutes.ONE.toStandardSeconds().getSeconds()));
}

@Test
public void should_round_minutes() throws Exception {
    DateTime dateTime1 = new DateTime(2018, 1, 1, 1, 0, 0);
    DateTime dateTime2 = new DateTime(2018, 1, 1, 1, 0, 29);
    DateTime dateTime3 = new DateTime(2018, 1, 1, 1, 0, 30);
    DateTime dateTime4 = new DateTime(2018, 1, 1, 1, 1, 1);
    DateTime dateTime5 = new DateTime(2018, 1, 1, 1, 1, 31);

    assertThat(getRoundedMinutes(dateTime1, dateTime2).getMinutes()).isEqualTo(0);
    assertThat(getRoundedMinutes(dateTime1, dateTime3).getMinutes()).isEqualTo(1);
    assertThat(getRoundedMinutes(dateTime1, dateTime4).getMinutes()).isEqualTo(1);
    assertThat(getRoundedMinutes(dateTime1, dateTime5).getMinutes()).isEqualTo(2);
}