1

I have set of date ranges, I need to get the combined date range if any of the dates overlap in Java.

Given three sets of date ranges, if any of the dates overlap with another range of dates need to be combined.

Example:

20170101-20170331
20170101-20170430
20170430-20170501

Expected result is:

20170101-20170430
20170430-20170501

I have all the dates in String Variable. Can please any one help me to how to write the code for that. I have pasted below my code.

I want to achieve the expected results. I couldn't find out how I need to modify this code. I am a beginner, please help me to do that. I have got this sample program from StackOverflow.

package com.kkkkk.Combine;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;

public class Ideone {
    public static void main(String[] args) throws java.lang.Exception {
        ArrayList<Interval> x = new ArrayList<>();
        x.add(new Interval("20170430", "20170501")); // "20170101", "20170430"
        x.add(new Interval("20170101", "20170430"));// 20170101-20170430
                x.add(new Interval("20170101", "20170331"));
                x = merge(x);

        for (Interval i1 : x) {
            System.out.println(i1.getStartDate() + " " + i1.getEndDate());

        }

    }

    public static ArrayList<Interval> merge(ArrayList<Interval> intervals) {


        if (intervals.size() == 0 || intervals.size() == 1)
            return intervals;

        ArrayList<Interval> result = new ArrayList<Interval>();

        Collections.sort(intervals, new IntervalComparator());


        System.out.println("intervals ggggg\n" + intervals + "\n");

        Interval first = intervals.get(0);

        String start = first.getStartDate();
        String end = first.getEndDate();
        Date startDateF = null;
        Date endDateF = null;

        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            startDateF = sdf.parse(start);
            endDateF = sdf.parse(end);


            // ArrayList<Interval> result = new ArrayList<Interval>();

            for (int i = 1; i < intervals.size(); i++) {

                Interval current = intervals.get(i);

                Date currentEndDate = sdf.parse(current.getEndDate());
                Date currentStartDate = sdf.parse(current.getStartDate());

                // if ((current.getStartDate().after(endDateF)) ||
                                Date d1 = minDate(endDateF, currentStartDate);

                                if ((currentStartDate).compareTo(endDateF) <= 0) {
                    endDateF = maxDate(currentEndDate, endDateF);

                                                        } else {

                                                            result.add(new Interval(start, (sdf.format(endDateF))));

                    // start = current.start;
                    // end = current.end;

                    start = sdf.format(currentStartDate);
                    endDateF = (currentEndDate);



    enter code here

                }

            }
            result.add(new Interval(start, end));
            // result.add(new Interval(start, (sdf.format(endDateF))));
        }

        catch (ParseException ex) {
            ex.printStackTrace();
        }

        // result.add(new Interval(start, end));

        return result;
        // return intervals;

    }

    public static Date minDate(Date date1, Date date2) {
        // if date1 before date2 then return date1 else return date2

        return date1.before(date2) ? date1 : date2;
    }

    /**
     * find Max Dates
     * 
     * @param date1
     * @param date2
     * @return
     */
    public static Date maxDate(Date date1, Date date2) {
        // if date1 after date2 then return date1 else return date2
        System.out.println("date max");
        return date1.after(date2) ? date1 : date2;
    }
}
Jay
  • 1,089
  • 12
  • 29
juniper
  • 11
  • 2
  • 4
  • 1
    As an aside, I recommend you scrap the long outdated classes `Date` and `SimpleDateFormat`. The modern Java date & time API is so much better, so much nicer to work with. The modern `LocalDate` class also fits your need just fine, while with `Date` you are dragging a time-of-day with you that you would really prefer to avoid. See for example the [tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Sep 11 '17 at 05:06
  • Further suggestion: Your `Interval` class could hold `LocalDate` objects rather than strings (or even `Date` objects if you really insist). The constructor may still accept strings and parse them. This way you need to parse the strings only once rather than every time you need to compare them (the hacker version would be to compare the strings directly, since they are `yyyyMMdd` that would work too). – Ole V.V. Sep 11 '17 at 05:10
  • See the `LocalDateRange` class in the ThreeTen-Extra project. – Basil Bourque Sep 11 '17 at 05:14
  • 1
    Why do you not want your example intervals to one single interval [2017-01-01/2017-05-01) assuming that you want half-open intervals (although I and many other people are rather accustomed to see closed date intervals)? You should really clarify the state of your interval boundaries and why not to merge abutting intervals (there is no gap!). – Meno Hochschild Sep 11 '17 at 07:23
  • yes You are right! .By wrongly I mentioned expected result .Result should come like one single interval [2017-01-01/2017-05-01] – juniper Sep 11 '17 at 14:38
  • because of the enddate is coming as start date of another .so need to combine as one interval [2017-01-01/2017-05-01] – juniper Sep 11 '17 at 14:40
  • Ok, then the easiest solution would be offered by my lib [Time4J](https://github.com/MenoData/Time4J), see the [API](http://time4j.net/javadoc-en/net/time4j/range/IntervalCollection.html#withBlocks--). Example for creation of date intervals, see [my gist](https://gist.github.com/MenoData/49fa10691baf0318dd04). Then add such intervals to an `IntervalCollection` and call `withBlocks()`. Done. – Meno Hochschild Sep 12 '17 at 04:24

3 Answers3

1

ISO 8601

Use standard ISO 8601 formats when serializing date-time values to text. Your format complies with the “basic” version of the standard, but better to use the full format when possible:

YYYY-MM-DD

Use the standard format for a date range, using a slash character as separator:

YYYY-MM-DD/YYYY-MM-DD

If you cannot alter the input strings, split the string on the hyphen. Parse each piece as a LocalDate. Use those objects to instantiate a LocalDateRange.

LocalDate ld = LocalDate.parse( "20170101" , DateTimeFormatter.BASIC_ISO_DATE ) ; 

LocalDateRange

Use the LocalDateRange class from the ThreeTen-Extra project which extends java.time class functionality. Uses the standard format when parsing and generating text.

LocalDateRange range = LocalDateRange.parse( "2017-01-01/2017-03-31" ) ;

Collect in a List<LocalDateRange>.

To sort, write a comparator that calls LocalDateRange::getStart.

Compare to another range to see if they overlap. If so, combine with a call to union.

if ( range.overlaps( otherRange ) ) {
    range = range.union( otherRange ) ;
}

If they do not overlap, you have finished that round. Store this result in another List<LocalDateRange>. Start another round with the next range.

Lather, rinse, repeat.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

I assume your Interval is something like this:

    private static class Interval {
    private String begin;
    private String end;

    public Interval(String begin, String end) {
        this.begin = begin;
        this.end = end;
    }

    public String getStartDate() {
        return begin;
    }

    public String getEndDate() {
        return end;
    }
}

What you need to do is to merge a Interval list. A solution is sort list with start date then end date. And then store the earliest start time and latest end time in a cursor variable. A example:

    public List<Interval> merge(List<Interval> intervals) {
    Collections.sort(intervals, new Comparator<Interval>() {
        @Override
        public int compare(Interval o1, Interval o2) {
            if (o1.getStartDate().equals(o2.getStartDate())) {
                return o1.getEndDate().compareTo(o2.getEndDate());
            }
            return o1.getStartDate().compareTo(o2.getStartDate());
        }
    });
    List<Interval> ret = new ArrayList<>();
    String MAX_VAL = "99999999";
    String MIN_VAL = "00000000";
    String start = MAX_VAL, end = MIN_VAL;

    for (Interval interval : intervals) {
        if (interval.getStartDate().compareTo(end) > 0) {
            if (start.compareTo(MAX_VAL) < 0) {
                ret.add(new Interval(start, end));
            }
            start = interval.getStartDate();
            end = interval.getEndDate();
        } else {
            if (start.compareTo(interval.getStartDate()) < 0) {
                start = interval.getStartDate();
            }
            if (end.compareTo(interval.getEndDate()) > 0) {
                end = interval.getEndDate();
            }
        }
    }
    if (start.compareTo(MAX_VAL) < 0) {
        ret.add(new Interval(start, end));
    }
    return ret;
}
Ke Li
  • 942
  • 6
  • 12
0
public static void main(String[] args) {
    // TODO Auto-generated method stub


    ArrayList<MainLab.Interval> list = new ArrayList<MainLab.Interval>();


    list.add(new MainLab.Interval("20170430", "20170501"));
    list.add(new MainLab.Interval("20170101", "20170430"));
    list.add(new MainLab.Interval("20170101", "20170331"));



for (Iterator iterator = mergeInterval(list).iterator(); iterator.hasNext();) {
    Interval interval = (Interval) iterator.next();

    System.out.println(interval.getStart()+ "==="+interval.getEnd());
}




}

public static List<Interval>  mergeInterval(ArrayList<MainLab.Interval> list){

    /*
     * Sort the list , Interval class have implemented Comparable Interface.
     *  So we will get sorted intervals. Intervals sorted based on start of interval
     */
    Collections.sort(list);
    Set<MainLab.Interval> resultlist = new TreeSet<MainLab.Interval>();

    List<MainLab.Interval> mergedIntervals = new ArrayList<MainLab.Interval>();

    //declare date formate to parse and format date from string to and from
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    if(list.size() == 1){
        //resultlist = list
        return list;
    }
    if(list.size() > 1){
        // get first interval Object. conside it as first interval
        Interval mergeInterval = list.get(0);

        // loop other intervals from second in the list
        for(int i=1; i< list.size() ; i++){

            Interval interval2 = list.get(i);
            try{


                Date startDate1  = sdf.parse(mergeInterval.getStart());
                Date endDate1  = sdf.parse(mergeInterval.getEnd());



                Date startDate2  = sdf.parse(interval2.getStart());
                Date endDate2  = sdf.parse(interval2.getEnd());


                // compare if current interval's start date is before merging interval's end date
                // then the two intervals are overlaping
                if(startDate2.compareTo(endDate1) < 0 ){

                    // check whether end date of current loop interval is after the merging interval.
                    // then we need to update the end date of merging interval with looping interval's end date
                    if(endDate2.compareTo(endDate1) > 0 ){

                        mergeInterval.setEnd(interval2.getEnd());

                    }
                }else{
                    // compare if current interval's start date is after merging interval's end date
                    // then it must be a new interval start so swap mergInterval variable with  current looping interval

                     mergeInterval = interval2;

                }

                //add merge interval to set. 
                resultlist.add(mergeInterval);
            }catch(Exception ex){
                ex.printStackTrace();
            }

        }

    }
    mergedIntervals.addAll(resultlist);
    return mergedIntervals;

}

public static class Interval implements Comparable<Interval>{

    private String start;
    private String end;

    public String getStart() {
        return start;
    }
    public void setStart(String start) {
        this.start = start;
    }
    public String getEnd() {
        return end;
    }
    public void setEnd(String end) {
        this.end = end;
    }
    public Interval(){



            }
    public Interval(String start,String end){

        this.start = start;
        this.end = end;

    }
        @Override
        public boolean equals(Object obj) {
            // TODO Auto-generated method stub
            Interval inteval = (Interval)obj;
            return this.getStart().equals(inteval.getStart()) && this.getEnd().equals(inteval.getEnd()) ;
        }

    @Override
    public int compareTo(Interval o) {
        // TODO Auto-generated method stub

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        try{
            Date startDate  = sdf.parse(start);
            Date endDate  = sdf.parse(end);
            Date pstartDate  = sdf.parse(o.start);
            Date pendDate  = sdf.parse(o.end);


            return startDate.compareTo(pstartDate);

        }catch(Exception ex){
            ex.printStackTrace();
        }
        return 0;
    }


}