Как округлить время до ближайшей четверти часа в java?

Учитывая сегодняшнее время, например. 14:24, как мне округлить до 14:30?

Точно так же, если время было 14:17, как мне округлить его до 14:15?


person lock    schedule 24.08.2010    source источник


Ответы (15)


Округление

Вам нужно будет использовать модуль, чтобы усечь четверть часа:

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

Как указывает EJP, это тоже нормально (замена последней строки действительна, только если календарь мягкий):

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

Улучшения

Если вы хотите быть точным, вам также придется обрезать меньшие поля:

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

Вы также можете использовать DateUtils.truncate() с Apache Commons/Lang на сделай это:

calendar = DateUtils.truncate(calendar, Calendar.MINUTE);
person Sean Patrick Floyd    schedule 24.08.2010
comment
я просто создаю его из класса календаря - person lock; 24.08.2010
comment
Этот метод будет уменьшать минуты до ближайшего ближайшего квартала, и вам нужно добавить некоторую проверку, если он был округлен до следующего часа, чтобы вам также нужно было обновить час... - person Ido Weinstein; 24.08.2010
comment
Текущая версия исправляет эти проблемы - person Sean Patrick Floyd; 24.08.2010
comment
(unroundedMinutes+8) % 15 проще, и просто ставить, не добавлять. - person user207421; 24.08.2010
comment
@EJP Вы правы, но я думаю, что на семантическом уровне установка времени на 10:60 - это ужасно, даже если я знаю, что класс календаря достаточно умен, чтобы превратить его в 11:00 - person Sean Patrick Floyd; 24.08.2010
comment
Я тоже. Я ничего не сказал против. - person user207421; 24.08.2010
comment
@lock Мне потребовался целый день, чтобы понять, что ваш комментарий был направлен в мою сторону, извините. Обновил мой ответ. - person Sean Patrick Floyd; 24.08.2010
comment
@seanizer: извините, что не адресовал это вам, хе-хе, спасибо за обновление :D - person lock; 25.08.2010
comment
Так что этот ответ не полный. А. Откуда берется экземпляр календаря? B. Как вы получаете конкретную дату там? - person B T; 31.08.2012
comment
@BT экземпляр календаря взят из первого списка, и конкретная дата не входит в рамки вопроса. - person Sean Patrick Floyd; 31.08.2012

Если вы просто хотите округлить, это более удобочитаемая версия с использованием Java Time API:

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

выход:

2016-11-04T10:58:10.228

2016-11-04T10:45:00

person Dirk    schedule 04.11.2016
comment
Кстати, LocalDateTime — это неправильный класс для реальных моментов, для точки на временной шкале. В отсутствие какой-либо концепции зоны/смещения они неопределенны. Используйте ту же логику, но с ZonedDateTime.now(). - person Basil Bourque; 18.03.2018
comment
Кстати, если вы хотите увеличить половину, это может быть (15 * (time.getMinute() + 7,5) / 15). - person Shengfeng Li; 05.02.2019

Закомментированная реализация для Java 8. Принимает произвольные единицы округления и приращения:

 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;
  }

Источник: сам

person Dev    schedule 24.05.2016
comment
Написано семантически хорошо: переменные и расчеты понятны ???? Альтернатива полувыбору: return r < roundIncrement/2 ? floor : ceiling, где roundIncrement/2 — порог, т.е. половина - person hc_dev; 09.04.2020

Все просто, находим количество четвертей с 1970 года как двойное, округляем и умножаем на 15 минут:

long timeMs = System.System.currentTimeMillis();

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

Установите свой объект даты или календаря с этим.

person Lindhardt    schedule 04.01.2012
comment
Скобки не совпадают. Я думаю, что код должен быть: long timeMs = System.currentTimeMillis(); long roundedtimeMs = Math.round( (double)timeMs/(15*60*1000) ) * (15*60*1000); - person jgrocha; 25.04.2017

С ответом выше вы получите весь интересный код для обработки переполнения часов, дней и т. д.

Я бы использовал время в мс с эпохи.

добавить 7,5 минут или 7,5x60x1000 = 450000

и обрезать до кратного 900000

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

Это работает, потому что время начала отсчета мс равно 00:00:00. А поскольку все часовые пояса в мире меняются с шагом в 15 минут, это не влияет на округление до четверти.

(К сожалению, у меня было слишком много 0 и я забыл некоторые важные скобки: еще слишком рано)

person Peter Tillemans    schedule 24.08.2010
comment
Подход грубой силы, пропускает такие вещи, как високосные секунды. Хотя я согласен, что должно быть много високосных секунд, чтобы иметь значение. - person Sean Patrick Floyd; 24.08.2010
comment
Вы абсолютно правы, я не учитывал високосные секунды. Однако в прошлый раз, когда я проверял, в java.util.Date не было поддержки високосных секунд, поэтому люди устанавливали свои компьютерные часы в соответствии со временем стены (или атомными часами), а JVM вычисляла неправильное начало эпохи (выключено). по количеству високосных секунд). Так что на практике это сработает и сэкономит кучу сложного кода, который также должен иметь дело с високосными секундами. Вывод: используйте ntp для синхронизации часов вашего компьютера. - person Peter Tillemans; 24.08.2010
comment
все часовые пояса в мире меняются с шагом в 30 минут - это не верно для всех часовых поясов. Есть часовые пояса, в которых используются 15-минутные интервалы. - person B T; 31.08.2012
comment
@BT Согласно этой статье в Википедии, все смещения часовых поясов составляют 60, 45 или 30 минут. . - person Basil Bourque; 30.05.2014
comment
@BasilBourque Я признаю, что не уверен в деталях, но я мог иметь в виду, что некоторые часовые пояса в конечном итоге отличаются от GMT на 15 минут (что может вызвать 45-минутный шаг). Возможно, это также то, что означает этот источник: geography.about.com/od/culturegeography/ a/offsettimezones.htm - person B T; 01.06.2014
comment
@BT Я не хотел звучать спорно. Я просто изучаю эту запутанную тему даты и времени. Ваша ссылка ведет на интересную статью, спасибо. - person Basil Bourque; 01.06.2014
comment
Я не воспринял это таким образом, и я не хотел так звучать. Я законно забыл информацию, которая заставила меня написать свой первоначальный комментарий - person B T; 01.06.2014
comment
Я обновил свой ответ, чтобы использовать 15-минутную разницу в часовых поясах, спасибо за наблюдение. - person Peter Tillemans; 14.06.2014

Замечательный пост, спасибо большое ребята! Это было именно то, что мне было нужно :)

Вот мой код, основанный на моей работе.

Мой вариант использования: "Поскольку сейчас 11:47, я хочу установить две даты, символизирующие текущий 5-минутный кадр: 11:45 и 11:50"

            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()); 
person lboix    schedule 30.05.2014
comment
помог мне получить аккуратный код @Iboix. небольшие настройки, и я получил требуемый объект даты и времени. Спасибо - person parishodak; 14.10.2015

Вы можете использовать этот простой код...

int mode = min % 15;
if (mode > 15 / 2) {
    min = 15 - mode;
} else {
    min = 0 - mode;
}
cal.add(Calendar.MINUTE, min);
person Kandha    schedule 24.08.2010
comment
спасибо за подсказку, откуда взялась 8 (15/2). помог мне реализовать версию для других округлений, таких как 5 минут и так далее! - person 5im; 17.06.2014
comment
Лаконичный и элегантный фрагмент. Работает как уточнение предыдущих решений, но без контекста (где определены min, cal) и объяснения. - person hc_dev; 09.04.2020

Еще один альтернативный подход с использованием 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);
person Rakesh SKadam    schedule 07.10.2019

java.время

Я рекомендую вам сделать это, используя современную дату и время. 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));
    }
}

Вывод:

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]

Если вам нужно только время, используйте ZonedDateTime#toLocalTime чтобы получить LocalTime из полученных ZonedDateTime.

Узнайте больше о современном API даты и времени из Trail: Date Time< /а>.


* java.util API даты и времени и их API форматирования SimpleDateFormat устарели и подвержены ошибкам. Рекомендуется полностью отказаться от их использования и перейти на современный API даты и времени. По какой-либо причине, если вам нужно придерживаться Java 6 или Java 7, вы можете использовать ThreeTen-Backport, который переносит большую часть функций java.time на Java 6 и 7. Если вы работаете над проектом Android и вашим уровнем Android API по-прежнему не совместим с Java-8, проверьте Java 8+ API, доступные через дешугаринг и Как использовать ThreeTenABP в проекте Android.

person Arvind Kumar Avinash    schedule 02.04.2021

Возможно, вы можете использовать служебную библиотеку для управления датами, например, здесь у вас есть круглый метод, который может быть вам полезен:

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

Вот пример в коде:

    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));
person Torres    schedule 24.08.2010
comment
Я знаю об этом, но я не думаю, что здесь есть готовое решение для 15-минутных интервалов. - person Sean Patrick Floyd; 24.08.2010

Если вам нужно округлить время до ближайшего произвольного уровня, указанного как Duration:

static long truncateTo(long timeEpochMillis, Duration d) {
    long x = timeEpochMillis / d.toMillis();
    return x * d.toMillis();
}
person Sergey Shcherbakov    schedule 02.04.2021

если у вас есть минуты, вы можете округлить их с помощью следующей функции:
int minutes = i % 15 < 8 ? i / 15 * 15 : (i / 15 + 1) * 15;

person christian    schedule 24.08.2010
comment
почему так сложно? и зачем делить значения с плавающей запятой, когда вы хотите получить целочисленный результат? - person Sean Patrick Floyd; 24.08.2010

Используя некоторый код, который я нашел в Stackoverflow, я создал следующий код. Он будет выводить за каждую минуту квартал, до которого он будет округлен.

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));
  }
}

Поскольку я вывожу код на консоль, вам придется заменить LOG.info чем-то вроде System.out.println.

Результат:

2016-08-16 15:14:31 ИНФО 15:05,15:00
2016-08-16 15:14:31 ИНФО 15:06,15:00
2016-08-16 15:14 :31 ИНФО 15:07,15:00
2016-08-16 15:14:31 ИНФО 15:08,15:15
2016-08-16 15:14:31 ИНФО 15:09,15 :15
2016-08-16 15:14:31 ИНФОРМАЦИЯ 15:10,15:15

person Xander    schedule 16.08.2016
comment
пожалуйста, опишите, почему этот ответ лучше других ответов или чем он отличается и почему - person Florian Koch; 16.08.2016

Используйте следующие функции, чтобы округлить минуты до последнего квартала getRecentQuater():Date, getSysDate_LastQuarterMins("dd.MM.yyyy HH:mm:ss"):String: Преобразование 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;
}

Вы можете найти значение LastQuarter Value Round из следующих функций, снабженных некоторыми выходными данными при вызове функции 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;
}
person Yash    schedule 20.01.2020

Если кому-то интересно получить ближайший (вверх или вниз) интервал пять или пятнадцать, я сделал функцию, используя модуль, который выполняет эту работу.

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;
    }

Вы можете попробовать это с помощью этого модульного теста

 @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);

    }
person rortegat    schedule 16.07.2021