Generating all days between 2 given dates in Java [duplicate]

空扰寡人 提交于 2020-01-02 02:33:06

问题


I'm trying to get an array of Dates, while my input is a 'from'/'to' structure. So my input is:

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

My output should be an Arraylist with all dates between date1 and date2. I've already looked for this, but I could only find questions about the difference between 2 dates:

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

Any hints or suggestions? All other questions are for iOS or SQL.


回答1:


Take a look at 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);
}



回答2:


Below is the code to get array of dates between the two string date.

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

}



回答3:


If you don't want to use third party libraries you can use Calendar:

Check 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;
}



回答4:


I like JodaTime, but this can also be done without 3rd party libraries by using java.util.Calendar. Given a Calendar object, one can use its add method to increase certain fields of the date while honoring the calendar rules (like adding 1 day to the 31st of January gets you to the 1st of February, not to the 32nd of January).

First get the dates into one Calendar object each, in the correct chronological order so adding is going in the right direction later:

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

    if (date1.before(date2)) {
        cStart.setTime(date1);
        cStop.setTime(date2);
    } else {
        cStart.setTime(date2);
        cStop.setTime(date1);

date1 and date2 are the parsed Date objects from your question, for simplicity's sake.

Next, loop over an "add 1 to day-of-year" instruction until this gets you beyond the stop date:

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

And lastly print the stop date

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

pretty() is just some mini method sending the calendar through a SDF, like the one you used for parsing the Strings in the first place.

This solution will print the date range, including the start and stop dates, and might need some tweaking around the edge cases (like date1==date2). Can be easily adapted to exclude the start and stop dates. Printing can be swapped for aggregation of course. To get a Date object from the calendar, use the getTime() method (returns a snapshot, not a live reference).

The documentation for the relevant (Gregorian)Calendar can be found here.




回答5:


In case you are using Guava, there is a very elegant solution to this problem.

Guava has two neat classes, such as Range and ContiguousSet, which implement exactly what you need: first one operates on ranges of values, and second one - is able to convert a range to a set of discrete values.

Example of usage of both (together with 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...
}

Personally, I really prefer this way, as it eliminates some problems with understanding closed/open ranges, and makes code much easier to read and understand, while making no impact on performance. Also, it works with any kinds of dates, any libraries (you can swap YodaTime to Java8 Dates or even Java7- Date-based implementation).

Moreover, it allows you to do some neat operations on ranges like intersections, unions, spanning of ranges, incredibly fast "contains" and so on.

Only downsides are:

  1. Dependence on Guava.
  2. Need to create a special "DiscreteDomain" class, which Guava uses to understand where one date ends and other begins.

Example of LocalDateDomain implementation which operates as a bridge between Guava and JodaTime:

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



回答6:


I already know that OP isn't using Java 8 but here's the current solution - Java has been revamped and the new java.time API does every conceivable job in that regard:

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

This will output every valid day between the two dates whereas most of the other solutions will also give you invalid ones; heres the thing: temporal calculations need to be done on timezone-independent data - the output on the other hand may very well be timezone and/or chronology -dependent. Thats why there are packages like java.time.format - simply calculate your time/date values and format them for your chosen region ... thats how its done correctly.

If you need to convert temporal input there are also useful functions in the time-API, i recommend doing a thorough tutorial on the subject, a few good introductions may be 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.



来源:https://stackoverflow.com/questions/32372279/generating-all-days-between-2-given-dates-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!