生成 Java 中 2 个给定日期之间的所有日期

Generating all days between 2 given dates in Java

我正在尝试获取日期数组,而我的输入是 'from'/'to' 结构。 所以我的输入是:

String date1 = "2014-01-01";
String date2 = "2014-05-01";

我的输出应该是一个数组列表,其中包含日期 1 和日期 2 之间的所有日期。 我已经在找这个了,但我只能找到关于两个日期之间差异的问题:

SimpleDateFormat myFormat = new SimpleDateFormat("dd MM yyyy");
String inputString1 = "23 01 1997";
String inputString2 = "27 04 1997";

try {
    Date date1 = myFormat.parse(inputString1);
    Date date2 = myFormat.parse(inputString2);
    long diff = date2.getTime() - date1.getTime();
    System.out.println ("Days: " + TimeUnit.DAYS.convert(diff,TimeUnit.MILLISECONDS));
} catch (ParseException e) {
e.printStackTrace();
}

有什么提示或建议吗?所有其他问题都针对 iOS 或 SQL。

看看 JodaTime:http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTime.html

DateTime dateTime1 = new DateTime(date1);
DateTime dateTime2 = new DateTime(date2);

List<Date> allDates = new ArrayList();

while( dateTime1.before(dateTime2) ){
   allDates.add( dateTime1.toDate() );
   dateTime1 = dateTime1.plusDays(1);
}

如果您不想使用第三方库,您可以使用 Calendar:

检查here a working demo

public static void main(String[] args) throws Exception {
    SimpleDateFormat myFormat = new SimpleDateFormat("dd MM yyyy");
    String inputString1 = "23 01 1997";
    String inputString2 = "27 04 1997";
    ArrayList<Date> dates = new ArrayList<Date>();

    try {
        Date date1 = myFormat.parse(inputString1);
        Calendar c1 = DateToCalendar(date1);
        Date date2 = myFormat.parse(inputString2);
        Calendar c2 = DateToCalendar(date2);

        while (!areEqualDate(c1, c2)) {
            dates.add(c1.getTime());
            System.out.println (c1.getTime());
            c1.add(Calendar.DAY_OF_YEAR, 1);
        }
    } catch (ParseException e) {
        e.printStackTrace();
    }


    // ArrayList<Date> dates >> contain all dates between both given days.
}


private static boolean areEqualDate(Calendar c1, Calendar c2) {
    if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)) return false; 
    if (c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)) return false; 
    if (c1.get(Calendar.DAY_OF_YEAR) != c2.get(Calendar.DAY_OF_YEAR)) return false; 
    return true;
}

public static Calendar DateToCalendar(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    return cal;
}

我喜欢 JodaTime,但这也可以通过使用 java.util.Calendar 在没有第 3 方库的情况下完成。给定一个 Calendar 对象,可以使用它的 add 方法来增加日期的某些字段,同时遵守日历规则(比如在 1 月 31 日加上 1 天就得到 2 月 1 日,而不是到 1 月 32 日)。

首先将日期按正确的时间顺序分别放入一个 Calendar 对象中,以便稍后按正确的方向添加:

    Calendar cStart = Calendar.getInstance(),
             cStop = Calendar.getInstance();

    if (date1.before(date2)) {
        cStart.setTime(date1);
        cStop.setTime(date2);
    } else {
        cStart.setTime(date2);
        cStop.setTime(date1);
为简单起见,

date1date2 是您问题中已解析的 Date 对象。

接下来,循环执行 "add 1 to day-of-year" 指令,直到超出停止日期:

do {
        System.out.println(pretty(cStart));
        cStart.add(Calendar.DAY_OF_YEAR, 1);
} while (cStart.before(cStop));

最后打印停止日期

    System.out.println(pretty(cStop));

pretty() 只是一些通过 SDF 发送日历的迷你方法,就像您首先用于解析字符串的方法一样。

此解决方案将打印日期范围,包括开始日期和结束日期,并且可能需要围绕边缘情况进行一些调整(例如 date1==date2)。可以很容易地调整以排除开始和结束日期。当然,打印可以换成聚合。要从日历中获取日期对象,请使用 getTime() 方法(returns 快照,而不是实时引用)。

可以找到相关 (Gregorian)Calendar 的文档 here

我已经知道 OP 没有使用 Java 8,但这是当前的解决方案 - Java 已经过改进,新的 java.time API 可以想象这方面的工作:

//change these values :
LocalDate ld1 = LocalDate.ofEpochDay(0);
LocalDate ld2 = LocalDate.now();

//do NOT change these:
final LocalDate begin = ld1.isBefore(ld2) ? ld1 : ld2;
final LocalDate end = ld2.isAfter(ld1) ? ld2 : ld1;

for (int i = 0; i < begin.until(end, ChronoUnit.DAYS); i++) {
    final LocalDate curDate = begin.plusDays(i);
    System.out.println("current date : " + curDate);
}

这将在两个日期之间每隔 valid 天输出一次,而大多数其他解决方案也会为您提供 invalid 个;事情是这样的:时间计算需要在与时区无关的数据上完成——另一方面,output 很可能是时区 and/or 时间顺序相关的。 这就是为什么有像 java.time.format 这样的软件包的原因 - 只需计算您的 time/date 值并 格式化 它们适用于您选择的区域...这就是正确完成的方式。

如果你需要转换时间输入,time-API中也有一些有用的函数,我建议你做一个关于这个主题的完整教程,一些好的介绍可能是 this and especially that :

There are two basic ways to represent time. One way represents time in human terms, referred to as human time, such as year, month, day, hour, minute and second. The other way, machine time, measures time continuously along a timeline from an origin, called the epoch, in nanosecond resolution. The Date-Time package provides a rich array of classes for representing date and time. Some classes in the Date-Time API are intended to represent machine time, and others are more suited to representing human time.

下面是获取两个字符串日期之间的日期数组的代码。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
public class DateFormatExample {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        SimpleDateFormat myFormat = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");  
        String date1 = "2014-01-01";
        String date2 = "2014-05-01";
        try {
            Date d1 = myFormat.parse(date1);
            Date d2 = myFormat.parse(date2);
            List<Date> allDates = new ArrayList<Date>();
            List<String> allDatesString = new ArrayList<String>();
            while( d1.before(d2) ){
                d1 = addDays(d1, 1);  
                allDates.add(d1);
                allDatesString.add(formatter.format(d1));
            }
            System.out.println(allDates);
            System.out.println(allDatesString);
        } catch (ParseException e) {
        e.printStackTrace();
        }
    }
    private static Date addDays(Date d1, int i) {
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(d1);
        cal.add(Calendar.DATE, 1);
        return cal.getTime();
    }

}

如果你使用的是 Guava,这个问题有一个非常优雅的解决方案。

Guava 有两个整洁的 classes,例如 Range and ContiguousSet,它们实现了您所需要的:第一个对值的范围进行操作,第二个 - 能够将范围转换为一组离散值。

两者的用法示例(连同 JodaTime):

LocalDate start = LocalDate.parse("2015-01-01");
LocalDate end = LocalDate.parse("2019-02-01");
Range<LocalDate> range = Range.closed(start, end); //Creates a "closed" range, that is both dates are inclusive. There are also options like "openClosed", "closedOpen" and "open"
final Set<LocalDate> daySet = ContiguousSet.create(range, LocalDateDomain.INSTANCE); //Create a "virtual" set of days in given the range. "virtual" part means that if you create a set of 10 thousand years, it will not eat your memory at all
for (LocalDate day : daySet) {
  //...operation...
}

就我个人而言,我真的更喜欢这种方式,因为它消除了理解 closed/open 范围的一些问题,并使代码更易于阅读和理解,同时对性能没有影响。此外,它适用于任何类型的日期、任何库(您可以将 YodaTime 交换为 Java8 日期甚至 Java7- 基于日期的实现)。

此外,它允许您对范围进行一些巧妙的操作,例如交集、并集、范围跨越,速度非常快 "contains" 等等。

唯一的缺点是:

  1. 对番石榴的依赖。
  2. 需要创建一个特殊的 "DiscreteDomain" class,Guava 使用它来了解一个日期的结束位置和另一个日期的开始位置。

作为 Guava 和 JodaTime 之间的桥梁的 LocalDateDomain 实现示例:

public class LocalDateDomain extends DiscreteDomain<LocalDate> {
    public static final LocalDateDomain INSTANCE = new LocalDateDomain();

    @Override
    public LocalDate next(LocalDate value) {
        return value.plusDays(1);
    }

    @Override
    public LocalDate previous(LocalDate value) {
        return value.minusDays(1);
    }

    @Override
    public long distance(LocalDate start, LocalDate end) {
        return Days.daysBetween(start, end).getDays();
    }
}