第一种方法:仅使用 Java 6
当我看到你的约会例子时2015-01-31
然后我就强烈怀疑你所说的封闭日期间隔否则选择月底可能会显得有点奇怪。这是一种广泛传播且合理的做法。不幸的是选择了这样的数据类型java.util.Calendar
表示瞬间(也是日期-时区-组合)与闭区间不一致。这种类似即时的类型在半开区间工作得更好。结果是:
如果您决定仅使用 Java-6 类型,那么您可以尝试转换所有类型Calendar
-按照 @guillaume girod-vitouchkina 的建议,表示自 Unix 纪元以来经过的毫秒数的 Long 值的对象(已得到我的赞成票作为示例,说明如何在没有任何外部库的情况下执行此操作)。但你必须为每一个额外添加一天Calendar
-object(如果代表结束边界)提前达到封闭日期间隔的效果。
当然,您仍然需要自己做一些自制的区间算术,如该答案中以粗略的方式所示。如果您仔细研究其他提案和您自己的要求,您会发现最终的解决方案甚至需要的不仅仅是一个新的区间类或区间的基本比较。您还需要一个更高的抽象层,即几个间隔之间定义的操作。自己做这一切可能会引起一些头痛。另一方面:如果您具有良好的编程技能,那么实现基于长的间隔算术可能会节省一些性能开销,就像额外间隔库的典型情况一样。
第二种方法:使用专用的间隔库
我只知道四个承诺处理间隔的库。三十额外正如 @Basil Bourque 所提到的,无法使用,因为它需要 Java-8。它的间隔类的缺点是只能处理瞬间,而不能处理日历日期。也几乎不支持处理间隔集合。同样可以这样说乔达时间(它至少在 Java-6 上运行,并且还提供了专用的日历日期类型,即LocalDate
但没有日期间隔)。
一个有趣的选择是使用Guava及其类别RangeSet http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/RangeSet.html,特别是如果您决定继续使用Calendar
- 对象和长整型。这个类对处理间隔之间的操作有一些支持 - 对我来说比使用 Joda-Time 的简单间隔类更有吸引力。
最后您还可以选择使用我的图书馆Time4J其中有范围包 http://time4j.net/javadoc-en/net/time4j/range/package-summary.html。我现在将展示您问题的完整解决方案:
// 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 相减的余数为空,则测试区间被区间 i1 和 i2 覆盖。
顺便说一下:Time4J 中的日期间隔默认为闭间隔。如果您确实想要,您可以将这些间隔更改为半开间隔(只需调用withOpenEnd()
在给定的日期间隔内)。
如果您计划稍后迁移到 Java-8,您只需将 Time4J 版本更新到版本 4.x(版本 v3.x 适用于 Java-6),即可非常轻松地转换为 Java-8 类型,例如java.time.LocalDate
(例如:PlainDate.from(localDate)
or LocalDate ld = plainDate.toTemporalAccessor()
),因此即使在将来,您也可以继续使用 Time4J 来获得标准 Java 未涵盖的额外功能。