Java Instant 向上取整至下一秒

7

使用Java Instant类,如何将时间向上舍入到最近的一秒?我不关心是1毫秒、15毫秒还是999毫秒,所有时间都应该向上舍入到下一秒并保留0毫秒。

我想要实现的功能是:

Instant myInstant = ...

myInstant.truncatedTo(ChronoUnit.SECONDS);

但方向相反。


5
你能不能只添加一秒并截断? - Michael Berry
2
@Michael Berry 我的第一个想法是,如果你恰好在整秒开始呢? - Ryan Pelletier
@Nexevis 和添加第二个然后截断一样的问题。 - Ryan Pelletier
5
如果截断后的实例与原始实例不同,就可以通过将它们进行比较并仅在它们不同时添加第二个实例来解决这个特殊情况。 - Michael Berry
2个回答

13

您可以使用.getNano覆盖角落情况,以确保时间不完全在整秒上,并在需要截断值时使用.plusSeconds()添加额外的一秒钟。

    Instant myInstant = Instant.now();
    if (myInstant.getNano() > 0) //Checks for any nanoseconds for the current second (this will almost always be true)
    {
        myInstant = myInstant.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1);
    }
    /* else //Rare case where nanoseconds are exactly 0
    {
        myInstant = myInstant;
    } */

我之所以在else语句中留下它仅仅是为了说明如果确切地等于0纳秒,则不需要执行任何操作,因为没有理由截断空值。

修改: 如果您想要检查时间是否至少比一秒多1毫秒以便进行四舍五入,而非1纳秒,则可以将其与1000000纳秒进行比较,但仍需保留else语句来截断纳秒:

    Instant myInstant = Instant.now();
    if (myInstant.getNano() > 1000000) //Nano to milliseconds
    {
        myInstant = myInstant.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1);
    }
    else
    {
        myInstant = myInstant.truncatedTo(ChronoUnit.SECONDS); //Must truncate the nanoseconds off since we are comparing to milliseconds now.
    }

1
非常好的答案。我发布问题后立即意识到getNano()方法显示了秒的纳秒数(而不是自纪元以来的纳秒数或其他奇怪的东西)。一旦你知道了这一点,该怎么做就很明显了。谢谢! - Ryan Pelletier

6
你可以使用lambda functional programming流方法将此代码改为一行。
添加第二个并截断。 为了覆盖正好在秒上的情况,请检查截断与原始值是否相同,仅当它们不同时才添加一个秒:
Instant myRoundedUpInstant = Optional.of(myInstant.truncatedTo(ChronoUnit.SECONDS))
                .filter(myInstant::equals)
                .orElse(myInstant.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1));

请查看 IdeOne.com上的代码运行结果

Instant.toString(): 2019-07-30T20:06:33.456424Z

myRoundedUpInstant(): 2019-07-30T20:06:34Z

...和...

myInstant.toString(): 2019-07-30T20:05:20Z

myRoundedUpInstant(): 2019-07-30T20:05:20Z

或者,采用稍微不同的方法:

Instant myRoundedUpInstant = Optional.of(myInstant)
        .filter(t -> t.getNano() != 0)
        .map(t -> t.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1))
        .orElse(myInstant);

请查看在IdeOne.com上运行的代码

myInstant.toString(): 2019-07-30T20:09:07.415043Z

myRoundedUpInstant(): 2019-07-30T20:09:08Z

......和......

myInstant.toString(): 2019-07-30T19:44:06Z

myRoundedUpInstant(): 2019-07-30T19:44:06Z

以上当然是在Java 8环境下。如果Optional不适合您,我会留给读者把它拆分成更传统的if/else if语句来练习 :-)


2
很好的例子,从Optional开始生成一个流,我之前不知道这个。谢谢。 - Basil Bourque
这段代码需要添加一个对 Optional::stream() 的调用吗? - Basil Bourque
@BasilBourque 谢谢。这里不需要使用 ::stream,除非我漏掉了什么 - 它只是使用 Optionalfilter()orElse() 方法来有条件地将第二个截断的结果添加到原始结果(仅当截断的瞬间与原始瞬间不同时)。有些人可能会说这是滥用 Optional,但我觉得它比实例化然后可能立即覆盖变量更整洁。 - Michael Berry
1
我现在明白了:Optional :: filterOptional :: orElse。很有趣。 - Basil Bourque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接