3

i'm trying to solve a seemingly simple problem, but just can't quite get my mind around it.

i have two times startTime and stopTime, which can be considered to be in the format: hh:mm:ss [24hr format].

Now given a third time - timeToTest - i need to find out if timeToTest lies between startTime and stopTime. There is no date information involved, other than just the times.

So for example - if i have startTime = '22:30:00' and stopTime = '03:30:00', then for timeToTest = '01:14:23', the test should return true.

I've tried a solution with java.util.Date by converting the times to milliseconds using getTime(), but with any interval which rolls over the 24 hr barrier, the logic fails.

I'm trying to build a solution using Java - but i believe the logic is language independent.

anirvan
  • 4,797
  • 4
  • 32
  • 42

6 Answers6

4

So the simplest solution i could come up with, sticking to plain old java.util.Date, is shown below:

    String d1 = "21:00:00";
    String d2 = "04:00:00";
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    String dToTest = "16:00:00";
    boolean isSplit = false, isWithin = false;

    Date dt1 = null, dt2 = null,  dt3 = null;

    dt1 = sdf.parse(d1);
    dt2 = sdf.parse(d2);
    dt3 = sdf.parse(dToTest);

    isSplit = (dt2.compareTo(dt1) < 0);
    System.out.println("[split]: " +isSplit);

    if (isSplit)
    {
        isWithin = (dt3.after(dt1) || dt3.before(dt2));
    }
    else
    {
        isWithin = (dt3.after(dt1) && dt3.before(dt2));
    }

    System.out.println("Is time within interval? " +isWithin);

feel free to point out any mistakes - would love to work and fix it.

anirvan
  • 4,797
  • 4
  • 32
  • 42
2

You must add a "day" where "0" == current day, "1" == next day and so on. So in fact when stopTime == '03:30:00' it should be '27:30:00' (i.e. on the next day).

In your case, if the stopTime < startTime, then add 86400 seconds.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • aaron, i've tried the approach of adding a day to `stopTime` when it is smaller than `startTime`. however, the problem i face is - when do i decide to add a day to the `timeToTest`. For example, i should add a day to `timeToTest` when it is > `00:00:00` but < `stopTime`. but when it is < `00:00:00` i shouldn't. Also, since i need to run this logic over a data-set, i somehow feel putting so many checks and creating new Date objects might lead to a performance overhead. – anirvan Jul 21 '10 at 10:12
  • Well, if the time is after midnight, then it is on the next day. Your problem is that the "day" information is thrown away and there is no safe way to recreate it from the piece you're left with. The only safe solution is to keep this important fact when the data is created. – Aaron Digulla Jul 21 '10 at 14:41
  • Referring to performance: Start to worry about it when your solution works **and** is slow. Avoid premature optimization. – Aaron Digulla Jul 21 '10 at 14:42
2

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Also, quoted below is a notice from the home page of Joda-Time:

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

Solution using java.time, the modern Date-Time API:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

public class Main {
    public static void main(String[] args) {
        String strStartTime = "22:30:00", strStopTime = "03:30:00", strTestTime = "01:14:23";
        LocalDate today = LocalDate.now();

        LocalDateTime startTime = today.atTime(LocalTime.parse(strStartTime));

        LocalDateTime stopTime = today.atTime(LocalTime.parse(strStopTime));
        if (stopTime.isBefore(startTime))
            stopTime = stopTime.plusDays(1);

        LocalDateTime testTime = today.atTime(LocalTime.parse(strTestTime));
        if (testTime.isBefore(startTime))
            testTime = testTime.plusDays(1);

        if (!testTime.isBefore(startTime) && !testTime.isAfter(stopTime))
            System.out.println(strTestTime + " is at or after " + strStartTime + " and is before or at " + strStopTime);
    }
}

Output:

01:14:23 is at or after 22:30:00 and is before or at 03:30:00

ONLINE DEMO

Note: If the start time and stop time are not inclusive, change the condition as follows:

if (testTime.isAfter(startTime) && testTime.isBefore(stopTime))
    System.out.println(strTestTime + " is after " + strStartTime + " and is before " + strStopTime);

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
2

anirvan's solution using JodaTime :

public class TimeInterval24H {
    private final LocalTime start;
    private final LocalTime end;

    public TimeInterval24H(LocalTime start, LocalTime end) {
        this.start = start;
        this.end = end;
    }

    public TimeInterval24H(Date start, Date end) {
        this(new LocalTime(start), new LocalTime(end));
    }

    public boolean contains(Date test) {
        return contains(new LocalTime(test));
    }

    public boolean contains(LocalTime test) {
        if (isAccrossTwoDays()) {
            return (test.isAfter(getStart()) || test.isBefore(getEnd()));
        } else {
            return (test.isAfter(getStart()) && test.isBefore(getEnd()));
        }
    }

    boolean isAccrossTwoDays() {
        return getEnd().isBefore(getStart());
    }

    public LocalTime getStart() {
        return start;
    }

    public LocalTime getEnd() {
        return end;
    }

}
user639466
  • 103
  • 1
  • 6
1

How about:

  • Find the next occurrence of the specified time after the start instant
  • Check whether that occurrence is before the end instant or not

The first step can probably be broken down pretty easily:

  • Is the specified time on/after the time of the start instant?
    • Yes: the next occurrence is that time on the same day as the start instant
    • No: the next occurrence is that time on the next day from the start instant

All of this is likely to be somewhat easier to write in Joda Time than using java.util.*, by the way :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Indeed, Joda Time is very nice, and maybe the new JSR-310 https://jsr-310.dev.java.net/ implementation would solve many of the ugly problems with java.util.Date. – A. Ionescu Jul 21 '10 at 09:40
  • jon, well that's the logic i've been working on too. however, things like finding the next occurrence of the specified time is something i am finding hard or rather convoluted to figure out [using java.util.Date]. I'm looking into Joda, but haven't seen something which simplifies that task yet. – anirvan Jul 21 '10 at 10:23
  • @anirvan: Joda Time makes it easy to separate out the ideas of "LocalDateTime", "LocalDate" and "LocalTime". Things get confusing if you go for full DateTime values, where there could be DST changes involved... for example, occasionally you could have a start of 10pm and a finish of 5pm the next day - but not have a 1.30am between the two... – Jon Skeet Jul 21 '10 at 10:34
0

I strongly recommend java.util.Calendar, the before() and after() can be useful. However, you'll need a date like 5/18/2011 specified together with your time. Is it possible to specify a mock date (or a pair of date in your case) to leverage the Calendar?

Evi Song
  • 862
  • 11
  • 14