如何检查日期数组是否从今天开始连续?

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

我有一组用户每次完成任务时的唯一日期。我想检查数组中的日期是否与今天的日期连续并包括今天的日期。

如果数组包含日期:"2017/6/2, 2017/6/3, 2017/6/4, 2017/6/5" 那么根据今天的日期 2017/6/5 函数将 return 4 因为有 4 个连续的日期从今天开始(包括今天) .

如果数组包含日期 "2017/6/2, 2017/6/3, 2017/6/4" 那么它将 return 0 因为数组不包含今天的日期。否则计数将在非连续日期中断。

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.
}

有很多方法可以写的更清楚:

  1. 使用新日期API;
  2. 使用图书馆;

但是,在这种情况下,使用旧日期 类,我会这样做:

public static void main(String[] args) {
    long millisInDay = TimeUnit.DAYS.toMillis(1);
    List<Date> dates = Arrays.asList(new Date("2017/6/2"), new Date("2017/6/3"), new Date("2017/6/4"), new Date("2017/6/5"));
    System.out.println(getSequentialNumber(millisInDay, dates));
}

private static int getSequentialNumber(long millisInDay, List<Date> dates) {
    int count = 0;
    Date now = setMidnight(Calendar.getInstance().getTime());
    for (int i = dates.size() - 1; i >= 0; i--) {
        Date date = setMidnight(dates.get(i));
        if (date.getTime() == now.getTime()) {
            count++;
        }
        now.setTime(now.getTime() - millisInDay);
    }
    return count;
}

private static Date setMidnight(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    calendar.set(Calendar.HOUR, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    return calendar.getTime();
}

如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java <= 7,您可以使用 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 ).

虽然您也可以使用 JodaTime,但它已经停产并被新的 API 取代,我不建议您使用 joda 开始一个新项目。即使在 joda's website 它说:"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).".


因为你只想比较日期 (day/month/year),而不是时间 (hour/minute/second),最好的选择是使用 LocalDate class .对于 java 8,这个 class 在 java.time 包中,而在 ThreeTen Backport 中,包在 org.threeten.bp 中。但是 classes 和方法名称是相同的。

代码应该是这样的:

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
}

测试此方法:

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

另一个测试(当今天不在列表中时)

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

备注:

  • 由于没有指定当日子不连续时要做什么(return 0 或抛出异常),我将这部分留在评论中。但是将其添加到代码中应该很简单
  • 如果你想把java.util.Date转换成LocalDate,你可以按照下面的方式做(使用代码of this answer,完整的解释在这个link 如果您有任何问题):

    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();
    }
    
  • 我了解到您想检查 连续 天(因此,日期之间相差 1 天)。但是如果你想检查前一个日期是否在之前下一个(无论多少天),你可以将if (prev.plusDays(1).equals(next))更改为if (prev.isBefore(next))

  • 我不确定是不是这样,但如果你愿意,你也可以将 String 直接解析为 LocalDate(所以你不需要创建大量 Date 个对象),使用 DateTimeFormatter:

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

如果我对要求的理解正确,你有一个 Date 对象数组,按日期排序,并保证同一天不会有两个 Date 对象,但可能有间隔那些日子。您的目标是 return 仅包含连续天数且还包括当前日期的最大子数组的长度,或者如果没有这样的子数组,则 return 0 。当前日期可能位于该子数组内的任何位置,不一定在开头或结尾。

不清楚您是否需要支持跨越年份界限,但我假设是这样。我还假设列表中的所有 Date 对象都针对同一时区,这也是您所在设备的时区 运行。如果不是这种情况,您应该参考 this answer 以获取有关测试两个 Date 对象是否指同一天的更多信息。

如果您使用 Calendar 个对象而不是 Date 个对象,则执行此操作相当简单。您不需要任何第三方库,因为 DateCalendar 都是标准 Android API 的一部分。我建议分两个阶段执行此操作:首先在数组中搜索当前日期,然后在两个方向上扫描日期中的间隙或数组边界。然后数一数你在每个方向上能走多远。

public int getDateSpanCount(List<Date> dateList) {
    final int n = dateList.size();
    final Calendar today = Calendar.getInstance();
    final Calendar other = Calendar.getInstance();
    int count = 0;

    // First search for today in the date array
    int posToday = -1;
    for (int i=0; i<n; i++) {
        other.setTime(dateList.get(i));
        if (areSameDay(today, other)) {
            posToday = i;
            break;
        }
    }

    // If today is in the list, count the size of the sub-array containing today
    if (posToday >= 0) {
        count++; // count today, at least
        final Calendar probe = Calendar.getInstance();

        // scan backwards from position of today's date
        for (int prevPos = posToday - 1; prevPos >= 0; prevPos--) {
            final Date prev = dateList.get(prevPos);
            probe.setTime(prev);
            other.add(Calendar.DAY_OF_YEAR, -1);
            if (areSameDay(probe, other)) {
                count++;
                other.setTime(prev);
            } else {
                break;
            }
        }

        // reset the other time
        other.setTime(today.getTime());

        // scan forward from position of today's date
        for (int nextPos = posToday + 1; nextPos < n; nextPos++) {
            final Date next = dateList.get(nextPos);
            probe.setTime(next);
            other.add(Calendar.DAY_OF_YEAR, 1);
            if (areSameDay(probe, other)) {
                count++;
                other.setTime(next);
            } else {
                break;
            }
        }
    }
    return count;
}

/** Test whether two Calendar objects are set to the same day */
private static boolean areSameDay(Calendar c1, Calendar c2) {
    // see discussion above if dates may not all be for the local time zone
    return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
           c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR);
}