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]