也许更优雅的东西是可能的,但以下似乎工作正常。
不过,请务必查看 DST 测试,因为您可能会发现将 05:00 和 07:00 作为 6 小时持续时间的开始令人惊讶。在这些情况下,也许这不是您真正想要的。
public class TruncationTest {
static ZonedDateTime truncateToDuration(ZonedDateTime zonedDateTime, Duration duration) {
ZonedDateTime startOfDay = zonedDateTime.truncatedTo(ChronoUnit.DAYS);
long millisSinceStartOfDay = zonedDateTime.toInstant().toEpochMilli() - startOfDay.toInstant().toEpochMilli();
long millisToSubtract = millisSinceStartOfDay % duration.toMillis();
return zonedDateTime.truncatedTo(ChronoUnit.MILLIS).minus(millisToSubtract, ChronoUnit.MILLIS);
}
@Test
public void test() {
Duration oneMinute = Duration.of(1, ChronoUnit.MINUTES);
Duration fiveMinutes = Duration.of(5, ChronoUnit.MINUTES);
Duration fifteenMinutes = Duration.of(15, ChronoUnit.MINUTES);
Duration oneHour = Duration.of(1, ChronoUnit.HOURS);
Duration sixHours = Duration.of(6, ChronoUnit.HOURS);
Duration oneDay = Duration.of(1, ChronoUnit.DAYS);
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime now = LocalDateTime.parse("2018-02-24T10:37:07.123").atZone(zone);
assertEquals(LocalDateTime.parse("2018-02-24T10:37:00.000").atZone(zone),
truncateToDuration(now, oneMinute));
assertEquals(LocalDateTime.parse("2018-02-24T10:35:00.000").atZone(zone),
truncateToDuration(now, fiveMinutes));
assertEquals(LocalDateTime.parse("2018-02-24T10:30:00.000").atZone(zone),
truncateToDuration(now, fifteenMinutes));
assertEquals(LocalDateTime.parse("2018-02-24T10:00:00.000").atZone(zone),
truncateToDuration(now, oneHour));
assertEquals(LocalDateTime.parse("2018-02-24T06:00:00.000").atZone(zone),
truncateToDuration(now, sixHours));
assertEquals(LocalDateTime.parse("2018-02-24T00:00:00.000").atZone(zone),
truncateToDuration(now, oneDay));
}
@Test
public void testOnFirstDST() {
Duration oneMinute = Duration.of(1, ChronoUnit.MINUTES);
Duration fiveMinutes = Duration.of(5, ChronoUnit.MINUTES);
Duration fifteenMinutes = Duration.of(15, ChronoUnit.MINUTES);
Duration oneHour = Duration.of(1, ChronoUnit.HOURS);
Duration sixHours = Duration.of(6, ChronoUnit.HOURS);
Duration oneDay = Duration.of(1, ChronoUnit.DAYS);
ZoneId zone = ZoneId.of("Europe/Paris");
ZonedDateTime now = LocalDateTime.parse("2018-03-25T10:37:07.123").atZone(zone);
assertEquals(LocalDateTime.parse("2018-03-25T10:37:00.000").atZone(zone),
truncateToDuration(now, oneMinute));
assertEquals(LocalDateTime.parse("2018-03-25T10:35:00.000").atZone(zone),
truncateToDuration(now, fiveMinutes));
assertEquals(LocalDateTime.parse("2018-03-25T10:30:00.000").atZone(zone),
truncateToDuration(now, fifteenMinutes));
assertEquals(LocalDateTime.parse("2018-03-25T10:00:00.000").atZone(zone),
truncateToDuration(now, oneHour));
assertEquals(LocalDateTime.parse("2018-03-25T07:00:00.000").atZone(zone),
truncateToDuration(now, sixHours));
assertEquals(LocalDateTime.parse("2018-03-25T00:00:00.000").atZone(zone),
truncateToDuration(now, oneDay));
}
@Test
public void testOnSecondDST() {
Duration oneMinute = Duration.of(1, ChronoUnit.MINUTES);
Duration fiveMinutes = Duration.of(5, ChronoUnit.MINUTES);
Duration fifteenMinutes = Duration.of(15, ChronoUnit.MINUTES);
Duration oneHour = Duration.of(1, ChronoUnit.HOURS);
Duration sixHours = Duration.of(6, ChronoUnit.HOURS);
Duration oneDay = Duration.of(1, ChronoUnit.DAYS);
ZoneId zone = ZoneId.of("Europe/Paris");
ZonedDateTime now = LocalDateTime.parse("2018-10-28T10:37:07.123").atZone(zone);
assertEquals(LocalDateTime.parse("2018-10-28T10:37:00.000").atZone(zone),
truncateToDuration(now, oneMinute));
assertEquals(LocalDateTime.parse("2018-10-28T10:35:00.000").atZone(zone),
truncateToDuration(now, fiveMinutes));
assertEquals(LocalDateTime.parse("2018-10-28T10:30:00.000").atZone(zone),
truncateToDuration(now, fifteenMinutes));
assertEquals(LocalDateTime.parse("2018-10-28T10:00:00.000").atZone(zone),
truncateToDuration(now, oneHour));
assertEquals(LocalDateTime.parse("2018-10-28T05:00:00.000").atZone(zone),
truncateToDuration(now, sixHours));
assertEquals(LocalDateTime.parse("2018-10-28T00:00:00.000").atZone(zone),
truncateToDuration(now, oneDay));
}
}