aboutsummaryrefslogtreecommitdiffstats
path: root/config-model-api/src/main/java/com/yahoo/config/application/api/TimeWindow.java
diff options
context:
space:
mode:
Diffstat (limited to 'config-model-api/src/main/java/com/yahoo/config/application/api/TimeWindow.java')
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/TimeWindow.java141
1 files changed, 141 insertions, 0 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/TimeWindow.java b/config-model-api/src/main/java/com/yahoo/config/application/api/TimeWindow.java
new file mode 100644
index 00000000000..40b5a5370e7
--- /dev/null
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/TimeWindow.java
@@ -0,0 +1,141 @@
+package com.yahoo.config.application.api;
+
+import java.time.DateTimeException;
+import java.time.DayOfWeek;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * This class represents a window of time for selected hours on selected days.
+ *
+ * @author mpolden
+ */
+public class TimeWindow {
+
+ private final List<DayOfWeek> days;
+ private final List<Integer> hours;
+ private final ZoneId zone;
+
+ private TimeWindow(List<DayOfWeek> days, List<Integer> hours, ZoneId zone) {
+ this.days = Collections.unmodifiableList(new ArrayList<>(new TreeSet<>(days)));
+ this.hours = Collections.unmodifiableList(new ArrayList<>(new TreeSet<>(hours)));
+ this.zone = zone;
+ }
+
+ /** Returns days in this time window */
+ public List<DayOfWeek> days() {
+ return days;
+ }
+
+ /** Returns hours in this time window */
+ public List<Integer> hours() {
+ return hours;
+ }
+
+ /** Returns the time zone of this time window */
+ public ZoneId zone() { return zone; }
+
+ /** Returns whether the given instant is in this time window */
+ public boolean includes(Instant instant) {
+ LocalDateTime dt = LocalDateTime.ofInstant(instant, zone);
+ return days.contains(dt.getDayOfWeek()) && hours.contains(dt.getHour());
+ }
+
+ @Override
+ public String toString() {
+ return "time window for hour(s) " +
+ hours.toString() +
+ " on " + days.stream().map(DayOfWeek::name)
+ .map(String::toLowerCase)
+ .collect(Collectors.toList()).toString() +
+ " in " + zone;
+ }
+
+ /** Parse a time window from the given day, hour and time zone specification */
+ public static TimeWindow from(String daySpec, String hourSpec, String zoneSpec) {
+ List<DayOfWeek> days = parse(daySpec, TimeWindow::parseDays);
+ List<Integer> hours = parse(hourSpec, TimeWindow::parseHours);
+ ZoneId zone = zoneFrom(zoneSpec);
+ return new TimeWindow(days, hours, zone);
+ }
+
+ /** Parse a specification, e.g. "1,4-5", using the given value parser */
+ private static <T> List<T> parse(String spec, BiFunction<String, String, List<T>> valueParser) {
+ List<T> values = new ArrayList<>();
+ String[] parts = spec.split(",");
+ for (String part : parts) {
+ if (part.contains("-")) {
+ String[] startAndEnd = part.split("-");
+ if (startAndEnd.length != 2) {
+ throw new IllegalArgumentException("Invalid range '" + part + "'");
+ }
+ values.addAll(valueParser.apply(startAndEnd[0], startAndEnd[1]));
+ } else {
+ values.addAll(valueParser.apply(part, part));
+ }
+ }
+ return Collections.unmodifiableList(values);
+ }
+
+ /** Returns a list of all hours occurring between startInclusive and endInclusive */
+ private static List<Integer> parseHours(String startInclusive, String endInclusive) {
+ int start = hourFrom(startInclusive);
+ int end = hourFrom(endInclusive);
+ if (end < start) {
+ throw new IllegalArgumentException(String.format("Invalid hour range '%s-%s'", startInclusive,
+ endInclusive));
+ }
+ return IntStream.rangeClosed(start, end).boxed()
+ .collect(Collectors.toList());
+ }
+
+ /** Returns a list of all days occurring between startInclusive and endInclusive */
+ private static List<DayOfWeek> parseDays(String startInclusive, String endInclusive) {
+ DayOfWeek start = dayFrom(startInclusive);
+ DayOfWeek end = dayFrom(endInclusive);
+ if (end.getValue() < start.getValue()) {
+ throw new IllegalArgumentException(String.format("Invalid day range '%s-%s'", startInclusive,
+ endInclusive));
+ }
+ return IntStream.rangeClosed(start.getValue(), end.getValue()).boxed()
+ .map(DayOfWeek::of)
+ .collect(Collectors.toList());
+ }
+
+ /** Parse day of week from string */
+ private static DayOfWeek dayFrom(String day) {
+ return Arrays.stream(DayOfWeek.values())
+ .filter(dayOfWeek -> day.length() >= 3 && dayOfWeek.name().toLowerCase().startsWith(day))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Invalid day '" + day + "'"));
+ }
+
+ /** Parse hour from string */
+ private static int hourFrom(String hour) {
+ try {
+ return ChronoField.HOUR_OF_DAY.checkValidIntValue(Integer.parseInt(hour));
+ } catch (DateTimeException|NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid hour '" + hour + "'", e);
+ }
+ }
+
+ /** Parse time zone from string */
+ private static ZoneId zoneFrom(String zone) {
+ try {
+ return ZoneId.of(zone);
+ } catch (DateTimeException e) {
+ throw new IllegalArgumentException("Invalid time zone '" + zone + "'", e);
+ }
+ }
+
+}