71

Given today's time e.g. 2:24PM, how do I get it to round to 2:30PM?

Similarly if the time was 2:17PM, how do I get it to round to 2:15PM?

17 Answers 17

103

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);
10
  • uh i just construct it from the calendar class
    – lock
    Commented Aug 24, 2010 at 6:36
  • 2
    This method will floor the minutes to low closest quarter and you need to add some check if it was rounded to the next hour so that you need to update the hour too... Commented Aug 24, 2010 at 6:38
  • 1
    The current version fixes these issues Commented Aug 24, 2010 at 7:38
  • (unroundedMinutes+8) % 15 is simpler, and just set, don't add.
    – user207421
    Commented Aug 24, 2010 at 7:41
  • @EJP You are right, but I think on the semantic level setting the time to 10:60 is an awful thing to do, even if I know the calendar class is smart enough to turn it into 11:00 Commented Aug 24, 2010 at 8:03
49

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

3
  • 4
    By the way, LocalDateTime is the wrong class for actual moments, for a point on the timeline. Lacking any zone/offset concept, they are indefinite. Use same logic, but with ZonedDateTime.now(). Commented Mar 18, 2018 at 20:59
  • 5
    By the way, if you want half up, it could be (15 * (time.getMinute() + 7.5) / 15). Commented Feb 4, 2019 at 21:54
  • Not an answer to a question. 2.24 will go to 2.15, but the Op wanted 2.24 goes to 2.30 Commented Dec 2, 2023 at 14:27
13

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

2
  • 1
    Semantically well written: variables & calculations are clear 👍 Alternative for half-up: return r < roundIncrement/2 ? floor : ceiling where roundIncrement/2 is the threshold, i.e. half
    – hc_dev
    Commented Apr 8, 2020 at 23:18
  • 1
    I used your answer and adapted it into separate methods to ceil or truncate the input and found a small bug. For anyone with the same goal as me: ceiling in the given answer is always ceiled, even if not necessary. I adapted it the following way: ceiling = input.equals(floor) ? input : ceiling
    – kistlers
    Commented May 17, 2022 at 9:09
10

With the answer above you end up with all kind of interesting code to handle overflows to hours, days etc.

I would use the time in ms since the epoch.

add 7.5minutes or 7.5x60x1000 = 450000

and truncate to a multiple of 900000

new Date(900000 * ((date.getTime() + 450000) / 900000))

This works, because the time where the ms time starts happens to be 00:00:00. And since all time zones in the world change in 15min steps, this does not affect rounding to quarters.

(Oops, I had a 0 too much and forgot some important parentheses : it is still too early)

8
  • Brute force approach, misses things like leap seconds. Although I agree there have to be many leap seconds to make a difference. Commented Aug 24, 2010 at 6:39
  • 4
    You are absolutely right, I did not consider leap seconds. However last time I checked, there was no support for leap seconds in java.util.Date, so people would set their computer clock to match wall time (or an atomic clock) and the JVM would calculated the wrong start of the epoch (off by the number of leap seconds). So in practice this would work and would save a bunch of tricky code, which also must deal with leap seconds. Conclusion : use ntp to keep your computer clock synchronised. Commented Aug 24, 2010 at 6:50
  • 3
    "all time zones in the world change in 30min steps" - this isn't true for all time zones. There are time-zones that use incriments of 15 minutes.
    – B T
    Commented Aug 31, 2012 at 1:53
  • 1
    @BT I did not mean to sound argumentative. I'm just educating myself about this perplexing topic of date-time. Your link leads to an interesting article, thanks. Commented May 31, 2014 at 23:54
  • 1
    I didn't take it that way, and I didn't mean to sound that way either. I've legitimately forgotten the information that caused me to write my original comment
    – B T
    Commented May 31, 2014 at 23:57
10

It's simple, find the number of quaters since 1970 as double, round it and multiply by 15 minutes:

long timeMs = System.System.currentTimeMillis();

long roundedtimeMs = Math.round( (double)( (double)timeMs/(double)(15*60*1000) ) ) * (15*60*1000);

Set your Date or Calendar object with that. Change the time you want mutiple by "n"

1
  • 2
    The parentheses do not match. I think the code should be: long timeMs = System.currentTimeMillis(); long roundedtimeMs = Math.round( (double)timeMs/(15*60*1000) ) * (15*60*1000);
    – jgrocha
    Commented Apr 25, 2017 at 14:19
6

Wonderful post, thank you so much guys! It was exactly what I needed :)

Here's my code based on jour work.

My usecase is "Given it's 11:47 am, I want to set two dates symbolizing the current 5-minutes frame : 11:45 am and 11:50 am"

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

            int modulo = calendar.get(Calendar.MINUTE) % 5;
            if(modulo > 0) {

                calendar.add(Calendar.MINUTE, -modulo);
            }

            myObject.setStartDate(calendar.getTime());

            calendar.add(Calendar.MINUTE, 5);
            myObject.setDueDate(calendar.getTime()); 
1
  • helped me to get a neat code @Iboix. small tweaks and i got my required datetime obj. thanks
    – parishodak
    Commented Oct 14, 2015 at 14:33
4

You can use this simple code...

int mode = min % 15;
if (mode > 15 / 2) {
    min = 15 - mode;
} else {
    min = 0 - mode;
}
cal.add(Calendar.MINUTE, min);
2
  • 1
    thanks for the hint where the 8 comes from (15/2). helped me to implement a version for other roundings like 5 minutes and so on!
    – 5im
    Commented Jun 16, 2014 at 23:21
  • Concise and elegant snippet. Works as refinement to previous solutions, while missing context (where's min, cal defined) and explanation.
    – hc_dev
    Commented Apr 8, 2020 at 23:40
4

One more alternate approach using java Instant api.

Instant instant =  Instant.now();
int intervalInMinutes = 10;
instant.truncatedTo(ChronoUnit.MINUTES).minus(instant.atZone(ZoneId.of("UTC")).getMinute() % (1* intervalInMinutes),ChronoUnit.MINUTES);
4

If you need to round down time to the nearest arbitrary level provided as Duration:

static long truncateTo(long timeEpochMillis, Duration d) {
    long x = timeEpochMillis / d.toMillis();
    return x * d.toMillis();
}
3

java.time

I recommend you do it using the the modern date-time API*:

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        
        // Change it to the applicable ZoneId e.g. ZoneId.of("Asia/Kolkata")
        ZoneId zoneId = ZoneId.systemDefault();
        
        Stream.of(
                        "10:00", 
                        "10:05", 
                        "10:10", 
                        "10:15", 
                        "10:20", 
                        "10:25", 
                        "10:30"
            ).forEach(t -> System.out.println(roundToNearestQuarter(t, zoneId)));
    }

    static ZonedDateTime roundToNearestQuarter(String strTime, ZoneId zoneId) {
        LocalTime time = LocalTime.parse(strTime);
        return LocalDate.now()
                        .atTime(time)
                        .atZone(zoneId)
                        .truncatedTo(ChronoUnit.HOURS)
                        .plusMinutes(15 * Math.round(time.getMinute() / 15.0));
    }
}

Output:

2021-04-02T10:00+01:00[Europe/London]
2021-04-02T10:00+01:00[Europe/London]
2021-04-02T10:15+01:00[Europe/London]
2021-04-02T10:15+01:00[Europe/London]
2021-04-02T10:15+01:00[Europe/London]
2021-04-02T10:30+01:00[Europe/London]
2021-04-02T10:30+01:00[Europe/London]

In case you are looking for just time, use ZonedDateTime#toLocalTime to get the LocalTime from the obtained ZonedDateTime.

Learn more about the modern date-time API from Trail: Date Time.


* The java.util date-time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API. For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

1
  • Btw. this ignores seconds, so since the median between 15'' is 7'' 30''', rounding will be incorrect between 7'' 0''' and 7'' 29''' of each quarter hour.
    – dualed
    Commented Mar 8, 2022 at 14:48
1

Maybe you can use an utility library for manipulating Dates, here for example you have a round method which can be useful for you:

http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/time/DateUtils.html#round%28java.util.Calendar,%20int%29

Here an example in code:

    FastDateFormat formatter = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT;

    Date now = new Date();
    System.out.println("now = " + formatter.format(now));       

    // Get nearest second
    Date nearestSecond = DateUtils.round(now, Calendar.SECOND);
    System.out.println("nearestSecond = " + formatter.format(nearestSecond));

    // Get nearest minute
    Date nearestMinute = DateUtils.round(now, Calendar.MINUTE);
    System.out.println("nearestMinute = " + formatter.format(nearestMinute));

    // Get nearest hour
    Date nearestHour   = DateUtils.round(now, Calendar.HOUR);
    System.out.println("nearestHour = " + formatter.format(nearestHour));
1
  • I know about these, but I don't think there is an out of the box solution here for 15 minute intervals Commented Aug 24, 2010 at 7:57
1
public static Date getCurrentDateTimeWithQuarterRounding() {
    final Calendar calendar = new GregorianCalendar();
    calendar.setTime(new Date());
    calendar.set(Calendar.MILLISECOND, 0);
    calendar.set(Calendar.SECOND, 0);
    final int minutes = calendar.get(Calendar.MINUTE);

    if (minutes < 15) {
        calendar.set(Calendar.MINUTE, 0);
    } else if (minutes >= 45) {
        calendar.set(Calendar.MINUTE, 45);
    } else if (minutes < 30) {
        calendar.set(Calendar.MINUTE, 15);
    } else {
        calendar.set(Calendar.MINUTE, 30);
    }

    return calendar.getTime();
}
0

if you have the minutes you can round them with the following function:
int minutes = i % 15 < 8 ? i / 15 * 15 : (i / 15 + 1) * 15;

1
  • why so complicated? and why divide through float values when what you want is an integer result? Commented Aug 24, 2010 at 6:50
0
minutes = (int) (Math.round(minutes / 15.0) * 15.0);
0

Using some code on I found on Stackoverflow, I have created the following code. It will output for every minute the quarter it will be rounded to.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

DateTimeFormatter Datum_Format = DateTimeFormatter.ofPattern("HH:mm");

LocalDateTime time = LocalDateTime.now();
for(int i=0; i<=59; i++) {
  time = time.withMinute(i);
  int Minute = time.getMinute();
  int Quarter = 15 * (int) Math.round(Minute / 15);
  if (Quarter == 60) { 
    Time2 = time.plusHours(1);
    Time2 = Time2.withMinute(0); 
    LOG.info (Datum_Format.format(time) + "," + Datum_Format.format(Time2));
  }
  else {
    Time2 = time; 
    Time2 = Time2.withMinute(Quarter); 
    LOG.info (Datum_Format.format(time) + "," + Datum_Format.format(Time2));
  }
}

As I output the code to a console, you will have to replace the LOG.info with something like System.out.println.

Result:

2016-08-16 15:14:31 INFO 15:05,15:00
2016-08-16 15:14:31 INFO 15:06,15:00
2016-08-16 15:14:31 INFO 15:07,15:00
2016-08-16 15:14:31 INFO 15:08,15:15
2016-08-16 15:14:31 INFO 15:09,15:15
2016-08-16 15:14:31 INFO 15:10,15:15

1
  • please describe why this answer is better than other answers or what it does different and why Commented Aug 16, 2016 at 13:34
0

Use the following functions to get the minutes rounded to last quarter getRecentQuater():Date, getSysDate_LastQuarterMins("dd.MM.yyyy HH:mm:ss"):String: Converting LocalDateTime to Date

public static Date getRecentQuater() {
    LocalDateTime time = LocalDateTime.now();
    LocalDateTime lastQuarter = time.truncatedTo(ChronoUnit.HOURS).plusMinutes(getLastQuarterValue(time.getMinute()));
    System.out.println("lastQuarter LocalDateTime: " + lastQuarter);

    Date date = Date.from(lastQuarter.atZone(ZoneId.systemDefault()).toInstant());
    System.out.println("lastQuarter Date: " + lastQuarter);
    return date;
}
public static String getSysDate_LastQuarterMins(String dateFormat) {
    Date date = getRecentQuater();
    SimpleDateFormat ft = new SimpleDateFormat (dateFormat);
    String sysDate_RoundMin = ft.format(date);
    System.out.println("getSysDate_LastQuarterMins() LocalDateTime : "+sysDate_RoundMin);
    return sysDate_RoundMin;
}

getSysDate_LastQuarterMins() : Mon Jan 20 17:30:00 CET 2020

public static Date getSysDate_LastQuarterMins() {
    Calendar cal = Calendar.getInstance();
    cal.setTime( new Date(System.currentTimeMillis()) );

    int min = cal.get(Calendar.MINUTE);

    cal.set(Calendar.MINUTE, getLastQuarterValue(min));
    cal.set(Calendar.SECOND, 00);
    Date lastQuarter = cal.getTime();
    System.out.println("getSysDate_LastQuarterMins() Calendar : "+lastQuarter);
    return lastQuarter;
}

You can find the LastQuarter Value Round value from the follwing fucntions, provided with some outputs on function call diaplayLastQuarter_RoundValue(min):

Min: 10, LastQuarter:  0, Round: 15
Min: 24, LastQuarter: 15, Round: 30
Min: 36, LastQuarter: 30, Round: 30
Min: 37, LastQuarter: 30, Round: 30
Min: 38, LastQuarter: 30, Round: 45
Min: 39, LastQuarter: 30, Round: 45
Min: 44, LastQuarter: 30, Round: 45
Min: 57, LastQuarter: 45, Round: 00 [57, 07:45:00, 08:00:00]
public static void diaplayLastQuarter_RoundValue(int minutes) {
    System.out.format("Min: %2d, LastQuarter: %2d, Round: %2d\n",
            minutes, getLastQuarterValue(minutes), getRoundValue(minutes));
}
public static int getLastQuarterValue(int minutes) {
    int min = 15 * (minutes / 15);
    //System.out.println("Min: "+minutes+", getLastQuarterValue : "+ min);
    return min;
}
public static int getRoundValue(int minutes) {
    getLastQuarterValue(minutes);
    int minRound = (int) (Math.round(minutes / 15.0) * 15.0);
    //System.out.println("Min: "+minutes+", getRoundValue : "+minRound);
    return minRound;
}
0

If someone is interested to get the nearest (up or down) five or fifteen interval, I made a function using module that does the job.

public LocalTime roundToTheNearestInterval(LocalTime original, Integer measurementInterval) {
        LocalTime nearest;
        int mod;
        switch (measurementInterval) {
            case 5:
                mod = original.getMinute() % 5;
                nearest = mod >= 3 ?
                        original.truncatedTo(ChronoUnit.HOURS)
                                .plusMinutes((long) 5 * (original.getMinute() / 5) + 5) :
                        original.truncatedTo(ChronoUnit.HOURS)
                                .plusMinutes((long) 5 * (original.getMinute() / 5));
                break;
            case 15:
                mod = original.getMinute() % 15;
                nearest = mod >= 8 ?
                        original.truncatedTo(ChronoUnit.HOURS)
                                .plusMinutes((long) 15 * (original.getMinute() / 15) + 15) :
                        original.truncatedTo(ChronoUnit.HOURS)
                                .plusMinutes((long) 15 * (original.getMinute() / 15));
                break;
            default:
                nearest = original;
        }
        return nearest;
    }

You can try it with this unit test

 @Test
    void roundToTheNearestInterval() {
        //given
        LocalTime originalTime1 = LocalTime.of(6, 31, 15);
        LocalTime originalTime2 = LocalTime.of(19, 13, 42);
        LocalTime originalTime3 = LocalTime.of(6, 37, 11);
        LocalTime originalTime4 = LocalTime.of(19, 40, 34);
        Integer measurementInterval_5min = 5;
        Integer measurementInterval_15min = 15;

        MyService myService = new MyService();

        //when
        LocalTime rounded1_5min = myService.roundToTheNearestInterval(originalTime1, measurementInterval_5min);
        LocalTime rounded2_5min = myService.roundToTheNearestInterval(originalTime2, measurementInterval_5min);

        LocalTime rounded1_15min = myService.roundToTheNearestInterval(originalTime3, measurementInterval_15min);
        LocalTime rounded2_15min = myService.roundToTheNearestInterval(originalTime4, measurementInterval_15min);

        //then
        assertEquals(LocalTime.of(6, 30, 0), rounded1_5min);
        assertEquals(LocalTime.of(19, 15, 0), rounded2_5min);
        assertEquals(LocalTime.of(6, 30, 0), rounded1_15min);
        assertEquals(LocalTime.of(19, 45, 0), rounded2_15min);

    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.