2

I am struggling with following problem:

In my app I want to check whether a current datetime from LocalDateTime.now() is in range of some specific days of week, let's say between Friday and Sunday.

Example:

Datetime window is from Friday 20:00 to Sunday 20:00. When I check current date on Thursday at 22:00*, answer should be false but when I check it on **Saturday at 8:00, answer should be true.

I can sort of achieve this with DayOfWeek class in java.time API using compareTo() method and comparing hours and minutes separately but I would like to know if there is any better way to do it and especially when the range of days spans over two weeks (e.g. Friday to Monday of the next week)?

Thanks in advance for any advice or help.

Martin Vrábel
  • 830
  • 1
  • 13
  • 36
  • @Spock I think that topics covers the situation of 2 date ranges and checking whether they overleap. My situation is different. I have only 1 date range and want to check if specific date is in that range. – Martin Vrábel Nov 13 '15 at 20:22
  • 1
    @Supermartzin But it provides many of the concepts you need to achieve your goal – MadProgrammer Nov 13 '15 at 20:23
  • What you're looking for is something like Span: http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/util/Span.java (You can remove the `implements PrimaryKey`) – ControlAltDel Nov 13 '15 at 20:25
  • 2
    Simply using isAfter and isBefore you should be able to determine if a date falls before and/or after two dates – MadProgrammer Nov 13 '15 at 20:25
  • As a conceptual [example](http://stackoverflow.com/questions/30338162/java-datetime-comparison-per-15-minutes/30338317#30338317) – MadProgrammer Nov 13 '15 at 20:29
  • The problem is I don't have a specific datetime in threshold values I have only **day of week** and **time** in that day to make it general and repeative every week. So if I was able to find out what date will be on that days it would be much easier I think. – Martin Vrábel Nov 13 '15 at 20:32
  • So? Combine them into a date/value. The question is the knowing if you want move backwards or forwards from the current date (ie move to the previous Thursday and the next Sunday) – MadProgrammer Nov 13 '15 at 20:35
  • Yeah, that sounds good, but how can I get the date of the next end day and previous start day? – Martin Vrábel Nov 13 '15 at 20:48

4 Answers4

4

Since you want to compare a LocalDateTime to a DayOfWeek+LocalTime, or rather check if it's between two DayOfWeek+LocalTime pairs, a helper class may be good:

public final class DayOfWeekTimeRange {
    private final DayOfWeek fromDay;
    private final LocalTime fromTime;
    private final DayOfWeek toDay;
    private final LocalTime toTime;
    private final boolean   inverted;
    public DayOfWeekTimeRange(DayOfWeek fromDay, LocalTime fromTime, DayOfWeek toDay, LocalTime toTime) {
        this.fromDay = fromDay;
        this.fromTime = fromTime;
        this.toDay = toDay;
        this.toTime = toTime;
        this.inverted = compare(this.fromDay, this.fromTime, this.toDay, this.toTime) > 0;
    }
    public boolean inRange(LocalDateTime dateTime) {
        return inRange(dateTime.getDayOfWeek(), dateTime.toLocalTime());
    }
    public boolean inRange(DayOfWeek day, LocalTime time) {
        boolean fromOk = compare(day, time, this.fromDay, this.fromTime) >= 0; // Lower-inclusive
        boolean toOk   = compare(day, time, this.toDay  , this.toTime  ) <  0; // Upper-exclusive
        return (this.inverted ? fromOk || toOk : fromOk && toOk);
    }
    private static int compare(DayOfWeek day1, LocalTime time1, DayOfWeek day2, LocalTime time2) {
        int cmp = day1.compareTo(day2);
        if (cmp == 0)
            cmp = time1.compareTo(time2);
        return cmp;
    }
}

Test

// Fri 10:00 PM  to  Sun 10:00 PM
DayOfWeekTimeRange range = new DayOfWeekTimeRange(DayOfWeek.FRIDAY, LocalTime.of(20,0), DayOfWeek.SUNDAY, LocalTime.of(20,0));
System.out.println(range.inRange(LocalDateTime.of(2015, 11, 12, 22, 0))); // Thu Nov. 12 2015 at 10:00 PM
System.out.println(range.inRange(LocalDateTime.of(2015, 11, 14,  8, 0))); // Sat Nov. 14 2015 at  8:00 AM
System.out.println(range.inRange(LocalDateTime.of(2015, 11, 16, 15, 0))); // Mon Nov. 16 2015 at  3:00 PM

// Fri 10:00 PM  to  Mon 10:00 PM
range = new DayOfWeekTimeRange(DayOfWeek.FRIDAY, LocalTime.of(20,0), DayOfWeek.MONDAY, LocalTime.of(20,0));
System.out.println(range.inRange(LocalDateTime.of(2015, 11, 12, 22, 0))); // Thu Nov. 12 2015 at 10:00 PM
System.out.println(range.inRange(LocalDateTime.of(2015, 11, 14,  8, 0))); // Sat Nov. 14 2015 at  8:00 AM
System.out.println(range.inRange(LocalDateTime.of(2015, 11, 16, 15, 0))); // Mon Nov. 16 2015 at  3:00 PM

Output

false
true
false
false
true
true

Alternative

Of course, if you do a lot of processing with DayOfWeek+LocalTime, you should consider implementing your own LocalDayOfWeekTime class, combining the two, in the same way LocalDateTime is simply a combined LocalDate+LocalTime (and it is, check for yourself).

Andreas
  • 154,647
  • 11
  • 152
  • 247
1

The problem is only well defined if we also assume that the interval is always shorter than one week.

You can then work modulo 1 week; eg. if you express everything in milliseconds you would work modulo N = 1000 * 60 * 60 * 24 * 7.

Now calculate a = checkdate - start (mod N) and b = end - start (mod N) and test wether a < b. If this is the case, the date is in the interval.

You need to pick a real date for start and end, in your example a real friday 20:00 and a real saturday 20:00. It does not matter which one because of the modulo arithmetic.

Also keep in mind, that the Java % operator differs from the mathematical mod for negative numbers. So make sure to apply it only to positive numbers.

Henry
  • 42,982
  • 7
  • 68
  • 84
  • Your suggestion is right, now the only thing to solve is finding out the real date for those days of week. Any idea? I can't find any. – Martin Vrábel Nov 13 '15 at 20:59
  • For example, Friday 20:00 would be 4 days and 20 hours after Nov 9th, 2015, 00:00. – Henry Nov 13 '15 at 21:03
0

To start with you need to make your funktion take a datetime parameter, you won't be able to test it if you use "now()" inside it.

Your basic idea seems good, use day of week either as an index to an array or to check for a range, if the range passes the start/end of the week either make it two ranges or invert it.

Samuel Åslund
  • 2,814
  • 2
  • 18
  • 23
0

I wrote a test program that takes two date times (the range) and then tests a date time to see if it falls in that range.

I used full dates so that you could span more than one week if you wanted.

Here are the test results:

Testing for the date range Fri, 13 Nov 2015 20:00 to Sun, 15 Nov 2015 20:00
12 Nov 2015 22:00: false
14 Nov 2015 08:00: true
16 Nov 2015 10:00: false

And here's the code. I input the date times as Strings to make it easier to work with this code,

package com.ggl.testing;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateRangeTesting {

    private static final SimpleDateFormat inputDateFormat = new SimpleDateFormat(
            "d MMM yyyy H:mm");
    private static final SimpleDateFormat outputDateFormat = new SimpleDateFormat(
            "EEE, d MMM yyyy H:mm");

    private Calendar fromDate;
    private Calendar toDate;

    public static void main(String[] args) {
        DateRangeTesting drTesting = new DateRangeTesting("13 Nov 2015 20:00",
                "15 Nov 2015 20:00");
        printResult("12 Nov 2015 22:00", drTesting);
        printResult("14 Nov 2015 08:00", drTesting);
        printResult("16 Nov 2015 10:00", drTesting);
    }

    private static void printResult(String s, DateRangeTesting drTesting) {
        System.out.println(s + ": " + drTesting.testDate(s));
    }

    public DateRangeTesting(String fromDateString, String toDateString) {
        this.fromDate = convertInputDate(fromDateString);
        this.toDate = convertInputDate(toDateString);
        System.out.println(displayRange());
    }

    private String displayRange() {
        StringBuilder builder = new StringBuilder();
        builder.append("Testing for the date range ");
        builder.append(outputDateFormat.format(fromDate.getTime()));
        builder.append(" to ");
        builder.append(outputDateFormat.format(toDate.getTime()));

        return builder.toString();
    }

    public boolean testDate(String dateString) {
        Calendar testDate = convertInputDate(dateString);
        boolean fromTest = fromDate.compareTo(testDate) <= 0;
        boolean toTest = testDate.compareTo(toDate) <= 0;
        return fromTest && toTest;
    }

    private Calendar convertInputDate(String dateString) {
        Calendar calendar = Calendar.getInstance();
        try {
            Date tempDate = inputDateFormat.parse(dateString);
            calendar.setTime(tempDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return calendar;
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • Thanks, that's a good solution except that I don't know exact dates in range, I only know a day and time of day, so I have to somehow find out the date of those treshold days and times. – Martin Vrábel Nov 13 '15 at 21:40
  • @Supermartzin: Yes, that's another method to be created. What would the method signature be? Something like getDate(Calendar.SUNDAY, 0), where the 0 represents the current Sunday, 1 represents the following Sunday, etc. I assume this would be based on the current date. – Gilbert Le Blanc Nov 13 '15 at 21:43
  • I see what you mean. Anyway I prefer the way working with the new **Java 8 Datetime API ** – Martin Vrábel Nov 13 '15 at 21:53