First approach: Only using Java 6
When I see your date examples like 2015-01-31
then I get the strong suspicion that you speak about closed date intervals otherwise choosing the end of a month might appear a little bit strange. This is a wide spread and reasonable approach. Unfortunately choosing a data type like java.util.Calendar
representing an instant (also a date-time-zone-combo, too) is not in harmony with closed intervals. Such instant-like types work better with half-open intervals. The consequence is:
If you decide to use only Java-6-types then you can try to convert all Calendar
-objects to Long-values representing the elapsed millisecs since Unix epoch as suggested by @guillaume girod-vitouchkina (has got my upvote as an example how to do this without any external library) . But you have to add an extra day to every single Calendar
-object (if representing an end boundary) in advance to achieve the effect of closed date intervals.
And of course, you have still to do some home grown interval arithmetic yourself as shown in that answer in a sketchy way. If you carefully study the other proposals and your own requirements you will find that the final solution even requires more than just a new interval class or basic comparisons of intervals. You will also need a higher abstraction layer, namely defined operations between several intervals. Doing this all yourself might cause some headache. On the other hand: Implementing a Long-based interval arithmetic might save some performance overhead as typical for an extra interval library if you have good programming skills.
Second approach: Using a dedicated interval library
I only know four libraries which promise to handle intervals. Threeten-Extra as mentioned by @Basil Bourque cannot be used because it requires Java-8. Its interval class has the disadvantage to handle instants only, but not calendar dates. There is also almost no support for handling collections of intervals. The same can be said for Joda-Time (which is at least working on Java-6 and also offers a dedicated calendar date type, namely LocalDate
but no date intervals).
An interesting option is using Guava and its class RangeSet, especially if you decide to continue using Calendar
-objects and Longs. This class has some support for handling operations between intervals - for me much more appealing than using the simple interval class of Joda-Time.
Finally you also have the option to use my library Time4J which has the range-package. I will show now a complete solution for your problem:
// our test interval
PlainDate start = PlainDate.of(2013, Month.JANUARY, 31);
PlainDate end = SystemClock.inLocalView().today();
DateInterval test = DateInterval.between(start, end);
IntervalCollection<PlainDate> icTest = IntervalCollection.onDateAxis().plus(test);
// two intervals for your GOOD case
PlainDate s1 = PlainDate.of(2013, Month.JANUARY, 31);
PlainDate e1 = PlainDate.of(2014, Month.JANUARY, 31);
DateInterval i1 = DateInterval.between(s1, e1);
PlainDate s2 = PlainDate.of(2013, Month.MAY, 31);
PlainDate e2 = end; // today
DateInterval i2 = DateInterval.between(s2, e2);
IntervalCollection<PlainDate> goodCase =
IntervalCollection.onDateAxis().plus(i1).plus(i2);
boolean covered = icTest.minus(goodCase).isEmpty();
System.out.println("Good case: " + covered); // true
// two intervals for your BAD case
PlainDate s3 = PlainDate.of(2013, Month.JANUARY, 31);
PlainDate e3 = PlainDate.of(2014, Month.JANUARY, 31);
DateInterval i3 = DateInterval.between(s3, e3);
PlainDate s4 = PlainDate.of(2014, Month.MARCH, 31);
PlainDate e4 = end; // today
DateInterval i4 = DateInterval.between(s4, e4);
IntervalCollection<PlainDate> badCase =
IntervalCollection.onDateAxis().plus(i3).plus(i4);
covered = icTest.minus(badCase).isEmpty();
System.out.println("Bad case: " + covered); // false
The biggest part of code is just interval construction. The real interval arithmetic itself is done by this surprisingly small code fragment:
boolean covered =
IntervalCollection.onDateAxis().plus(test).minus(
IntervalCollection.onDateAxis().plus(i1).plus(i2)
).isEmpty();
Explanation: The test interval is covered by intervals i1 and i2 if the remainder of the subtraction of i1 and i2 from test is empty.
By the way: Date intervals in Time4J are closed intervals by default. You can change these intervals to half open intervals however if you really want (simply by calling withOpenEnd()
on a given date interval).
And if you plan to migrate to Java-8 later, you can just update the Time4J-version to version line 4.x (version v3.x is for Java-6) and get very easy conversions to Java-8 types like java.time.LocalDate
(for example: PlainDate.from(localDate)
or LocalDate ld = plainDate.toTemporalAccessor()
) so you can continue to use Time4J for extra features not covered by standard Java even in the future.