【问题标题】:Date interval sum and subtraction in JavaJava中的日期间隔和和减法
【发布时间】:2015-03-24 08:57:30
【问题描述】:

我正在寻找一个 Java 库或帮助类,它允许我执行日期间隔求和和减法。

例如,假设我有以下日期间隔:

A = ["2015-01-01 00:00", "2015-01-20 00:00"]
B = ["2015-01-05 00:00", "2015-01-10 00:00"]
C = ["2015-01-11 00:00", "2015-01-14 00:00"]
D = ["2015-01-19 00:00", "2015-01-25 00:00"]

1                  A               20
|----------------------------------|
    |---------|   |----------|   |------------|
    5    B    10  11    C    14  19    D      25

假设我想计算以下内容:

A - B - C + D = { ["2015-01-01 00:00", "2015-01-05 00:00"[,
                  ]"2015-01-10 00:00", "2015-01-11 00:00"[,
                  ]"2015-01-14 00:00", "2015-01-25 00:00"] }

1   5         10  11         14               25
|---|         |---|          |----------------|

我知道我可以使用纯 Java 构建自己的逻辑,但我不想重新发明轮子...

我正在研究Joda-Time,但我不知道如何使用它执行此类操作。

非常感谢!

【问题讨论】:

  • 顺便提一下,Joda Time 已作为新的日期/时间 API 集成到 Java 8 中。
  • 有趣!但它能够执行我描述的操作吗?
  • 我认为你的意思是“联合”和“交集”,而不是和和减法......
  • @Michal,我不确定我是否关注你。 Days#daysBetween 方法计算一个周期,但我需要一个区间(或区间列表)。

标签: java date sum intervals subtraction


【解决方案1】:

我认为基本上可以使用 Joda-Time 和一些自定义代码来完成。假设A 是所有其他间隔应该相关的间隔。

虽然此代码应该给出预期的结果(并且应该相应地适用于不同的值),但我强烈建议使用非常不同的数据对其进行测试,尤其是对于这三种情况 a) 一个区间根本不与 A 相交,b) 相交A 开头和 c) 与 BCD 相交的区间。

因此,尽管如此,它可能有助于进一步的测试。

Interval a = new Interval(Instant.parse("2015-01-01T00:00Z"), Instant.parse("2015-01-20T00:00Z"));
List<Interval> l = Arrays.asList(
        /* b */ new Interval(Instant.parse("2015-01-05T00:00Z"), Instant.parse("2015-01-10T00:00Z")),
        /* c */ new Interval(Instant.parse("2015-01-11T00:00Z"), Instant.parse("2015-01-14T00:00Z")),
        /* d */ new Interval(Instant.parse("2015-01-19T00:00Z"), Instant.parse("2015-01-25T00:00Z"))
);
List<Interval> results = new ArrayList<Interval>();

for (Interval i : l) {
    if (a.contains(i)) {
        // if i is completely inside a, then calculate the first part and the remaining part
        //   whereas the first part will be added to the result
        Interval firstPart = new Interval(a.getStart(), i.getStart());
        results.add(firstPart);
        // followed by i itself (skipped)
        // part after i, inside a
        Interval remainingPart = new Interval(i.getEnd(), a.getEnd());
        a = remainingPart;
    } else if (i.overlaps(a)) {
        // if the intervals only overlap, then we take the earliest beginning and the latest ending as a result part
        DateTime overlapMin = (a.getStart().isBefore(i.getStart())) ? a.getStart() : i.getStart();
        DateTime overlapMax = (a.getEnd().isAfter(i.getEnd())) ? a.getEnd() : i.getEnd();
        Interval overlapAndBothParts = new Interval(overlapMin, overlapMax);
        results.add(overlapAndBothParts);
        // if the checked interval i is at the beginning, then a will become the part after this "overlap"
        if (i.getStartMillis() < a.getStartMillis()) {
            Interval whatsLeft = new Interval(i.getEndMillis(), a.getEndMillis());
            a = whatsLeft;
        }
    }
}

// print result
for (Interval i : results) {
    System.out.println("result part: " + i);
}

【讨论】:

  • 非常感谢@jCoder 的帮助。这个解决方案的唯一问题是它不允许我指定我想要的操作(和/联合或减法)。如果我理解正确,它会在 A 包含另一个区间的情况下执行减法,并在区间与 A 重叠的情况下执行求和/并集。无论如何,我会尝试编写一个代码来根据你的问题解决我的问题。跨度>
【解决方案2】:

我找到了我需要的东西:Ranges,来自guava-libraries

像这样工作:

Range<Date> a = Range.closed(
    new GregorianCalendar(2015, 0, 1).getTime(),
    new GregorianCalendar(2015, 0, 20).getTime());
Range<Date> b = Range.closed(
    new GregorianCalendar(2015, 0, 5).getTime(),
    new GregorianCalendar(2015, 0, 10).getTime());
Range<Date> c = Range.closed(
    new GregorianCalendar(2015, 0, 11).getTime(),
    new GregorianCalendar(2015, 0, 14).getTime());
Range<Date> d = Range.closed(
    new GregorianCalendar(2015, 0, 19).getTime(),
    new GregorianCalendar(2015, 0, 25).getTime());

RangeSet<Date> result = TreeRangeSet.create();
result.add(a);
result.remove(b);
result.remove(c);
result.add(d);

System.out.println(result);

上面的代码打印出来:

[
    [Thu Jan 01 00:00:00 BRST 2015‥Mon Jan 05 00:00:00 BRST 2015),
    (Sat Jan 10 00:00:00 BRST 2015‥Sun Jan 11 00:00:00 BRST 2015),
    (Wed Jan 14 00:00:00 BRST 2015‥Sun Jan 25 00:00:00 BRST 2015]
]

【讨论】:

  • 是的,如果您仍想继续使用GregorianCalendar 等,这是一个很好的解决方案。但是,这些类还带有时区信息(未包含在您的原始问题中)。另一个你可能会感兴趣的库是我的库 Time4J,尤其是带有类 IntervalCollection 的 range-package
  • 有趣,@MenoHochschild。我去看看你的图书馆。但是,RangeRangeSet 都是通用的,所以我可以选择一个不携带时区信息的日期类(只要它实现了Comparable 接口)。
  • 没错,Guava 也可以与 Joda-Time-classes 或 Java-8-classes 如LocalDate 等结合使用。这比尝试直接使用 Joda-Time-Interval 要好得多(参见其他与 Joda 相关的答案)。关于我的库,我将在下一个版本中实现缺少的减号()操作间隔(尽管可以解决)。
猜你喜欢
  • 1970-01-01
  • 2014-12-17
  • 2023-03-29
  • 2015-06-21
  • 2015-03-12
  • 2014-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多