如何在Java中迭代一系列的日期?

182

我需要在我的脚本中通过一系列日期执行一些操作,给定一个起始和结束日期。
请为我提供使用Java实现此功能的指导。

for ( currentDate = starDate; currentDate < endDate; currentDate++) {

}

我知道上面的代码根本不可能实现,但我这样做是为了向您展示我想要实现的内容。

15个回答

244

嗯,你可以使用Java 8的时间API来解决这个问题,特别是可以使用java.time.LocalDate(或者对于Java 7及更早版本可以使用等效的Joda Time类)。

for (LocalDate date = startDate; date.isBefore(endDate); date = date.plusDays(1))
{
    ...
}

我会强烈推荐使用java.time(或Joda Time)而不是内置的Date/Calendar类。


2
关于Joda Time的观点,需要进一步阐述:由于夏令时的变化和转换等边角情况,尝试自己正确地实现它比人们想象的更加困难。 - Raedwald
+1 for Joda,我希望有一天它能够成为标准API的一部分。 - gyorgyabraham
4
JSR-310 在 Java 8 中看起来非常不错。 - Jon Skeet
4
可以确认,使用Java 8中的java.time.LocalDate代替Joda,可以运行同样的代码。 - Molten Ice
3
Joda-Time项目现在处于维护模式,并建议迁移到java.time类。如评论中所述,此答案的代码在java.time中仍可直接使用,只需更改您的“import”语句即可。 - Basil Bourque

162
JodaTime很好,但是为了完整性和/或如果您更喜欢API提供的设施,这里是标准API方法。当从像下面这样的java.util.Date实例开始时:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date startDate = formatter.parse("2010-12-20");
Date endDate = formatter.parse("2010-12-26");

如果您还没有使用Java8,这里是传统的java.util.Calendar方法:

Calendar start = Calendar.getInstance();
start.setTime(startDate);
Calendar end = Calendar.getInstance();
end.setTime(endDate);

for (Date date = start.getTime(); start.before(end); start.add(Calendar.DATE, 1), date = start.getTime()) {
    // Do your job here with `date`.
    System.out.println(date);
}

下面介绍Java8的java.time.LocalDate方法,基本上与JodaTime方法相同:

LocalDate start = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate end = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

for (LocalDate date = start; date.isBefore(end); date = date.plusDays(1)) {
    // Do your job here with `date`.
    System.out.println(date);
}

如果您想要包括结束日期,那么分别使用!start.after(end)!date.isAfter(end)

94

Java 8 样式,使用 java.time 类:

// Monday, February 29 is a leap day in 2016 (otherwise, February only has 28 days)
LocalDate start = LocalDate.parse("2016-02-28"),
          end   = LocalDate.parse("2016-03-02");

// 4 days between (end is inclusive in this example)
Stream.iterate(start, date -> date.plusDays(1))
        .limit(ChronoUnit.DAYS.between(start, end) + 1)
        .forEach(System.out::println);

输出:

2016-02-28
2016-02-29
2016-03-01
2016-03-02

另外选择:

LocalDate next = start.minusDays(1);
while ((next = next.plusDays(1)).isBefore(end.plusDays(1))) {
    System.out.println(next);
}

Java 9 新增了 datesUntil() 方法:

start.datesUntil(end.plusDays(1)).forEach(System.out::println);

1
你可以放置多个值吗?例如:只有星期一或星期四或两者都有。 - user3402040
我不明白这个问题?听起来你可能正在寻找 Stream.filter - Martin Andersson

30

这本质上是BalusC提供的相同答案,但用while循环代替for循环使代码更易读:

Calendar start = Calendar.getInstance();
start.setTime(startDate);

Calendar end = Calendar.getInstance();
end.setTime(endDate);

while( !start.after(end)){
    Date targetDay = start.getTime();
    // Do Work Here

    start.add(Calendar.DATE, 1);
}

3
如果逻辑中包括“continue”语句,则这种方法不起作用,而BalusC的for循环版本可以处理带有continue语句的逻辑。 - Sanjiv Jivan

8

Apache Commons

    for (Date dateIter = fromDate; !dateIter.after(toDate); dateIter = DateUtils.addDays(dateIter, 1)) {
        // ...
    }

+1,依我之见,当你在处理旧代码时,这是最干净的方法。只需添加一个额外的静态导入addDays(..),它甚至可以更短。 - Priidu Neemre

7

从Java 9开始内置:LocalDate.datesUntil()

目前为止,之前的回答似乎仅考虑了Java 8及更早版本。从Java 9开始,使用以下方式:

    LocalDate startDate = LocalDate.of(2021, Month.JUNE, 29);
    LocalDate endDate = LocalDate.of(2021, Month.JULY, 3);
    
    startDate.datesUntil(endDate).forEach(System.out::println);

这个示例的输出结果是:

2021-06-29
2021-06-30
2021-07-01
2021-07-02

虽然开始日期是包含在内的,但结束日期是排除在外的,就像我读到你的问题的方式一样。如果有人想要包含结束日期,很简单,只需加一天:

    startDate.datesUntil(endDate.plusDays(1)).forEach(System.out::println);
2021-06-29
2021-06-30
2021-07-01
2021-07-02
2021-07-03

你可以像我展示的那样使用方法引用System.out::println,同样地,你也可以迭代多年。

链接


5
我们可以将逻辑迁移到各种方法用于Java 7、Java 8和Java 9:
public static List<Date> getDatesRangeJava7(Date startDate, Date endDate) {
    List<Date> datesInRange = new ArrayList<>();
    Calendar startCalendar = new GregorianCalendar();
    startCalendar.setTime(startDate);
    Calendar endCalendar = new GregorianCalendar();
    endCalendar.setTime(endDate);
    while (startCalendar.before(endCalendar)) {
        Date result = startCalendar.getTime();
        datesInRange.add(result);
        startCalendar.add(Calendar.DATE, 1);
    }
    return datesInRange;
}

public static List<LocalDate> getDatesRangeJava8(LocalDate startDate, LocalDate endDate) {
    int numOfDays = (int) ChronoUnit.DAYS.between(startDate, endDate);
    return IntStream.range(0, numOfDays)
            .mapToObj(startDate::plusDays)
            .collect(Collectors.toList());
}

public static List<LocalDate> getDatesRangeJava9(LocalDate startDate, LocalDate endDate) {
    return startDate.datesUntil(endDate).collect(Collectors.toList());
}

然后我们可以这样调用这些方法:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date startDate = formatter.parse("2010-12-20");
Date endDate = formatter.parse("2010-12-26");
List<Date> dateRangeList = getDatesRangeJava7(startDate, endDate);
System.out.println(dateRangeList);

LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate endLocalDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
List<LocalDate> dateRangeList8 = getDatesRangeJava8(startLocalDate, endLocalDate);
System.out.println(dateRangeList8);
List<LocalDate> dateRangeList9 = getDatesRangeJava8(startLocalDate, endLocalDate);
System.out.println(dateRangeList9);

输出将是:

[Mon Dec 20 00:00:00 IST 2010,Tue Dec 21 00:00:00 IST 2010,Wed Dec 22 00:00:00 IST 2010,Thu Dec 23 00:00:00 IST 2010,Fri Dec 24 00:00:00 IST 2010,Sat Dec 25 00:00:00 IST 2010]

[2010-12-20,2010-12-21,2010-12-22,2010-12-23,2010-12-24,   2010-12-25]

[2010-12-20,2010-12-21,2010-12-22,2010-12-23,2010-12-24,   2010-12-25]


2
可怕的DateCalendar类多年前被java.time类所取代。具体来说,被InstantZonedDateTime替代。 - Basil Bourque
3
我喜欢Java 8和9的方式。对于Java 6和7,我建议使用ThreeTen Backport库,然后按照Java 8的方式操作。您很好地展示了这种方式更加清晰易懂且更加适合程序员使用。 - Ole V.V.

5
private static void iterateBetweenDates(Date startDate, Date endDate) {
    Calendar startCalender = Calendar.getInstance();
    startCalender.setTime(startDate);
    Calendar endCalendar = Calendar.getInstance();
    endCalendar.setTime(endDate);

    for(; startCalender.compareTo(endCalendar)<=0;
          startCalender.add(Calendar.DATE, 1)) {
        // write your main logic here
    }

}

3
public static final void generateRange(final Date dateFrom, final Date dateTo)
{
    final Calendar current = Calendar.getInstance();
    current.setTime(dateFrom);

    while (!current.getTime().after(dateTo))
    {
        // TODO

        current.add(Calendar.DATE, 1);
    }
}

2

这是Java 8的代码。我认为这段代码可以解决你的问题。祝您编写愉快

    LocalDate start = LocalDate.now();
    LocalDate end = LocalDate.of(2016, 9, 1);//JAVA 9 release date
    Long duration = start.until(end, ChronoUnit.DAYS);
    System.out.println(duration);
     // Do Any stuff Here there after
    IntStream.iterate(0, i -> i + 1)
             .limit(duration)
             .forEach((i) -> {});
     //old way of iteration
    for (int i = 0; i < duration; i++)
     System.out.print("" + i);// Do Any stuff Here

这是您可以采用的最佳且简单的方法。 - jatin Goyal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接