我有两个对象日历
Calendar startCalendar = new GregorianCalendar(2013,0,31);
Calendar endCalendar = new GregorianCalendar();
我想知道上面列出的两个日期之间的间隔是否被n个其他对象覆盖,日历之间没有间隔的日历
例1:
Calendar startCalendar1(2013,0,31);
Calendar endCalendar1(2014,0,31);
Calendar startCalendar2(2013,5,31);
Calendar endCalendar2();
好吗
例2:
Calendar startCalendar1(2013,0,31);
Calendar endCalendar1(2014,0,31);
Calendar startCalendar2(2014,2,31);
Calendar endCalendar2();
不好
我使用Java 6 感谢
答案 0 :(得分:3)
1粗鲁但简单的方法
使用Set<长>
Set<Long> all_times_in_milli=new HashSet<Long>();
// Put every interval
// interval 1
for (long time_in_millis=startCalendar1.getTimeInMillis();
time_in_millis<= endCalendar1.getTimeInMillis();
time_in_millis+=86400000)
all_times_in_milli.add(time_in_millis);
// interval 2
for (long time_in_millis=startCalendar2.getTimeInMillis();
time_in_millis<= endCalendar2.getTimeInMillis();
time_in_millis+=86400000)
all_times_in_milli.add(time_in_millis);
// ETC
// AND TEST !
boolean failed=false;
for (long time_in_millis=startCalendar.getTimeInMillis();
time_in_millis<= endCalendar.getTimeInMillis();
time_in_millis+=86400000)
{
if (all_times_in_milli.contains(time_in_millis))
{
failed=true; break;
}
}
if (failed) System.out.println("Your are done !");
2智能方法 因为每个区间都是[长 - 长]间隔
答案 1 :(得分:3)
第一种方法:仅使用Java 6
当我看到您的日期示例如2015-01-31
时,我强烈怀疑您谈论关闭日期间隔,否则选择月末可能会显得有些奇怪。这是一种广泛而合理的方法。不幸的是,选择代表瞬间的java.util.Calendar
数据类型(也是日期 - 时区 - 组合)也与闭合间隔不一致。这种类似瞬间的类型在半开间隔时更好地工作。结果是:
如果您决定只使用Java-6类型,那么您可以尝试将所有Calendar
- 对象转换为表示自Unix时代以来经过的毫秒数的长值,如@guillaume girod-vitouchkina所建议的那样我的upvote作为一个例子,如何在没有任何外部库的情况下执行此操作)。 但是您必须提前为每个Calendar
- 对象(如果表示结束边界)添加额外的一天,以达到关闭日期间隔的效果。
当然,你仍然需要自己做一些本土间隔算法,如粗略地回答那样。如果您仔细研究其他提案和您自己的要求,您会发现最终解决方案甚至不仅需要新的间隔类或间隔的基本比较。您还需要一个更高的抽象层,即在几个区间之间定义的操作。所有这一切都可能会引起一些头痛。另一方面:如果您具有良好的编程技能,实现基于长的区间运算可能会节省一些额外区间库的典型性能开销。
第二种方法:使用专用间隔库
我只知道四个承诺处理间隔的库。 @Basil Bourque提到的 Threeten-Extra 无法使用,因为它需要Java-8。它的间隔类的缺点是只处理瞬间,而不是日历日期。几乎没有支持处理间隔集合。对于 Joda-Time (至少在Java-6上工作,并且还提供专用的日历日期类型,即LocalDate
但没有日期间隔),也可以这样说。
一个有趣的选择是使用 Guava 及其类RangeSet,特别是如果您决定继续使用Calendar
- 对象和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();
说明:如果从测试中减去i1和i2的剩余部分为空,则测试间隔由间隔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未涵盖的额外功能。
答案 2 :(得分:2)
您使用的旧日期时间类已被Java 8及更高版本中的java.time框架取代。事实证明,这些旧班级很笨拙,容易混淆,而且存在缺陷。
新的java.time课程受到非常成功的Joda‑Time库的启发,该库旨在作为其继承者,在概念上类似但重新设计。由JSR 310定义。由ThreeTen‑Extra项目扩展。请参阅Tutorial。
新类包含LocalDate
,仅适用于没有时间的日期值。出于您的目的,请使用此代替Calendar
。
请注意,月份数字明智地从一个开始,与LocalDate start = LocalDate.of( 2013 , 1 , 31 );
不同。
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
LocalDate today = LocalDate.now ( zoneId );
请注意,为了确定日期,时区至关重要。世界各地的日期并不一样。例如,巴黎的新日早些时候比蒙特利尔要早。
isBefore
从那里,您可以调用isAfter
,isEqual
或LocalDate
的任意组合来完成您的逻辑。你的问题并不完全清楚这个逻辑,所以我无法解决这个问题。
扩展java.time的ThreeTen-Extra项目包括一个可以帮助你的Interval
类。不幸的是,该类仅适用于Instant
个对象(UTC中的日期时间),而不适用于IntervalLD
。具体而言,比较区间的方法会有所帮助,abuts
,encloses
和overlaps
。
您可以为LocalDate对象添加自己的LocalDate
类。通常我不建议滚动你自己的日期时间处理类,因为日期工作是非常棘手的。但在这种情况下,使用package com.example.javatimestuffmaven;
import java.time.LocalDate;
/**
* Similar to the 'Interval'class in the ThreeTen-Extra project, but for LocalDate objects.
*
* @author Basil Bourque
*/
public class IntervalLD {
private LocalDate start, end;
// Constructor
public IntervalLD ( LocalDate startArg , LocalDate endArg ) {
this.start = startArg;
this.end = endArg;
}
public Boolean isBefore ( IntervalLD interval ) {
// True if this one's end is before that one's start.
boolean before = this.getEnd ().isBefore ( interval.getStart () );
return before;
}
public Boolean isAfter ( IntervalLD interval ) {
// True if this one's start is after that one's end.
boolean after = this.getStart ().isAfter ( interval.getStart () );
return after;
}
public Boolean abuts ( IntervalLD interval ) {
// True if the intervals are next to each other on the time line but do not share a date. (exclusive of each other, not half-open)
// True if either one's end is a day ahead of the other's start or vice versa, either's start is day after the other's end.
if ( this.isBefore ( interval ) ) {
if ( this.getEnd ().plusDays ( 1 ).equals ( interval.getStart () ) ) {
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
} else if ( this.isAfter ( interval ) ) {
if ( this.getStart ().minusDays ( 1 ).equals ( interval.getEnd () ) ) {
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
} else if ( this.isEqual ( interval ) ) {
return Boolean.FALSE;
}
// Impossible. Should never reach this point.
// TODO: Handle this error condition.
return Boolean.FALSE;
}
public Boolean encloses ( IntervalLD interval ) {
//This checks if the specified interval is fully enclosed by this interval.
// The result is true if the start of the specified interval is contained in this interval, and
// the end is contained or equal to the end of this interval.
boolean thatOneStartsOnOrAfterThisOne = ! interval.getStart ().isBefore ( this.getStart () );
boolean thatOneEndsOnOrAfterThisOne = ! interval.getEnd ().isAfter ( this.getEnd () );
boolean doesEnclose = ( thatOneStartsOnOrAfterThisOne && thatOneEndsOnOrAfterThisOne );
return doesEnclose;
}
public Boolean overlaps ( IntervalLD interval ) {
// True if the two intervals share some part of the timeline.
// True if this interval does NOT start after that one ends OR this interval does NOT end before that one starts.
boolean startsTooLate = this.getStart ().isAfter ( interval.getEnd () );
boolean endsTooEarly = this.getEnd ().isAfter ( interval.getEnd () );
boolean doesOverlap = ( ! startsTooLate && ! endsTooEarly );
return ( doesOverlap );
}
public Boolean isEqual ( IntervalLD interval ) {
boolean sameStart = this.getStart ().isEqual ( interval.getStart () );
boolean sameEnd = this.getEnd ().isEqual ( interval.getEnd () );
return ( sameStart && sameEnd );
}
@Override
public String toString () {
String output = this.getStart () + "/" + this.getEnd ();
return output;
}
// Getters. Read-only (immutable) so no Setters.
/**
* @return the start
*/
public LocalDate getStart () {
return this.start;
}
/**
* @return the end
*/
public LocalDate getEnd () {
return this.end;
}
}
逻辑可能很简单。这是我快速草拟完全未经测试的示例,以帮助您入门。
var startButton = SKNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
startButton = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 100, height: 50))
startButton.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
self.addChild(startButton)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
let gameSceneTransition = GameScene(fileNamed: "GameScene")
let touch = touches
let location = touch.first!.locationInNode(self)
let node = self.nodeAtPoint(location)
// If next button is touched, start transition to second scene
if (node.name == "startButton") {
self.scene?.view?.presentScene(gameSceneTransition!, transition: SKTransition.fadeWithDuration(1.0))
}
}