Home > Blockchain >  get the nearest date closest to temp date
get the nearest date closest to temp date

Time:03-14

I have two date fields:

start date (2021-03-07T07:37:15) and end date (2021-03-07T07:37:25) and temp date (2021-03-07T07:37:20) .

I have a list where I have returned 6 records between the start date and end date. How can I filter the records and return the datetime which is closest to temp date?

here in the example records I need to get the 2nd record because the 2021-03-07T07:37:19.999 is closet to the temp date. Example

    2021-03-07T07:37:15.000
    2021-03-07T07:37:19.999
    2021-03-07T07:37:20.000
    2021-03-07T07:37:20.809
    2021-03-07T07:37:22.100
    2021-03-07T07:37:22.814
     public RequiredRecord findCloseRecord(List<RequiredRecord > list,  Date tempDate) {
Date startTime  = new Date(tempDate.getTime() - 5000);  
    Date endTime = new Date(tempDate.getTime()   5000);
          Log.logInfo(this, "Find close record");
          if (list != null && !list.isEmpty()) {
               List<RequiredRecord > filteredRec = list.stream()
                         .filter(rec -> (rec.getLogRecDateTime() != null ))
                         .sorted(Comparator.comparing(RequiredRecord ::getLogRecDateTime))
                         .collect(Collectors.toList());    
          
               if (!ClrUtils.isCollectionEmpty(filteredRec)) {
                    return (RequiredRecord ) (filteredRec);
               }
          }
          return null;
     }

CodePudding user response:

You can provide the comparator to compare the absolute difference of the date in question and temp date.

public RequiredRecord findCloseRecord(List<RequiredRecord> list, Date tempDate) {
        if (list == null || list.isEmpty()) return null;
        
        return list.stream()
                .min(Comparator.comparingLong(d -> Math.abs(tempDate.getTime() - d.getLogRecDateTime().getTime())))
                .orElse(null);
}

I'm not sure what logic you need for start and end date. But I suppose you can alter the logic according to your requirement.

CodePudding user response:

tl;dr

Use LocalDateTime, not Date.

Get the closest value(s) by sorting on the absolute value of he distance in time determined by Duration class.

Comparator.comparing( ( LocalDateTime ldt ) -> Duration.between( ldt , target ).abs() )

Details

As wisely commented by Ole V.V., avoid Date class. Along with Calendar, SimpleDateFormat, and such, these terrible classes were years ago supplanted by the modern java.time classes defined by JSR 310.

Create our sample data. Parse the string inputs as LocalDateTime objects.

List < LocalDateTime > ldts =
        List.of(
                LocalDateTime.parse( "2021-03-07T07:00:00.000" ) ,
                LocalDateTime.parse( "2021-03-07T07:37:15.000" ) ,
                LocalDateTime.parse( "2021-03-07T07:37:19.999" ) ,
                LocalDateTime.parse( "2021-03-07T07:37:20.000" ) ,
                LocalDateTime.parse( "2021-03-07T07:37:20.809" ) ,
                LocalDateTime.parse( "2021-03-07T07:37:22.100" ) ,
                LocalDateTime.parse( "2021-03-07T07:37:22.814" ) ,
                LocalDateTime.parse( "2021-03-07T17:00:00.000" )
        );

Define our target, what you called your "temp date". I will call it target.

LocalDateTime target = LocalDateTime.parse( "2021-03-07T07:37:20" );

In a Comment, you said you wanted to filter /- five seconds.

LocalDateTime tooEarly = target.minusSeconds( 5 );
LocalDateTime tooLate = target.plusSeconds( 5 );

Make a stream of our LocalDateTime objects. Filter for outside our five second range, too early or too late. Then filter out any elements that exactly equal our target, as I surmise from your Question though that requirement is not explicitly stated.

Then we ask the stream to sort its remaining elements, using two criteria.

Our first criterion is the absolute value of the distance in time from our target. The absolute part is because we are willing to consider elements that are on either side of the timeline, earlier or later. This approach of using Duration absolute value comes from that same Comment by Ole V.V.

Our second criterion for sorting is the natural order. I chose this is an arbitrary way to resolve the possibility of two elements being equidistant in time, one ahead and one behind. I chose to go with the earlier one, though you could easily change that.

Lastly we return the very first item of the final sorted stream. The return value is an Optional, as there may be nothing to return depending on the data set.

Optional < LocalDateTime > hit =
        ldts
                .stream()
                .filter( ldt -> ( ! ldt.isBefore( tooEarly ) ) && ldt.isBefore( tooLate ) )
                .filter( ldt -> ( ! ldt.isEqual( target ) ) )
                .sorted(
                        Comparator
                                .comparing(
                                        Function.identity() ,
                                        Comparator.comparing( ( LocalDateTime ldt ) -> Duration.between( ldt , target ).abs() )
                                )
                                .thenComparing( Comparator.naturalOrder() )  // If two elements are equidistant in time (before and after target), go with earlier one as our arbitrary resolution rule.
                )
                .findFirst();

Dump to console.

System.out.println( "ldts = "   ldts );
System.out.println( "hit = "   hit );

ldts = [2021-03-07T07:00, 2021-03-07T07:37:15, 2021-03-07T07:37:19.999, 2021-03-07T07:37:20, 2021-03-07T07:37:20.809, 2021-03-07T07:37:22.100, 2021-03-07T07:37:22.814, 2021-03-07T17:00]

hit = Optional[2021-03-07T07:37:19.999]

  • Related