How to round time to the nearest quarter hour in java?

Solution 1:

Rounding

You will need to use modulo to truncate the quarter hour:

Date whateverDateYouWant = new Date();
Calendar calendar = Calendar.getInstance();
calendar.setTime(whateverDateYouWant);

int unroundedMinutes = calendar.get(Calendar.MINUTE);
int mod = unroundedMinutes % 15;
calendar.add(Calendar.MINUTE, mod < 8 ? -mod : (15-mod));

As pointed out by EJP, this is also OK (replacement for the last line, only valid if the calendar is lenient):

calendar.set(Calendar.MINUTE, unroundedMinutes + mod);

Improvements

If you want to be exact, you will also have to truncate the smaller fields:

calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);

You can also use DateUtils.truncate() from Apache Commons / Lang to do this:

calendar = DateUtils.truncate(calendar, Calendar.MINUTE);

Solution 2:

If you just want to round down this is a more readable version using Java Time API:

LocalDateTime time = LocalDateTime.now();
LocalDateTime lastQuarter = time.truncatedTo(ChronoUnit.HOURS)
                                .plusMinutes(15 * (time.getMinute() / 15));

output:

2016-11-04T10:58:10.228

2016-11-04T10:45:00

Solution 3:

A commented implementation for Java 8. Accepts arbitrary rounding units and increments:

 public static ZonedDateTime round(ZonedDateTime input, TemporalField roundTo, int roundIncrement) {
    /* Extract the field being rounded. */
    int field = input.get(roundTo);

    /* Distance from previous floor. */
    int r = field % roundIncrement;

    /* Find floor and ceiling. Truncate values to base unit of field. */
    ZonedDateTime ceiling = 
        input.plus(roundIncrement - r, roundTo.getBaseUnit())
        .truncatedTo(roundTo.getBaseUnit());

    ZonedDateTime floor = 
        input.plus(-r, roundTo.getBaseUnit())
        .truncatedTo(roundTo.getBaseUnit());

    /*
     * Do a half-up rounding.
     * 
     * If (input - floor) < (ceiling - input) 
     * (i.e. floor is closer to input than ceiling)
     *  then return floor, otherwise return ceiling.
     */
    return Duration.between(floor, input).compareTo(Duration.between(input, ceiling)) < 0 ? floor : ceiling;
  }

Source: myself