Java дата округления

Я хотел бы элегантный способ округлить дату Java вверх или вниз до ближайшей минуты (или секунды, час, день).

Например, дата "ср 25 января 10:36:34 GMT 2012", округленная до ближайшей минуты, будет "ср 25 января 10:37:00 GMT 2012"

7 ответов

Решение

Если вы используете Apache commons-lang, вы можете использовать DateUtils для округления дат:

Date now = new Date();
Date nearestMinute = DateUtils.round(now, Calendar.MINUTE);

Способ сделать это без сторонних библиотек (может быть, не такой элегантный и не такой гибкий): добавить половину поля (для округления по минутам - 30 секунд) и установить это поле и нижние в ноль.

Calendar calendar = ... // assume you already have it with a specified Date value

// 'add' cause changing larger fields if necessary
calendar.add( Calendar.SECOND, 30 ); 
calendar.set( Calendar.SECOND, 0 );
calendar.set( Calendar.MILLISECOND, 0 );

Если текущее значение меньше 30 секунд, минутное значение не изменится при добавлении. В противном случае он будет увеличен на 1. В любом случае, секунды и более низкие значения обнуляются. Итак, у нас есть округление.

Вы можете использовать Apache-Commons' DateUtils,

import org.apache.commons.lang.time.FastDateFormat;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;

FastDateFormat dtFormat = DateFormatUtils.ISO_DATETIME_FORMAT;

Date now = new Date( );
Date nearestHour = DateUtils.round( now, Calendar.HOUR );
Date nearestDay = DateUtils.round( now, Calendar.DAY_OF_MONTH );
Date nearestYear = DateUtils.round( now, Calendar.YEAR );

System.out.println( "Now: " + dtFormat.format( now ) );
System.out.println( "Nearest Hour: " + dtFormat.format( nearestHour ) );
System.out.println( "Nearest Day: " + dtFormat.format( nearestDay ) );
System.out.println( "Nearest Year: " + dtFormat.format( nearestYear )

Для округления вверх / вниз с Apache commons-lang вы можете использовать следующие методы:

  • Чтобы округлить использование DateUtils.ceiling(date, Calendar.HOUR)
  • Чтобы округлить, используйте DateUtils.truncate(date, Calendar.HOUR)
  • Чтобы округлить до ближайшей единицы, используйте DateUtils.round(date, Calendar.HOUR)

См. Примеры:

    SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

    String sDate1 = "11-05-2019 19:01:00";
    Date date1 = formatter.parse(sDate1);
    System.out.println(DateUtils.round(date1, Calendar.HOUR));    // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.truncate(date1, Calendar.HOUR)); // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.ceiling(date1, Calendar.HOUR));  // Sat May 11 20:00:00 MSK 2019

    String sDate2 = "11-05-2019 19:29:00";
    Date date2 = formatter.parse(sDate2);
    System.out.println(DateUtils.round(date2, Calendar.HOUR));    // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.truncate(date2, Calendar.HOUR)); // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.ceiling(date2, Calendar.HOUR));  // Sat May 11 20:00:00 MSK 2019

    String sDate3 = "11-05-2019 19:30:00";
    Date date3 = formatter.parse(sDate3);
    System.out.println(DateUtils.round(date3, Calendar.HOUR));    // Sat May 11 20:00:00 MSK 2019
    System.out.println(DateUtils.truncate(date3, Calendar.HOUR)); // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.ceiling(date3, Calendar.HOUR));  // Sat May 11 20:00:00 MSK 2019

    String sDate4 = "11-05-2019 19:31:00";
    Date date4 = formatter.parse(sDate4);
    System.out.println(DateUtils.round(date4, Calendar.HOUR));    // Sat May 11 20:00:00 MSK 2019
    System.out.println(DateUtils.truncate(date4, Calendar.HOUR)); // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.ceiling(date4, Calendar.HOUR));  // Sat May 11 20:00:00 MSK 2019

    String sDate5 = "11-05-2019 19:59:00";
    Date date5 = formatter.parse(sDate5);
    System.out.println(DateUtils.round(date5, Calendar.HOUR));    // Sat May 11 20:00:00 MSK 2019
    System.out.println(DateUtils.truncate(date5, Calendar.HOUR)); // Sat May 11 19:00:00 MSK 2019
    System.out.println(DateUtils.ceiling(date5, Calendar.HOUR));  // Sat May 11 20:00:00 MSK 2019

Вот ответ Java8+:

public static Instant roundHalfUp(Instant instant, TemporalUnit unit) {
    return instant.plus(unit.getDuration().dividedBy(2))
            .truncatedTo(unit);
}

Чтобы округлить дату до минуты, с преобразованием даты-> мгновенно -> даты:

Date.from(roundHalfUp(date.toInstant(), ChronoUnit.MINUTES))

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

 new Date( ((new Date().getTime() + 500) / 1000) * 1000 )

Лучшее решение - использовать DateUtils от Apache Commons. Однако, если вы хотите избежать их импорта, это будет решением в Java. Не уверен, что это означает "элегантное решение".

/** 
      Takes given date and returns date rounded to nearest  minute
*/
public Date roundToMin(Date d){
    Calendar date = new GregorianCalendar();
    date.setTime(d);
    int deltaMin = date.get(Calendar.SECOND)/30;

    date.set(Calendar.SECOND, 0);
    date.set(Calendar.MILLISECOND, 0);
    date.add(Calendar.MINUTE, deltaMin);

    return date.getTime();
}

Если вам нужно округлить минуты, вы можете сделать это с помощью int minutes = minutes - (minutes % n);

если хотите округлить минуты на 5, тогда n = 5.

Добавлять банку commons-lang просто для округления даты не очень хорошая идея, я предпочитаю эту функцию:

public static Date round(Date d) {
    try {
        SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyyy");
        return sdf.parse(sdf.format(d));
    } catch (ParseException ex) {
        //This exception will never be thrown, because sdf parses what it   formats
        return d;
    }
}
   //How about if we represent the window to round to in Duration
   // like this
   Duration duration = Duration.of(14, ChronoUnit.MINUTES);


   // example of any time
   ZonedDateTime time = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
   long s = duration.getSeconds();
   long st = time.toEpochSecond();
   st = st - st%s;

   time = ZonedDateTime.ofInstant(Instant.ofEpochSecond(st), time.getZone());

   // this trims the nanos and round to secons

В следующей функции "roundBy" можно округлить дату
опционально по секундам / минутам / часам / дням.
Это может быть неточным, но идея ясна, надеюсь

public static final long MILLISECONDS_IN_SECOND = (long)(1000);
public static final long MILLISECONDS_IN_MINUTE = (MILLISECONDS_IN_SECOND * 60);
public static final long MILLISECONDS_IN_HOUR = (MILLISECONDS_IN_MINUTE * 60);
public static final long MILLISECONDS_IN_DAY = (MILLISECONDS_IN_HOUR * 24);
public synchronized static Date roundBy(Date date, long UNIT) {
    long time = date.getTime();
    long timeOutOfUnit= time % UNIT;
    time -= timeOutOfUnit;
    if(timeOutOfUnit >= (UNIT / 2)) {
        time += UNIT;
    }
    return new Date(time);
}
Другие вопросы по тегам