第一种方法:仅使用 Java 6
当我看到像2015-01-31 这样的日期示例时,我强烈怀疑您所说的是关闭的日期间隔,否则选择月底可能会显得有点奇怪。这是一种广泛而合理的做法。不幸的是,选择像java.util.Calendar 这样的数据类型表示一个瞬间(也是一个日期-时区组合)与封闭间隔不一致。这种类似即时的类型在半开区间上效果更好。结果是:
如果您决定只使用 Java-6 类型,那么您可以尝试将所有 Calendar-objects 转换为表示自 Unix 纪元以来经过的毫秒数的长值,如 @guillaume girod-vitouchkina 所建议的(得到了我的赞成作为一个例子,如何在没有任何外部库的情况下做到这一点)。 但是你必须提前给每一个Calendar-object(如果代表一个结束边界)添加一个额外的一天,以达到封闭日期间隔的效果。
当然,您仍然需要自己做一些自学的区间算术,如答案中粗略所示。如果您仔细研究其他建议和您自己的要求,您会发现最终解决方案甚至需要的不仅仅是一个新的区间类或区间的基本比较。您还需要一个更高的抽象层,即在几个间隔之间定义的操作。自己做这一切可能会引起一些头痛。另一方面:如果您具有良好的编程技能,则实现基于 Long 的区间算法可能会节省一些性能开销,这对于额外的区间库来说是典型的。
第二种方法:使用专用区间库
我只知道四个承诺处理间隔的库。 @Basil Bourque 提到的 Threeten-Extra 不能使用,因为它需要 Java-8。它的间隔类的缺点是只能处理瞬间,而不是日历日期。也几乎不支持处理间隔集合。 Joda-Time 也是如此(它至少在 Java-6 上工作,并且还提供专用的日历日期类型,即 LocalDate,但没有日期间隔)。
一个有趣的选择是使用 Guava 及其类RangeSet,特别是如果您决定继续使用Calendar-objects 和Longs。这个类对处理间隔之间的操作有一些支持——对我来说比使用简单的 Joda-Time 间隔类更有吸引力。
最后,您还可以选择使用我的库 Time4J,它有 range-package。我现在将为您的问题提供一个完整的解决方案:
// 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
代码的最大部分只是区间构造。真正的区间运算本身是由这个令人惊讶的小代码片段完成的:
boolean covered =
IntervalCollection.onDateAxis().plus(test).minus(
IntervalCollection.onDateAxis().plus(i1).plus(i2)
).isEmpty();
说明:如果从 test 中减去 i1 和 i2 的余数为空,则 test 区间被区间 i1 和 i2 覆盖。
顺便说一句:Time4J 中的日期间隔默认是闭合间隔。如果您真的需要,您可以将这些间隔更改为半开间隔(只需在给定的日期间隔调用withOpenEnd())。
如果您计划稍后迁移到 Java-8,您只需将 Time4J 版本更新到 4.x 版本(v3.x 版本适用于 Java-6),即可轻松转换为 Java-8 类型像 java.time.LocalDate(例如:PlainDate.from(localDate) 或 LocalDate ld = plainDate.toTemporalAccessor())这样您就可以继续使用 Time4J 来实现标准 Java 未涵盖的额外功能。