Home > Software engineering >  Java find current time is between Map of Local Date times
Java find current time is between Map of Local Date times

Time:03-30

Currently i have a map with the below entries ,

Map<LocalDateTime, Integer> resultMap = new HashMap<LocalDateTime, Integer>();
 //Put localdatetime and 30mins split indicator

  for(Map.Entry<LocalDateTime, Integer> entry: resultMap.entrySet()) {
      System.out.println(entry.getKey()   " : "   entry.getValue());
  }

  2022-03-29T00:30 : 1
  2022-03-29T01:00 : 2
  2022-03-29T01:30 : 3
  2022-03-29T02:00 : 4
  2022-03-29T02:30 : 5
  2022-03-29T03:00 : 6
  2022-03-29T03:30 : 7
  .
  .
  .
  2022-03-29T23:30 : 47
  2022-03-30T00:00 : 48

How do i find if the current LocalDateTime falls in between the times? For example if the current time is 2022-03-29T01:27 need to get the value 3.

Was able to do it with iterator. Any other efficient way to do this?

Iterator keyIterator = resultMap.entrySet().iterator(); 
  Map.Entry<LocalDateTime, Integer> perviousEntry = new AbstractMap.SimpleEntry<LocalDateTime, Integer>(null, 0); 
  while(keyIterator.hasNext()) {
          Map.Entry<LocalDateTime, Integer> NextEntry = 
               (Entry<LocalDateTime, Integer>) keyIterator.next();
          if(null != perviousEntry.getKey()) {
              if(LocalDateTime.now().isBefore(NextEntry.getKey()) && LocalDateTime.now().isAfter(perviousEntry.getKey())){
                  System.out.println(NextEntry.getKey()   " Value : "   NextEntry.getValue());
              }
          }
          perviousEntry = NextEntry; 
  }

CodePudding user response:

First idea: If you know that all the time values in your map are either 00 min or 30 min You could try to work on a currentTimeRoundedToTheNext00or30min value that will exist in your map.

Ex: (pseudo code as i'm not familiar with LocalDateTime)

LocalDateTime currentTimeRoundedToTheNext00or30min = currentTime;
if (0 < currentTime.minutes < 30) {
    currentTimeRoundedToTheNext00or30min.setMinutes(30);
} else {
    currentTimeRoundedToTheNext00or30min.setMinutes(0);
    currentTimeRoundedToTheNext00or30min.addHour(1);
}
int result = yourMap.get(currentTimeRoundedToTheNext00or30min);

Second idea: If the time values used as keys in your map are sorted, your need can also be seen as "finding the first key of the map that is greater than the current time". By going through the map keys, comparing the key to the currentTime, and getting out of the loop as soon as one is greater than your tested currentTime value, you can solve the issue

Third idea: Without even using the map, there is a simple way of finding the expected value based on currentTime's hour and minute values. (also pseudo code as i'm not sure of syntaxes using LocalDateTime functions)

LocalDateTime currentTime = LocalDateTime.now();
Integer result = currentTime.getHours() * 2;
if (0 < currentTime.getMinutes() <= 30) {
    result  = 1;
} else {
    result  = 2;
}

This can be seen as "counting the number of 30-minutes sections that have been started since the beginning of the day"

CodePudding user response:

To find if the current LocalDateTime falls in between the times it is possible to use
LocalDateTime::isBefore along with Duration::between. As the first step we can find out all dates that happened before the current time and among these dates, we can find out the ones that happened within last 15 seconds (or any other meaningful time slot).

Something similar to

resultMap.entrySet().stream()
.filter( entry -> { 
  val now = LocalDateTime.now(); 
   return (entry.getKey() == now) || entry.getKey().isBefore(now) && 
          Duration.between(entry.getKey(),now()).toSeconds() == 15)
)
.forEach( entry -> System.out.println(entry.getKey()   " : "   entry.getValue());

CodePudding user response:

You can retrieve the value in O(log n) time by utilizing a TreeMap.

Below is a complete example of how it can be achieved:

public class DataTimeStorage {
    private final NavigableMap<LocalDateTime, Integer> storage = new TreeMap<>();

    public int getClosest(LocalDateTime dateTime) {
        LocalDateTime lo = storage.floorKey(dateTime);
        LocalDateTime hi = storage.ceilingKey(dateTime);

        if (lo == null && hi == null) throw new IllegalArgumentException();
        if (lo == null) return storage.get(hi);
        if (hi == null) return storage.get(lo);

        return diffInMillis(lo, dateTime) < diffInMillis(dateTime, hi) ?
                storage.get(lo) : storage.get(hi);
    }

    public long diffInMillis(LocalDateTime dateTime1, LocalDateTime dateTime2) {
        return Duration.between(dateTime1, dateTime2).toMillis();
    }

    public void addItemsForDay(LocalDate day) {
        int count = 0;
        for (LocalDateTime i = day.atTime(0, 0);
             i.toLocalDate().equals(day);
             i = i.plusMinutes(30))
            storage.put(i, count  );
    }

    @Override
    public String toString() {
        return storage.toString();
    }
}

main() - demo

public static void main(String[] args) {
    DataTimeStorage storage = new DataTimeStorage();

    storage.addItemsForDay(LocalDate.of(2022,03,29));
    System.out.println(storage.getClosest(
            LocalDateTime.of(2022,03,29,01,27)));
}

Output

3

CodePudding user response:

You’re correct that NavigableMap is a good approach:

NavigableMap<LocalDateTime, Integer> sorted = new TreeMap<>(resultMap);
Map.Entry<LocalDateTime, Integer> next = sorted.ceilingEntry(targetTime);

OptionalInt match;
if (next != null) {
    match = OptionalInt.of(next.getValue());
} else {
    match = OptionalInt.empty();
}

I’m not sure if this is the exact logic you want, but you can study the javadoc of ceilingEntry, floorEntry, higherEntry and lowerEntry and choose the ones that fit your need.

  • Related