Java Instant round up to the next second
Asked Answered
U

2

9

Using the Java Instant class, how can I round up to the nearest second? I don't care if it is 1 millisecond, 15 milliseconds, or 999 milliseconds, all should round up to the next second with 0 milliseconds.

I basically want,

Instant myInstant = ...

myInstant.truncatedTo(ChronoUnit.SECONDS);

but in the opposite direction.

Ulema answered 30/7, 2019 at 14:36 Comment(4)
Can't you just add a second and truncate?Confiscable
@Michael Berry My first thought. What if you start exactly on a second?Ulema
@Nexevis same issue as adding a second then truncating.Ulema
You can cover that corner case by comparing the truncated instance to the original, and only adding a second if they differ.Confiscable
M
15

You can cover the corner case by using .getNano to make sure the time is not exactly even on the second and then add the extra second using .plusSeconds() when there is a value to truncate.

    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;
    } */

I left in the else statement just to demonstrate no operations need to be done if it is exactly 0 nanoseconds, because there is no reason to truncate nothing.

EDIT: If you want to check if the time is at least 1 millisecond over a second in order to round up, instead of 1 nanosecond you can then compare it to 1000000 nanoseconds but leave the else statement in to truncate the nanoseconds:

    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.
    }
Metapsychology answered 30/7, 2019 at 14:50 Comment(1)
Great answer. Right after I posted my question I realized the getNano() method showed the nanos of the second (instead of nanos since epoch or something weird). Pretty obvious what to do once you know that. Thanks!Ulema
C
6

You can use the lambda functional programming streams approach to make this a one liner.

Add a second and truncate. To cover the corner case of being exactly on a second, check the truncated to the original, and only add a second if they differ:

Instant myRoundedUpInstant = Optional.of(myInstant.truncatedTo(ChronoUnit.SECONDS))
                .filter(myInstant::equals)
                .orElse(myInstant.truncatedTo(ChronoUnit.SECONDS).plusSeconds(1));

See this code run line at IdeOne.com.

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

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

…and…

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

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

Or alternatively, with a slightly different approach:

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

See this code run live at IdeOne.com.

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

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

…and…

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

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

Above is of course in Java 8 land. I'll leave it as an exercise to the reader to split that out into the more traditional if/else if Optional isn't your thing :-)

Confiscable answered 30/7, 2019 at 15:1 Comment(4)
Nifty example of starting with an Optional to firm a Stream. I didn’t know about that. Thanks.Mabellemable
Does this code need to add a call to Optional::stream()?Mabellemable
@BasilBourque Thanks. No ::stream needed here unless I'm missing something - it's just using the Optional's filter() and orElse() methods to conditionally add a second to the truncated result (only if the truncated instant differs from the original instant.) Some might say it's abusing an Optional, but I find it neater than instantiating and then potentially immediately overwriting a variable.Confiscable
I see now: Optional :: filter and Optional :: orElse. Interesting.Mabellemable

© 2022 - 2024 — McMap. All rights reserved.