4

How can I create a generic date (i.e. not attached to any actual yyMMdd but similar to:

val START = LocalTime.of(startHour, startMinute)
val END = LocalTime.of(endHour, endMinute)

However, I additionally want to include the dayOfWeek.

My intention is to calculate the overlap of time intervals, i.e. for two given (start, end) timestamps I want to calculate overlap with a specified weekday and time (i.e. like opening hours).

edit

I am not sure if a custom class is good enough. My intention is if I have a list of events like:

id,start,end
1,2018-01-01 08:00, 2018-01-01 08:00 || opening hours 8-10, duration: 1
2,2018-01-01 10:00, 2018-01-01 12:00 || opening hours 8-10, duration: 0
3,2018-01-02 10:00, 2018-01-02 12:00 || opening hours 10-15, duration 2

to validate if time interval of start-end intersects with another time interval (i.e. opening hours), but this other time interval depends on the day of week.

After constructing the object (where I currently have my probably as I can't combine dayOfWeek and Time in a generic way)

I would use isBefore/isAfter a couple of times and then calculate the duration of overlap.

Georg Heiler
  • 16,916
  • 36
  • 162
  • 292
  • 1
    With a class that has 3 fields: `dayOfWeek`, `startTime`, and `endTime`. – Andreas Sep 27 '18 at 16:09
  • Was nothing to down vote. Upvoted! –  Sep 27 '18 at 16:14
  • `id,start,end` doesn't tell which day of the week the event is related to. Where does that information come from? – ernest_k Sep 27 '18 at 16:16
  • @ernest_k `temporal if necessary, though ` this sounds interesting. Basically yes (see the edit) I want to compare overlap duration of business hours (which can be different per day). – Georg Heiler Sep 27 '18 at 16:16
  • 1
    Dear Down-Voter, please leave a criticism along with your vote. I see nothing wrong with this rather interesting Question. – Basil Bourque Sep 27 '18 at 16:20
  • @GeorgHeiler This Question is still vague. If you gave a more thorough definition of your business problem, you might get more helpful answers. – Basil Bourque Sep 27 '18 at 19:19

3 Answers3

2

Just use the DayOfWeek enum built into Java. It offers seven predefined objects, one for every day of the week, such as DayOfWeek.MONDAY.

I don’t follow exactly your business problem, but it sounds like you need to define your own class.

public class Shift {
    DayOfWeek dayOfWeek ;
    LocalTime startTime , stopTime ;
}

Add a method that translates that into real moments.

public ZonedDateTime startAtDate( LocalDate ld , ZoneId z ) {
    LocalDate LocalDate = ld.with( TemporalAdjustors.previousOrSame( this.dayOfWeek ) ) ;
    ZonedDateTime zdt = ZonedDateTime.of( localDate , this.startTime , z ) ;
    return zdt ;
}

You’ll likely want to add the ThreeTen-Extra library to your project. It offers classes such as Interval and LocalDateTime that you might find useful. These classes offer a bunch of comparison methods such as abuts, overlaps, etc.

Looks like that library lacks a LocalTimeRange, so might want to roll your own. Perhaps even donate such a class to that project.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

One simple approach is to use an enum map for those "standard" intervals.

Assuming the method below to calculate the overlap:

static int calculateOverlap(LocalTime expectedStartTime, LocalTime expectedEndTime, 
        LocalTime actualStartTime, LocalTime actualEndTime) {
    //calculate overlap...
}

You can look up the value in this way (using Pair for simplicity, you can use a custom class with two LocalTime fields):

Map<DayOfWeek, Pair<LocalTime, LocalTime>> openingHours = 
          new EnumMap<>(DayOfWeek.class);

openingHours.put(DayOfWeek.MONDAY, 
          Pair.of(LocalTime.of(8, 0), LocalTime.of(16, 0)));
openingHours.put(DayOfWeek.TUESDAY, 
          Pair.of(LocalTime.of(9, 0), LocalTime.of(17, 0)));
//...entries for other weekdays

And then look it up using:

MyEvent mondayEvent = ...

Pair<LocalTime, LocalTime> mondayHours = officialHours.get(DayOfWeek.MONDAY);
int overlap = calculateOverlap(mondayHours.getLeft(), mondayHours.getRight(),
        mondayEvent.getStart(), mondayEvent.getEnd());
ernest_k
  • 44,416
  • 5
  • 53
  • 99
1

My lib Time4J (v5.0) offers following out-of-box-solution for your whole business problem:

DayPartitionRule rule =
    new DayPartitionBuilder()
        .addWeekdayRule(
            Weekday.MONDAY,
            ClockInterval.between(PlainTime.of(8, 0), PlainTime.of(10, 0)))
        .addWeekdayRule(
            Weekday.TUESDAY,
            ClockInterval.between(PlainTime.of(10, 0), PlainTime.of(15, 0)))
        .build();
Map<Integer, TimestampInterval> events = new HashMap<>();
events.put(
    1,
    TimestampInterval.between(
        PlainTimestamp.of(2018, 1, 1, 8, 0),
        PlainTimestamp.of(2018, 1, 1, 9, 0)));
events.put(
    2,
    TimestampInterval.between(
        PlainTimestamp.of(2018, 1, 1, 10, 0),
        PlainTimestamp.of(2018, 1, 1, 12, 0)));
events.put(
    3,
    TimestampInterval.between(
        PlainTimestamp.of(2018, 1, 2, 10, 0),
        PlainTimestamp.of(2018, 1, 2, 12, 0)));
events.forEach(
    (id, interval) -> System.out.println(
        "Event: " + id + " => "
            + Duration.formatter(ClockUnit.class, "#h:mm").format(
                interval
                    .streamPartitioned(rule)
                    .map(i -> i.getDuration(ClockUnit.HOURS, ClockUnit.MINUTES))
                    .collect(Duration.summingUp())
                    .with(Duration.STD_CLOCK_PERIOD)
            )));

Output:

Event: 1 => 1:00

Event: 2 => 0:00

Event: 3 => 2:00

The algorithm uses the range-package of Time4J and still works properly if you have more than one clock interval per day-of-week or if the input interval stretches over more than one day (which requires summing up durations).

By the way, I assume that your first interval line "2018-01-01 08:00, 2018-01-01 08:00" contains a typo so I have adjusted the end time to 9AM (resulting in the expected duration of one hour).

Community
  • 1
  • 1
Meno Hochschild
  • 42,708
  • 7
  • 104
  • 126