Search code examples
javaarraysdatedate-comparison

How to check array of dates are consecutive from todays date?


I have an array of unique dates from each time the user completes a task. I want to check if the dates within the array are consecutive from and including todays date.

If the array contains dates: "2017/6/2, 2017/6/3, 2017/6/4, 2017/6/5" then based on today's date being 2017/6/5 the function would return 4 as there are 4 consecutive dates from and including today.

If the array contains dates "2017/6/2, 2017/6/3, 2017/6/4" then it would return 0 as the array does not include today's date. Otherwise the count would be broken upon a non consecutive date.

List<Date> dateList = new ArrayList<Date>();
int count = 0;
Date todayDate = new Date();

for (int i=0; i<dateList.size(); i++){
    // Check if dates within the array are consecutive from todayDate, if so then increment count by 1.
}

Solution

  • If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

    If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

    Although you can also use JodaTime, it's being discontinued and replaced by the new APIs, do I don't recommend start a new project with joda. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".


    As you want to compare just the date (day/month/year), and not the time (hour/minute/second), the best choice is to use the LocalDate class. For java 8, this class is in java.time package, and in ThreeTen Backport, the package is org.threeten.bp. But the classes and methods names are the same.

    The code would be like this:

    public int count(List<LocalDate> dateList, LocalDate today) {
        if (!dateList.contains(today)) { // today is not in the list, return 0
            return 0;
        }
    
        int count = 0;
        LocalDate prev = dateList.get(0); // get first date from list
        for (int i = 1; i < dateList.size(); i++) {
            LocalDate next = dateList.get(i);
            if (prev.plusDays(1).equals(next)) {
                // difference between dates is one day
                count++;
            } else {
                // difference between dates is not 1
                // Do what? return 0? throw exception?
            }
            prev = next;
        }
    
        return count + 1; // didn't count the first element, adding 1
    }
    

    Testing this method:

    List<LocalDate> dateList = new ArrayList<>();
    dateList.add(LocalDate.of(2017, 6, 2));
    dateList.add(LocalDate.of(2017, 6, 3));
    dateList.add(LocalDate.of(2017, 6, 4));
    dateList.add(LocalDate.of(2017, 6, 5));
    LocalDate today = LocalDate.now();
    
    System.out.println(count(dateList, today)); // 4
    

    Another test (when today is not in the list)

    List<LocalDate> dateList = new ArrayList<>();
    dateList.add(LocalDate.of(2017, 6, 2));
    dateList.add(LocalDate.of(2017, 6, 3));
    dateList.add(LocalDate.of(2017, 6, 4));
    LocalDate today = LocalDate.now();
    
    System.out.println(count(dateList, today)); // 0
    

    Notes:

    • As it wasn't specified what to do when the days are not consecutive (return 0 or throw exception), I left this part commented. But it should be straightforward to add this to the code
    • If you want to convert java.util.Date to LocalDate, you can do as follows (using the code of this answer, full explanation is in this link in case you have any questions):

      public LocalDate convert(Date date) {
          return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
      }
      
      // if your Date has no toInstant method, try this:
      public LocalDate convert(Date date) {
          return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
      }
      
    • I understood that you want to check for consecutive days (so, a 1-day difference between the dates). But if you want to check if the previous date is before the next (no matter how many days), you can change the if (prev.plusDays(1).equals(next)) to if (prev.isBefore(next))

    • I'm not sure if that's the case, but if you want, you can also parse a String directly to a LocalDate (so you don't need to create lots of Date objects), using a DateTimeFormatter:

      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/M/d");
      LocalDate d = LocalDate.parse("2017/6/2", formatter); // 2017-06-02