1

我有一个 ZonedDateTime 存储在一个timeStamp变量中。

我也有一个持续时间barDuration,可以是一分钟、5 分钟、15 分钟、一小时、6 小时或一天。

我正在绘制一个宽度为 的蜡烛图barDuration

如果条形持续时间设置为 1 分钟,我需要这些条形从一分钟开始(例如 9:43:00)。

如果设置为 5 分钟,我需要条形图从 9:45、9:50、9:55 等开始。

如果设置为 15 分钟,则条形图从 9:45、10:00、10:15 等开始。

什么时候barDuration是 1 分钟,我知道我可以使用以下方法计算柱的结束时间:

timeStamp.truncatedTo(ChronoUnit.MINUTES).plus(barDuration)

但这是硬编码的,我想barDuration直接使用 来执行截断。如何才能做到这一点?

编辑:

我在这里找到了一个带有Clocks 的解决方案:

Clock baseClock = Clock.tickMinutes(timeStamp.getZone());
Clock barClock = Clock.tick(baseClock, barDuration);
LocalDateTime now =  LocalDateTime.now(barClock);
LocalDateTime barEndTime =  now.plus(barDuration);

这会起作用,但它不是基于我的timeStamp,而是基于使用LocalDateTime.now()的,这不是我想要的(我不确定我机器上的本地时间是否与我从远程服务器检索的时间戳同步)。

4

2 回答 2

1

也许更优雅的东西是可能的,但以下似乎工作正常。

不过,请务必查看 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));
    }
}
于 2018-02-24T09:22:46.420 回答
1

作为对JB Nizets的一个不起眼的补充,这里的答案是(编辑:)一个 Java 9 版本,它更加时间单位中立。

static ZonedDateTime truncateToDuration(ZonedDateTime zonedDateTime, Duration duration) {
    ZonedDateTime startOfDay = zonedDateTime.truncatedTo(ChronoUnit.DAYS);
    return startOfDay.plus(duration.multipliedBy(
            Duration.between(startOfDay, zonedDateTime).dividedBy(duration)));
}

我还没有彻底测试过,但我希望它也可以四舍五入到例如 500 纳秒。我将自一天开始以来的持续时间除以四舍五入的持续时间,然后立即乘回,以获得该持续时间的整数。

如果你的持续时间没有分成一个小时,或者你的一天从一小时以外的其他时间开始(如果真的发生的话),可能会出现有趣的结果。

于 2018-02-24T09:53:32.630 回答