Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to round off to the closest hour if its only 5 minutes ahead or behind?

I want to round off an Instant to its closest hour in Java, but only if it's within 5 minutes of the closest hour.

Suppose the time is: 2021-02-08T19:02:49.594
Expected result: 2021-02-08T19:00:00.000

Similarly, if the time is: 2021-02-08T19:56:49.594
Expected result: 2021-02-08T20:00:00.000

But if the time is 2021-02-08T19:54:49.594 or 2021-02-08T19:06:49.594 then do nothing.

like image 459
Dev Avatar asked Feb 11 '21 06:02

Dev


3 Answers

Another option is to use the Duration class to get the difference and change it accordingly:

Instant roundToHourIfWithin5Min(Instant instant) {
  final Instant now = Instant.now();
  final Duration absoluteDifference = Duration.between(now, instant).abs();
  // If the difference is less than 5 min then we round off
  if (absoluteDifference.compareTo(Duration.ofMinutes(5)) < 0) {
    final Instant truncated = now.truncatedTo(ChronoUnit.HOURS);
    if (instant.isBefore(now)) {
      return truncated.plus(1, ChronoUnit.HOURS);
    }
    else {
      return truncated;
    }
  }
  return instant;
}
like image 143
Brett Maeyer Avatar answered Nov 16 '22 01:11

Brett Maeyer


You can simply check the minute part of the date-time and truncate it to hours as per the requirement.

Demo:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

class Main {
    public static void main(String[] args) {
        // Test
        String[] arr = { "2021-02-08T19:02:49.594", "2021-02-08T19:56:49.594", "2021-02-08T19:54:49.594",
                "2021-02-08T19:06:49.594" };

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS");

        for (String s : arr) {
            System.out.println(roundToNearestHour(s).format(dtf));
        }
    }

    static LocalDateTime roundToNearestHour(String str) {
        LocalDateTime ldt = LocalDateTime.parse(str);
        int minute = ldt.getMinute();
        return minute < 5 ? ldt.truncatedTo(ChronoUnit.HOURS)
                : (minute >= 55 ? ldt.plusHours(1).truncatedTo(ChronoUnit.HOURS) : ldt);
    }
}

Output:

2021-02-08T19:00:00.000
2021-02-08T20:00:00.000
2021-02-08T19:54:49.594
2021-02-08T19:06:49.594
like image 42
Arvind Kumar Avinash Avatar answered Nov 16 '22 00:11

Arvind Kumar Avinash


If minute-of-hour is below 5 or over 55

Given that we are dealing with Instant, we know the values are in UTC. And in UTC every hour is 60 minutes long. So we merely need to look at the minute-of-hour to tell if we need to round up or down.

To get the minute-of-hour, we must convert the Instant to the more flexible OffsetDateTime class. We will assign ZoneOffset.UTC constant as the offset, to keep things predictable.

If the minute is under 5, we merely truncate the OffsetDateTime object to the hour.

If the minute is 55 or over, we truncate to hour, but then we add an hour to effectively round up.

In either case of truncation, we extract and return an Instant from our OffsetDateDate truncation-result.

Else we return the same Instant we received, with no rounding.

package work.basil.example;

import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Objects;

public class InstantRounder
{
    public static void main ( String[] args )
    {
        Instant result = InstantRounder. roundInstantToHourIfWithin5Minutes( Instant.now() );
        System.out.println( "result = " + result );
    }

    public static Instant roundInstantToHourIfWithin5Minutes ( Instant instant )
    {
        OffsetDateTime odt = Objects.requireNonNull( instant ).atOffset( ZoneOffset.UTC );
        int minute = odt.getMinute();
        if ( minute < 5 )
        {
            return odt.truncatedTo( ChronoUnit.HOURS ).toInstant();
        } else if ( minute >= 55 ) // Else if in last 5 minutes, truncate to hour, then add an hour, to effectively round up.
        {
            return odt.truncatedTo( ChronoUnit.HOURS ).plus( Duration.ofHours( 1 ) ).toInstant();
        } else  // Else not within five minutes on either end of hour, so do no rounding. 
        {
            return instant;
        }
    }
}
like image 22
Basil Bourque Avatar answered Nov 16 '22 00:11

Basil Bourque