关于java:如何从今天的日期开始连续查看日期数组?

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,函数将返回4,因为从今天开始(包括今天)有4个连续的日期。

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

1
2
3
4
5
6
7
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.
}


如果使用Java 8,请考虑使用新的JavaTimeAPI。与旧的API相比,它更容易、更少被窃听和更少出错。

如果您使用的是Java <=7,您可以使用StuteTeN后端,这是Java 8新的日期/时间类的一个很大的后端。对于Android,有三个etenabp(更多关于如何使用它)。

尽管您也可以使用jodatime,但它已经被新的API中断并替换了,我不建议用joda启动新的项目吗?即使在Joda的网站上,它也说:"请注意,Joda时间被认为是一个很大程度上"完成"的项目。没有计划进行重大改进。如果使用JavaSE 8,请迁移到JavaTimes(JSR-310)。

由于您只想比较日期(日/月/年),而不想比较时间(小时/分钟/秒),所以最好的选择是使用LocalDate类。对于Java 8,这个类是在EDCOX1×1的封装中,在三端口后端,包是EDOCX1×2。但是类和方法的名称是相同的。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
}

测试此方法:

1
2
3
4
5
6
7
8
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

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

1
2
3
4
5
6
7
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

笔记:

  • 由于没有规定在不连续的日子里该怎么做(返回0或抛出异常),我留下了这部分评论。但是将它添加到代码中应该很简单
  • 如果你想把java.util.Date转换成LocalDate的话,你可以这样做(使用这个答案的代码,如果你有任何问题,在这个链接中有完整的解释):

    1
    2
    3
    4
    5
    6
    7
    8
    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();
    }
  • 我知道你想连续检查几天(所以日期之间相差一天)。但是如果你想检查上一个日期是否在下一个日期之前(不管多少天),你可以把if (prev.plusDays(1).equals(next))改为if (prev.isBefore(next))

  • 我不确定是不是这样,但是如果你愿意,你也可以直接把一个String解析成一个LocalDate(所以你不需要创建很多Date对象),使用DateTimeFormatter

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


如果我理解正确的要求,您有一组按日期排序的Date对象,并且保证在同一天内不会有两个Date对象,但可能有两天的间隔。您的目标是返回最大子数组的长度,该数组只包含连续的天数,并且还包含当前日期,或者如果没有此类子数组,则返回0。当前日期可能落在该子数组的任何位置,不一定是在开始或结束时。

目前还不清楚你是否需要支持跨越年份界限,但我想是这样。我还假设列表中的所有Date对象都属于同一时区,也就是运行设备的时区。如果不是这样的话,您应该参考这个答案了解更多关于测试两个Date对象是否在同一天引用的信息。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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);
}


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

  • 使用新的日期API;
  • 使用图书馆;
  • 但是,在这种情况下,使用旧的日期类,我会这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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();
    }