如何遍历Java中的日期范围?

Posted

技术标签:

【中文标题】如何遍历Java中的日期范围?【英文标题】:How to iterate through range of Dates in Java? 【发布时间】:2011-05-30 21:55:20 【问题描述】:

在我的脚本中,我需要在给定开始日期和结束日期的情况下,在日期范围内执行一组操作。 请指导我使用 Java 实现这一目标。

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


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

【问题讨论】:

【参考方案1】:

好吧,您可以使用 Java 8's time-API 执行类似的操作,特别是针对此问题 java.time.LocalDate(或 Java 7 及更早版本的等效 Joda Time 类)

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

    ...

我会彻底推荐在内置的Date/Calendar 类上使用java.time(或Joda Time)。

【讨论】:

进一步扩展关于 Joda Time 的观点:尝试自己正确实现这一点比人们想象的要困难,因为夏季前后的变化会出现一些极端情况。 +1 为 Joda,我希望有一天它会在标准 API 中实现它。 @gyabraham:JSR-310 在 Java 8 上的表现相当不错。 可以确认这个完全相同的代码可以使用 Java 8 的 java.time.LocalDate 而不是 Joda。 Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。如 cmets 中所述,此答案的代码在 java.time 中按原样工作,只需更改您的 import 语句即可。【参考方案2】:

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)

【讨论】:

【参考方案3】:

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);

【讨论】:

你能放多个值吗?例如:只有星期一或星期四或两者兼而有之 我不明白这个问题?听起来您可能正在寻找Stream.filter【参考方案4】:

这与 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);

【讨论】:

如果逻辑涉及“继续”语句,这将不起作用,而 BalusC 的 for 循环版本使用 continue 语句。【参考方案5】:

Apache Commons

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

【讨论】:

+1,恕我直言,当您使用旧代码时,这是最干净的。只需为 addDays(..) 添加一个额外的静态导入,它就会变得更短。【参考方案6】:
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
    


【讨论】:

【参考方案7】:

我们可以将逻辑迁移到 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);

输出将是:

[2010 年 12 月 20 日星期一 00:00:00 IST 2010 年 12 月 21 日星期二 00:00:00 IST 2010 年 12 月星期三 22 00:00:00 IST 2010,星期四 12 月 23 日 00:00:00 IST 2010,星期五 12 月 24 日 00:00:00 IST 2010,星期六 12 月 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]

【讨论】:

可怕的 DateCalendar 类在几年前被 java.time 类所取代。具体来说,替换为InstantZonedDateDate 我喜欢 Java 8 和 9 的方式。对于 Java 6 和 7,我建议使用 the ThreeTen Backport library,然后使用与 Java 8 中相同的方式。您很好地演示了这种方式如何更清晰,对程序员更友好。【参考方案8】:
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);
    

【讨论】:

【参考方案9】:

从 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

你显然可以用这种方式迭代几年,就像你可以放一个更长的 lambda 一样,我只是放了方法参考 System.out::println 进行演示。

链接

Oracle tutorial: Date Time 解释如何使用 java.time。 Documentation of LocalDate.datesUntil(LocalDate)

【讨论】:

【参考方案10】:

这是 Java 8 代码。我认为这段代码会解决你的问题。Happy Coding

    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

【讨论】:

这是您可以跟进的最佳且简单的方法。【参考方案11】:

为什么不使用 epoch 并轻松循环。

long startDateEpoch = new java.text.SimpleDateFormat("dd/MM/yyyy").parse(startDate).getTime() / 1000;

    long endDateEpoch = new java.text.SimpleDateFormat("dd/MM/yyyy").parse(endDate).getTime() / 1000;


    long i;
    for(i=startDateEpoch ; i<=endDateEpoch; i+=86400)

        System.out.println(i);

    

【讨论】:

【参考方案12】:

您可以编写一个类似的类(实现迭代器接口)并对其进行迭代。

public class DateIterator implements Iterator<Date>, Iterable<Date>


 private Calendar end = Calendar.getInstance();
 private Calendar current = Calendar.getInstance();

 public DateIterator(Date start, Date end)
 
     this.end.setTime(end);
     this.end.add(Calendar.DATE, -1);
     this.current.setTime(start);
     this.current.add(Calendar.DATE, -1);
 

 @Override
 public boolean hasNext()
 
     return !current.after(end);
 

 @Override
 public Date next()
 
     current.add(Calendar.DATE, 1);
     return current.getTime();
 

 @Override
 public void remove()
 
     throw new UnsupportedOperationException(
        "Cannot remove");
 

 @Override
 public Iterator<Date> iterator()
 
     return this;
 

并像这样使用它:

Iterator<Date> dateIterator = new DateIterator(startDate, endDate);
while(dateIterator.hasNext())
      Date selectedDate = dateIterator .next();


【讨论】:

【参考方案13】:

你可以试试这个:

OffsetDateTime currentDateTime = OffsetDateTime.now();
for (OffsetDateTime date = currentDateTime; date.isAfter(currentDateTime.minusYears(YEARS)); date = date.minusWeeks(1))

    ...

【讨论】:

【参考方案14】:

这将帮助您从 30 天开始并循环到今天的日期。您可以轻松更改日期范围和方向。

private void iterateThroughDates() throws Exception 
    Calendar start = Calendar.getInstance();
    start.add(Calendar.DATE, -30);
    Calendar end = Calendar.getInstance();
    for (Calendar date = start; date.before(end); date.add(Calendar.DATE, 1))
        
        System.out.println(date.getTime());
        

【讨论】:

【参考方案15】:

以下 sn-p(使用 Java 8 的 java.time.format)可用于迭代日期范围:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    // Any chosen date format maybe taken  
    LocalDate startDate = LocalDate.parse(startDateString,formatter);
    LocalDate endDate = LocalDate.parse(endDateString,formatter);
    if(endDate.isBefore(startDate))
    
        //error
    
    LocalDate itr = null;
    for (itr = startDate; itr.isBefore(endDate)||itr.isEqual(itr); itr = itr.plusDays(1))
    
        //Processing  goes here
    

plusMonths()/plusYears() 可以选择时间单位增量。 如上图所示,增量为一天。

【讨论】:

以上是关于如何遍历Java中的日期范围?的主要内容,如果未能解决你的问题,请参考以下文章

如何循环遍历日期范围?

在 SQL 中编写函数以遍历 UDF 中的日期范围

循环遍历日期范围

SQL Server 2008 中的 While 循环遍历日期范围,然后插入

遍历 Pandas Dataframe 中定义的日期时间索引范围

以每月步骤循环遍历日期范围