0

I have many timestamps (start, end) which define an interval and want to efficiently check if they overlap another single interval. If yes, compute overlap duration, otherwise return 0.

interval: 18:00 same day until 08:00 the next day.

start                  | end
2018-01-02 14:59:18.922|2018-01-02 14:59:38.804
2018-01-02 18:32:59.348|2018-01-02 20:30:41.192
2018-01-02 01:54:59.363|2018-01-02 01:54:59.363
2018-01-03 00:10:38.831|2018-01-03 00:11:53.103

I am unsure how to efficiently define the next day efficiently.

edit

LocalDate

has a method toInterval().overlaps(anotherInterval). I simply am unsure how to get fitting interval (18:00 - 08:00 next day) in a generic way, i.e. without manually reading the YYYMMDD and then creating a new object.

edit 2

toInterval is only present for jodatime - not java.time / JSR-310. What would be a viable way to calculate overlap duration with java.time?

https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap

edit3

A solution with jodaTime:

val begin = new DateTime(new java.sql.Timestamp().getTime())
val stop = new DateTime(new java.sql.Timestamp().getTime())
val i1 = new Interval(begin, stop)

val start = new DateTime(begin.year.get   , begin.monthOfYear.get, begin.dayOfMonth.get, startHour, 0, 0, 0);
val endIntermediate =stop.toDateTime.plusDays(1)
val end = new DateTime(endIntermediate.year.get   , endIntermediate.monthOfYear.get, endIntermediate.dayOfMonth.get, endHour, 0, 0, 0);
val i2 = new Interval(start, end)

val overlap = i1.overlap(i2)
val overlapDurationOrNull = overlap.toDuration

seems to work, but still is clumsy.

Georg Heiler
  • 16,916
  • 36
  • 162
  • 292
  • This is a little unclear. Is `interval: 18:00 same day until 08:00 the next day` the input "interval" to be checked against all periods in the table for overlaps? – ernest_k Jul 22 '18 at 14:14
  • Fore each entry in the samples above I want to check a single interval (18:00-08:00). and if there is any overlap, compute overlap duration. – Georg Heiler Jul 22 '18 at 14:15
  • http://joda-time.sourceforge.net/apidocs/org/joda/time/Interval.html#overlap(org.joda.time.ReadableInterval), http://joda-time.sourceforge.net/apidocs/org/joda/time/base/AbstractInterval.html#toDuration() – JB Nizet Jul 22 '18 at 14:16
  • *how to get fitting interval (18:00 - 08:00 next day) in a generic way* what does that mean, precisely? What is your input, and what is your expected output? Same day as what? Next day of what? – JB Nizet Jul 22 '18 at 14:18
  • I mean that I do not want to extract `yyMMdd` (i.e. the day) from start end end interval) and then manually instantiate a new object which has a fitting date and 18:00 and a second object with the next date and 08:00, but define 18:00-08:00 in a way that they are valid for any input day. – Georg Heiler Jul 22 '18 at 14:19
  • How else could you possibly do? You have a start date, and you want to find the next day of that date, but without extracting the day of the start date? Where would you find it then? – JB Nizet Jul 22 '18 at 14:21
  • So there is no such notion of a relative/generic interval? I.e starting 18:00 today until 08:00 the next day? – Georg Heiler Jul 22 '18 at 14:22
  • Yes, there is. You do that by taking the same day as the original date, and change the hour. – JB Nizet Jul 22 '18 at 14:22
  • With `there is`, you mean I would manually need to emulate it, i.e. extracting the fitting date from start and then begin the calculation? There is no notion of `generics /relative` interval by a java time library? – Georg Heiler Jul 22 '18 at 14:24
  • You can just check if the end of an event is after the start of the next event. – user1803551 Jul 22 '18 at 14:29
  • Yes, you'll have to write code to implement what you want. Joda-time doesn't have a magic method doing exactly what you want. But it has all the building blocks to make it easy to implement. – JB Nizet Jul 22 '18 at 14:40
  • [The `Interval` class in in the ThreeTen Extra library](http://www.threeten.org/threeten-extra/apidocs/org/threeten/extra/Interval.html). Not sure exactly how much it helps, nor whether that library is still being maintained. – Ole V.V. Jul 23 '18 at 07:56
  • Thanks for all the edits, but I am sorry, it is still very unclear. From 18:00 *on which day* to 08:00 the next? I also find it confusing that I don’t see the times 18 and 8 in your Joda-Time solution code. – Ole V.V. Jul 23 '18 at 08:05

2 Answers2

4

I believe that the following method gives you the equivalent of your Joda-Time solution.

private static final LocalTime START = LocalTime.of(18, 0);
private static final LocalTime END = LocalTime.of(8, 0);

public static Duration overlap(ZonedDateTime currentStart, ZonedDateTime currentEnd) {
    ZonedDateTime singleIntervalStart = currentStart.with(START);
    ZonedDateTime singleIntervalEnd = currentStart.plusDays(1).with(END);
    if (currentEnd.isBefore(singleIntervalStart)) {
        // no overlap
        return Duration.ZERO;
    }
    ZonedDateTime overlapStart = currentStart.isBefore(singleIntervalStart)
            ? singleIntervalStart : currentStart;
    ZonedDateTime overlapEnd = currentEnd.isBefore(singleIntervalEnd)
            ? currentEnd : singleIntervalEnd;
    return Duration.between(overlapStart, overlapEnd);
}

For trying it out with the timestamps from your question I am using the following utility method:

private static void demo(String from, String to) {
    ZoneId zone = ZoneId.of("Atlantic/Stanley");
    Duration overlapDuration = overlap(LocalDateTime.parse(from).atZone(zone),
            LocalDateTime.parse(to).atZone(zone));
    System.out.println("" + from + " - " + to + ": " + overlapDuration);
}

Now I call it like this:

    demo("2018-01-02T14:59:18.922", "2018-01-02T14:59:38.804");
    demo("2018-01-02T18:32:59.348", "2018-01-02T20:30:41.192");
    demo("2018-01-02T01:54:59.363", "2018-01-02T01:54:59.363");
    demo("2018-01-03T00:10:38.831", "2018-01-03T00:11:53.103");

And the output is:

2018-01-02T14:59:18.922 - 2018-01-02T14:59:38.804: PT0S
2018-01-02T18:32:59.348 - 2018-01-02T20:30:41.192: PT1H57M41.844S
2018-01-02T01:54:59.363 - 2018-01-02T01:54:59.363: PT0S
2018-01-03T00:10:38.831 - 2018-01-03T00:11:53.103: PT0S

In the first example 14:59 is before 18:00, so the result is an overlap of 0. In the second example the whole interval is counted as overlap (nearly 2 hours). Note that in the last two examples no overlap is reported because the the times are many hours before 18:00. I am unsure whether this is what you wanted since the times are also before 08:00.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • looks really great. That is what I wanted (0). – Georg Heiler Jul 23 '18 at 12:20
  • I think adding " || singleIntervalEnd.isBefore(currentStart)" would be better. Otherwise, nagative value might be returned. – Shengfeng Li Feb 06 '19 at 05:03
  • @ShengfengLi In this case, since `SingleIntervalEnd` is on the next day compared to `currentStart` (it has been initialized that way), it cannot be before. So your addition is not necessary (also does no harm). Thanks for commenting. – Ole V.V. Feb 06 '19 at 06:08
1

You can simply use LocalDate.plusDays to add a day.

Assuming an iteration where the following are to be compared:

LocalDateTime d1 = LocalDateTime.parse("2018-01-02T14:59:18"),
              d2 = LocalDateTime.parse("2018-01-02T14:59:38");

You can create the 18:00 and 08:00 date/time objects using:

LocalDateTime start = LocalDateTime.of(d1.toLocalDate(), LocalTime.of(18, 0));
LocalDateTime end = LocalDateTime.of(d1.toLocalDate().plusDays(1), 
                                       LocalTime.of(8, 0));

I've assumed that 18:00 is on the same day as d1.

ernest_k
  • 44,416
  • 5
  • 53
  • 99