#java #java-8 #localdatetime
#java #java-8 #localdatetime
Вопрос:
Я хочу округлить Instant / LocalDateTime до ближайшего 5-минутного интервала в Java.
Предположим, что время:
Примеры: 2021-02-08T19:02:49.594 Ожидаемый результат: 2021-02-08T19:00:00.000
Предположим, что время: 2021-02-08T19:03:49.594 Ожидаемый результат: 2021-02-08T19:05:00.000
Аналогично, если время: 2021-02-08T19:56:49.594 Ожидаемый результат: 2021-02-08T19:55:00.000
Аналогично, если время: 2021-02-08T19:58:49.594 Ожидаемый результат: 2021-02-08T20:00:00.000
Но если время 2021-02-08T19:55:00.000 или 2021-02-08T19:05:00.000 или 2021-02-08T19:00:00.000, тогда ничего не делайте.
Комментарии:
1. Используйте внешние часы в качестве эталона.
2.
2021-02-08T19:05:00.000
это ближайшая 5-минутная остановка2021-02-08T19:02:49.594
(потому что секунды прошли 30). Какова ваша логика для желания 19:00?
Ответ №1:
Вот решение общего назначения (не только 5 минут):
public static Instant toNearest(Duration interval, Instant instant) {
long intervalMillis = interval.toMillis();
long adjustedInstantMillis = (instant.toEpochMilli() (intervalMillis / 2)) / intervalMillis * intervalMillis;
return Instant.ofEpochMilli(adjustedInstantMillis);
}
public static LocalDateTime toNearest(Duration interval, LocalDateTime dateTime, ZoneId zoneId) {
ZoneRules zoneRules = zoneId.getRules();
Instant instant = toNearest(interval, dateTime.toInstant(zoneRules.getOffset(dateTime)));
return LocalDateTime.ofInstant(instant, zoneRules.getOffset(instant));
}
public static LocalDateTime toNearest(Duration interval, LocalDateTime dateTime) {
return toNearest(interval, dateTime, ZoneId.systemDefault());
}
@Test
public void toNearestRoundsCorrectly() {
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021, 2, 8, 19, 0, 0)))
.isEqualTo(LocalDateTime.of(2021, 2, 8, 19, 0, 0));
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021, 2, 8, 19, 2, 29, 999999999)))
.isEqualTo(LocalDateTime.of(2021, 2, 8, 19, 0, 0));
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021, 2, 8, 19, 2, 30)))
.isEqualTo(LocalDateTime.of(2021, 2, 8, 19, 5, 0));
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021, 2, 8, 19, 5, 0)))
.isEqualTo(LocalDateTime.of(2021, 2, 8, 19, 5, 0));
}
@Test
public void toNearestTreatsDaylightSavingChangesCorrectly() {
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021,3,27, 23,57,30), ZoneId.of("Europe/London")))
.isEqualTo(LocalDateTime.of(2021,3,28, 0,0,0));
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021,3,28, 0,57,29,999999999), ZoneId.of("Europe/London")))
.isEqualTo(LocalDateTime.of(2021,3,28, 0,55,0));
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021,3,28, 0,57,30), ZoneId.of("Europe/London")))
.isEqualTo(LocalDateTime.of(2021,3,28, 2,0,0));
assertThat(toNearest(Duration.ofMinutes(5), LocalDateTime.of(2021,3,28, 2,5,0), ZoneId.of("Europe/London")))
.isEqualTo(LocalDateTime.of(2021,3,28, 2,5,0));
}
Комментарии:
1. прежде всего, спасибо за ваши усилия. получил желаемый результат. Есть ли какой-нибудь способ округлить время только на 5 минут раньше? например: 2021-02-08T19:26:49.594, 2021-02-08T19:27:49.594. 2021-02-08T19:28:49.594, 2021-02-08T19:29:49.594 Ожидаемый результат: 2021-02-08T19:25:00.000
2. Да, просто замените 3-ю строку выше на
long adjustedInstantMillis = instant.toEpochMilli() / intervalMillis * intervalMillis;
(т. Е. Удалите добавление полуинтервала)3. Спасибо, не могли бы вы объяснить эту логическую линию простым однострочником..
4. Он основан на целочисленном делении. При использовании интегрального типа данных, такого как
int
илиlong
, выражениеa / b * b
эквивалентноtruncate(a / b) * b
(при условии, что существует метод truncate, который удаляет дробную часть). Этоa/b*b
приводит к значению, равному a с ближайшим кратным b, которое равно или меньше a. (Это становится немного сложнее с отрицательными числами, поэтому даты ранее 1970 года, которые дают отрицательное значение epochMilli, могут быть неверными.)
Ответ №2:
это должно округлить его соответствующим образом:
long min5 = 1000 * 60 * 5;
String time = "2021-02-08T19:56:49.594";
time = time.replace("T", " "); // cannot deal with T
SimpleDateFormat format = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSS");
try{
Date dt = format.parse(time);
long t = dt.getTime();
long d = t %min5;
if(d > min5/2) { // round up
t = min5 - d;
} else { // round down
t -= d;
}
System.out.println(format.format(new Date(t)));
}catch(ParseException e){
e.printStackTrace();
}
Ответ №3:
В этом коде приведены примеры как для Instant, так и для LocalDateTime.
Instant instant = Instant.now();
long instantLong = instant.toEpochMilli();
long roundedEpochMilli = (instantLong/(60*5*1000) ) * 60 * 5 *1000;
if((instantLong % (60*5*1000))> (30*5*1000))
roundedEpochMill = 60*5*1000;
Instant instantRounded = Instant.ofEpochMilli(roundedEpochMilli);
System.out.println("instant " instant " -- " instantRounded);
LocalDateTime now = LocalDateTime.now();
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);
long nowLong = now.toEpochSecond(currentOffsetForMyZone);
long roundedEpochSecond = (nowLong/(60*5) ) * 60 * 5;
if((nowLong % (60*5))> (30*5))
roundedEpochSecond = 60*5;
LocalDateTime nowRounded = LocalDateTime.ofEpochSecond(roundedEpochSecond, 0, currentOffsetForMyZone);
System.out.println("now " instant " -- " nowRounded);