Skip to content

Commit

Permalink
fix: reformat OpeningHoursEvaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
hbruch committed Jul 29, 2024
1 parent c670606 commit 3650c93
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 59 deletions.
132 changes: 78 additions & 54 deletions src/main/java/io/leonard/OpeningHoursEvaluator.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
package io.leonard;

import ch.poole.openinghoursparser.*;
import static ch.poole.openinghoursparser.RuleModifier.Modifier.*;

import ch.poole.openinghoursparser.*;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Stream;

import static ch.poole.openinghoursparser.RuleModifier.Modifier.*;

public class OpeningHoursEvaluator {

private static final Set<RuleModifier.Modifier> CLOSED_MODIFIERS = Set.of(CLOSED, OFF);
private static final Set<RuleModifier.Modifier> OPEN_MODIFIERS = Set.of(OPEN, UNKNOWN);
private static final Map<WeekDay, DayOfWeek> weekDayToDayOfWeek =
Map.of(
WeekDay.MO, DayOfWeek.MONDAY,
WeekDay.TU, DayOfWeek.TUESDAY,
WeekDay.WE, DayOfWeek.WEDNESDAY,
WeekDay.TH, DayOfWeek.THURSDAY,
WeekDay.FR, DayOfWeek.FRIDAY,
WeekDay.SA, DayOfWeek.SATURDAY,
WeekDay.SU, DayOfWeek.SUNDAY);
private static final Map<WeekDay, DayOfWeek> weekDayToDayOfWeek = Map.of(
WeekDay.MO,
DayOfWeek.MONDAY,
WeekDay.TU,
DayOfWeek.TUESDAY,
WeekDay.WE,
DayOfWeek.WEDNESDAY,
WeekDay.TH,
DayOfWeek.THURSDAY,
WeekDay.FR,
DayOfWeek.FRIDAY,
WeekDay.SA,
DayOfWeek.SATURDAY,
WeekDay.SU,
DayOfWeek.SUNDAY
);

// when calculating the next time the hours are open, how many days should you go into the future
// this protects against stack overflows when the place is never going to open again
Expand All @@ -31,8 +37,10 @@ public class OpeningHoursEvaluator {
public static boolean isOpenAt(LocalDateTime time, List<Rule> rules) {
var closed = getClosedRules(rules);
var open = getOpenRules(rules);
return closed.noneMatch(rule -> timeMatchesRule(time, rule))
&& open.anyMatch(rule -> rule.isTwentyfourseven() || timeMatchesRule(time, rule));
return (
closed.noneMatch(rule -> timeMatchesRule(time, rule)) &&
open.anyMatch(rule -> rule.isTwentyfourseven() || timeMatchesRule(time, rule))
);
}

/**
Expand All @@ -54,7 +62,10 @@ public static Optional<LocalDateTime> wasLastOpen(LocalDateTime time, List<Rule>
}

public static Optional<LocalDateTime> wasLastOpen(
LocalDateTime time, List<Rule> rules, int searchDays) {
LocalDateTime time,
List<Rule> rules,
int searchDays
) {
return isOpenIterative(time, rules, false, searchDays);
}

Expand All @@ -63,7 +74,10 @@ public static Optional<LocalDateTime> isOpenNext(LocalDateTime time, List<Rule>
}

public static Optional<LocalDateTime> isOpenNext(
LocalDateTime time, List<Rule> rules, int searchDays) {
LocalDateTime time,
List<Rule> rules,
int searchDays
) {
return isOpenIterative(time, rules, true, searchDays);
}

Expand All @@ -80,33 +94,29 @@ private static Optional<LocalDateTime> isOpenIterative(
final LocalDateTime initialTime,
final List<Rule> rules,
boolean forward,
final int searchDays) {

final int searchDays
) {
var nextTime = initialTime;
for (var iterations = 0; iterations <= searchDays; ++iterations) {
var open = getOpenRules(rules);
var closed = getClosedRules(rules);

var time = nextTime;
if (isOpenAt(time, rules)) return Optional.of(time);
else {

if (isOpenAt(time, rules)) return Optional.of(time); else {
var openRangesOnThatDay = getTimeRangesOnThatDay(time, open);
var closedRangesThatDay = getTimeRangesOnThatDay(time, closed);

var endOfExclusion =
closedRangesThatDay
.filter(r -> r.surrounds(time.toLocalTime()))
.findFirst()
.map(r -> time.toLocalDate().atTime(forward ? r.end : r.start));
var endOfExclusion = closedRangesThatDay
.filter(r -> r.surrounds(time.toLocalTime()))
.findFirst()
.map(r -> time.toLocalDate().atTime(forward ? r.end : r.start));

var startOfNextOpening =
forward
? openRangesOnThatDay
var startOfNextOpening = forward
? openRangesOnThatDay
.filter(range -> range.start.isAfter(time.toLocalTime()))
.min(TimeRange.startComparator)
.map(timeRange -> time.toLocalDate().atTime(timeRange.start))
: openRangesOnThatDay
: openRangesOnThatDay
.filter(range -> range.end.isBefore(time.toLocalTime()))
.max(TimeRange.endComparator)
.map(timeRange -> time.toLocalDate().atTime(timeRange.end));
Expand All @@ -129,37 +139,44 @@ private static Optional<LocalDateTime> isOpenIterative(
}

private static Stream<TimeRange> getTimeRangesOnThatDay(
LocalDateTime time, Stream<Rule> ruleStream) {
LocalDateTime time,
Stream<Rule> ruleStream
) {
return ruleStream
.filter(rule -> timeMatchesDayRanges(time, rule.getDays()))
.filter(r -> !Objects.isNull(r.getTimes()))
.flatMap(r -> r.getTimes().stream().map(TimeRange::new));
}

private static Stream<Rule> getOpenRules(List<Rule> rules) {
return rules.stream()
.filter(
r -> {
var modifier = r.getModifier();
return modifier == null || OPEN_MODIFIERS.contains(modifier.getModifier());
});
return rules
.stream()
.filter(r -> {
var modifier = r.getModifier();
return modifier == null || OPEN_MODIFIERS.contains(modifier.getModifier());
});
}

private static Stream<Rule> getClosedRules(List<Rule> rules) {
return rules.stream()
.filter(
r -> {
var modifier = r.getModifier();
return modifier != null && CLOSED_MODIFIERS.contains(modifier.getModifier());
});
return rules
.stream()
.filter(r -> {
var modifier = r.getModifier();
return modifier != null && CLOSED_MODIFIERS.contains(modifier.getModifier());
});
}

private static boolean timeMatchesRule(LocalDateTime time, Rule rule) {
return (timeMatchesDayRanges(time, rule.getDays())
|| rule.getDays() == null
&& dateMatchesDateRanges(time, rule.getDates()))
&& nullToEntireDay(rule.getTimes()).stream()
.anyMatch(timeSpan -> timeMatchesHours(time, timeSpan));
return (
(
timeMatchesDayRanges(time, rule.getDays()) ||
rule.getDays() == null &&
dateMatchesDateRanges(time, rule.getDates())
) &&
nullToEntireDay(rule.getTimes())
.stream()
.anyMatch(timeSpan -> timeMatchesHours(time, timeSpan))
);
}

private static boolean timeMatchesDayRanges(LocalDateTime time, List<WeekDayRange> ranges) {
Expand All @@ -177,18 +194,26 @@ private static boolean timeMatchesDay(LocalDateTime time, WeekDayRange range) {
}

private static boolean dateMatchesDateRanges(LocalDateTime time, List<DateRange> ranges) {
return nullToEmptyList(ranges).stream().anyMatch(dateRange -> dateMatchesDateRange(time, dateRange));
return nullToEmptyList(ranges)
.stream()
.anyMatch(dateRange -> dateMatchesDateRange(time, dateRange));
}

private static boolean dateMatchesDateRange(LocalDateTime time, DateRange range) {
// if the end date is null it means that it's just a single date like in "2020 Aug 11"
DateWithOffset startDate = range.getStartDate();
boolean afterStartDate = time.getYear() >= startDate.getYear() && time.getMonth().ordinal() >= startDate.getMonth().ordinal() && time.getDayOfMonth() >= startDate.getDay();
boolean afterStartDate =
time.getYear() >= startDate.getYear() &&
time.getMonth().ordinal() >= startDate.getMonth().ordinal() &&
time.getDayOfMonth() >= startDate.getDay();
if (range.getEndDate() == null) {
return afterStartDate;
}
DateWithOffset endDate = range.getEndDate();
boolean beforeEndDate = time.getYear() <= endDate.getYear() && time.getMonth().ordinal() <= endDate.getMonth().ordinal() && time.getDayOfMonth() <= endDate.getDay();
boolean beforeEndDate =
time.getYear() <= endDate.getYear() &&
time.getMonth().ordinal() <= endDate.getMonth().ordinal() &&
time.getDayOfMonth() <= endDate.getDay();
return afterStartDate && beforeEndDate;
}

Expand All @@ -202,8 +227,7 @@ private static int minutesAfterMidnight(LocalTime time) {
}

private static <T> List<T> nullToEmptyList(List<T> list) {
if (list == null) return Collections.emptyList();
else return list;
if (list == null) return Collections.emptyList(); else return list;
}

private static List<TimeSpan> nullToEntireDay(List<TimeSpan> span) {
Expand All @@ -214,4 +238,4 @@ private static List<TimeSpan> nullToEntireDay(List<TimeSpan> span) {
return List.of(allDay);
} else return span;
}
}
}
12 changes: 7 additions & 5 deletions src/main/java/io/leonard/TimeRange.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ public boolean surrounds(LocalTime time) {
return time.isAfter(start) && time.isBefore(end);
}

public static Comparator<TimeRange> startComparator =
Comparator.comparing(timeRange -> timeRange.start);
public static Comparator<TimeRange> startComparator = Comparator.comparing(timeRange ->
timeRange.start
);

public static Comparator<TimeRange> endComparator =
Comparator.comparing(timeRange -> timeRange.end);
}
public static Comparator<TimeRange> endComparator = Comparator.comparing(timeRange ->
timeRange.end
);
}

0 comments on commit 3650c93

Please sign in to comment.