ca678 1 год назад
Родитель
Сommit
c40a6158c9

+ 269 - 0
assira.drafts/src/main/java/net/ranides/assira/datetime/DateTimeUtils.java

@@ -0,0 +1,269 @@
+package net.ranides.assira.time;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.function.Function;
+
+import static java.util.Optional.ofNullable;
+
+public class DateTimeUtils {
+
+    private DateTimeUtils() {
+    }
+
+    public static final ZoneId ZONE_POLAND = ZoneId.of("Poland");
+    public static final ZoneId ZONE_0 = ZoneId.of("Etc/GMT0");
+
+    public static final ZonedDateTime MIN = ZonedDateTime.of(1900, 1, 1, 0, 0, 0, 0, ZONE_0);
+    public static final ZonedDateTime MAX = ZonedDateTime.of(4000, 1, 1, 0, 0, 0, 0, ZONE_0);
+
+
+    public static final String DATE_TIME_AT_UTC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+    public static final String DATE_TIME_ZONED_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_AT_UTC_PATTERN);
+    public static final DateTimeFormatter ZONED_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
+    private static final DateTimeFormatter COMPRESS_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
+    public static final DateTimeFormatter ONLY_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    public static final DateTimeFormatter ONLY_TIME = DateTimeFormatter.ofPattern("HH:mm");
+    public static final DateTimeFormatter DATE_HOUR_MINUTES = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+    public static final DateTimeFormatter DATE_HOUR_MINUTES_SECONDS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    public static final DateTimeFormatter YEAR = DateTimeFormatter.ofPattern("yyyy");
+
+    public static final DateTimeFormatter SHORT_DAY = DateTimeFormatter.ofPattern("dd.MM EE");
+
+    private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
+
+    public static String convertDateTimeToCompressFormat(ZonedDateTime dateTime) {
+        return mapNullable(dateTime, v -> COMPRESS_FORMAT.format(v));
+    }
+
+    public static String convertOnlyTimeToString(ZonedDateTime date) {
+        return mapNullable(date, v -> ONLY_TIME.format(v));
+    }
+
+    public static String convertOnlyDateToString(ZonedDateTime date) {
+        return mapNullable(date, v -> ONLY_DATE.format(v));
+    }
+
+    public static String convertOnlyDateToString(ZonedDate date) {
+        return mapNullable(date, v -> ONLY_DATE.format(v.withMidnight()));
+    }
+
+    public static String formatAsDateHoursMinutes(ZonedDateTime dateTime) {
+        return mapNullable(dateTime, v -> DATE_HOUR_MINUTES.format(v));
+    }
+
+    public static String formatAsDateHoursMinutesSeconds(ZonedDateTime dateTime) {
+        return mapNullable(dateTime, v -> DATE_HOUR_MINUTES_SECONDS.format(v));
+    }
+
+    public static String convertShortDayForPolishTime(ZonedDateTime date) {
+        Locale locale = Locale.getDefault();
+        return mapNullable(polishTime(date), v -> DateTimeFormatter.ofPattern("dd.MM EE", locale).format(v));
+    }
+
+    public static String getFullDayNameFromDate(ZonedDateTime date) {
+        Locale locale = Locale.getDefault();
+
+        return mapNullable(polishTime(date), v -> DateTimeFormatter.ofPattern("EEEE", locale).format(v));
+    }
+
+    public static String convertDateToString(ZonedDateTime date) {
+        return mapNullable(date, v -> FORMATTER.format(v));
+    }
+
+    public static ZonedDateTime convertStringToDate(String date) {
+        return mapNullable(date, v -> ZonedDateTime.parse(v, FORMATTER));
+    }
+
+    public static ZonedDateTime truncateToDate(ZonedDateTime date) {
+        return mapNullable(date, v -> v.withMinute(0).withHour(0).withSecond(0).withNano(0));
+    }
+
+    public static ZonedDateTime utcTime(ZonedDateTime dateTime) {
+        return mapNullable(dateTime, v -> v.withZoneSameInstant(ZONE_0));
+    }
+
+    public static ZonedDateTime polishTime(ZonedDateTime dateTime) {
+        return mapNullable(dateTime, v -> v.withZoneSameInstant(ZONE_POLAND));
+    }
+
+    public static ZonedDate polishDate(ZonedDateTime dateTimeInUtc) {
+        return ZonedDate.from(polishTime(dateTimeInUtc));
+    }
+
+    /**
+     * Parses date from the given date time string and returns as ZonedDate in Polish timezone
+     * @param dateTime String in yyyy-MM-dd'T'HH:mm:ss.SSSX format, where X is time zone indicator
+     * @return Parsed date
+     */
+    public static ZonedDate parseToPolishDate(String dateTime) {
+        return ZonedDate.from(parseToPolishTimezone(dateTime));
+    }
+
+    /**
+     * Parses time from the given date time string and returns as ZonedTime in Polish timezone
+     *
+     * @param dateTime String in yyyy-MM-dd'T'HH:mm:ss.SSSX format, where X is time zone indicator
+     * @return Parsed time
+     */
+    public static ZonedTime parseToPolishTime(String dateTime) {
+        return ZonedTime.from(parseToPolishTimezone(dateTime));
+    }
+
+    /**
+     * Parses date and time from the given date time string and returns as ZonedDateTime in Polish timezone
+     * @param dateTime String in yyyy-MM-dd'T'HH:mm:ss.SSSX format, where X is time zone indicator
+     * @return Parsed date and time with Polish timezone set
+     */
+    public static ZonedDateTime parseToPolishTimezone(String dateTime) {
+        return ZonedDateTime.parse(dateTime, ZONED_FORMAT).withZoneSameInstant(ZONE_POLAND);
+    }
+
+    public static ZonedDateTime min(ZonedDateTime firstDate, ZonedDateTime secondDate) {
+        if (firstDate == null && secondDate == null) return null;
+        if (firstDate == null) return secondDate;
+        if (secondDate == null) return firstDate;
+        return firstDate.isBefore(secondDate)
+                ? firstDate
+                : secondDate;
+    }
+
+    public static ZonedDateTime max(ZonedDateTime firstDate, ZonedDateTime secondDate) {
+        if (firstDate == null && secondDate == null) return null;
+        if (firstDate == null) return secondDate;
+        if (secondDate == null) return firstDate;
+        return firstDate.isBefore(secondDate)
+                ? secondDate
+                : firstDate;
+    }
+
+    public static boolean dateBetweenWithMinMax(ZonedDateTime testedDate, ZonedDateTime dateFrom, ZonedDateTime dateTo) {
+        return dateBetweenWithMinMax(testedDate, dateFrom, dateTo, false);
+    }
+
+    public static boolean dateBetweenWithMinMax(ZonedDateTime testedDate, ZonedDateTime dateFrom, ZonedDateTime dateTo, boolean inclusive) {
+        dateFrom = valueOrMin(dateFrom);
+        dateTo = valueOrMax(dateTo);
+
+        if(inclusive) {
+            return !(testedDate.isBefore(dateFrom) || testedDate.equals(dateFrom)) && !(testedDate.isAfter(dateTo) || testedDate.equals(dateTo));
+        }
+
+        return !testedDate.isBefore(valueOrMin(dateFrom)) && !testedDate.isAfter(dateTo);
+    }
+
+    public static ZonedDateTime valueOrMin(ZonedDateTime date) {
+        return ofNullable(date).orElseGet(() -> MIN);
+    }
+
+    public static ZonedDateTime valueOrMax(ZonedDateTime date) {
+        return ofNullable(date).orElseGet(() -> MAX);
+    }
+
+    public static ZonedDateTime addTimeToDate(String dateValue, String timeValue) {
+        if(dateValue != null) {
+            return null;
+        }
+        ZonedDate zonedDate = parseToPolishDate(dateValue);
+        return timeValue != null ? zonedDate.withTime(parseToPolishTime(timeValue)) : zonedDate.withMidnight();
+    }
+
+    public static ZonedDateTime beginningOfDay(ZonedDateTime dateTime) {
+        return dateTime.with(LocalTime.MIDNIGHT);
+    }
+
+    public static ZonedDateTime beginningOfDay(ZonedDate date) {
+        return date.withMax();
+    }
+
+    public static ZonedDateTime endOfDay(ZonedDateTime dateTime) {
+        return dateTime.with(LocalTime.MAX);
+    }
+
+    public static boolean isSameDate(ZonedDateTime dateTime1, ZonedDateTime dateTime2) {
+        return dateTime1.truncatedTo(ChronoUnit.DAYS).isEqual(dateTime2.truncatedTo(ChronoUnit.DAYS));
+    }
+
+    public static boolean isMinTime(ZonedDateTime dateTime) {
+        return LocalTime.MIN.equals(dateTime.toLocalTime());
+    }
+
+    public static boolean isMaxTime(ZonedDateTime dateTime) {
+        return LocalTime.MAX.equals(dateTime.toLocalTime());
+    }
+
+    public static String extractTimeInPolishTimezone(ZonedDateTime dateTime) {
+        if (dateTime != null) {
+            return polishTime(dateTime).toLocalTime().format(TIME_FORMAT);
+        }
+        return "";
+    }
+
+    public static ZonedDateTime lastDayOfMonth(ZonedDateTime date) {
+        return date
+                .withDayOfMonth(1)
+                .plusMonths(1)
+                .with(LocalTime.MIDNIGHT)
+                .minusDays(1);
+    }
+
+    public static ZonedDateTime lastMinuteOfDay(ZonedDateTime date) {
+        return date.withHour(23).withMinute(59).withSecond(59);
+    }
+
+    public static boolean isSaturday(ZonedDateTime day) {
+        return day.getDayOfWeek() == DayOfWeek.SATURDAY;
+    }
+
+    public static boolean isSunday(ZonedDateTime day) {
+        return day.getDayOfWeek() == DayOfWeek.SUNDAY;
+    }
+
+    public static boolean isHoliday(ZonedDateTime day) {
+        return false;
+    }
+
+    private static <T, R> R mapNullable(T value, Function<T, R> mapper) {
+        return Objects.isNull(value) ? null : mapper.apply(value);
+    }
+
+    public static Duration timeDiff(LocalTime from, LocalTime to) {
+        if (from.isAfter(to)) {
+            return Duration.of(24, ChronoUnit.HOURS).minus(Duration.between(to, from));
+        }
+        return Duration.between(from, to);
+    }
+
+    public static Duration timeDiff(ZonedDateTime from, ZonedDateTime to) {
+        if (from.isAfter(to)) {
+            return Duration.of(24, ChronoUnit.HOURS).minus(Duration.between(to, from));
+        }
+        return Duration.between(from, to);
+    }
+
+    public static Duration timeDiff(ZonedTime from, ZonedTime to) {
+        if(!from.getZone().equals(to.getZone())) {
+            throw new IllegalArgumentException();
+        }
+        if (from.isAfter(to)) {
+            return Duration.of(24, ChronoUnit.HOURS).minus(Duration.between(to.getTimestamp(), from.getTimestamp()));
+        }
+        return Duration.between(from.getTimestamp(), to.getTimestamp());
+    }
+
+    public static ZonedDate literal(int y, int m, int d) {
+        return ZonedDate.from(literal(y, m, d, 0, 0, 0));
+    }
+
+    public static ZonedDateTime literal(int y, int m, int d, int H, int M) {
+        return literal(y, m, d, H, M, 0);
+    }
+
+    public static ZonedDateTime literal(int y, int m, int d, int H, int M, int S) {
+        return LocalDateTime.of(y, m, d, H, M, S).atZone(ZONE_POLAND);
+    }
+}

+ 144 - 0
assira.drafts/src/main/java/net/ranides/assira/datetime/ZonedDate.java

@@ -0,0 +1,144 @@
+package net.ranides.assira.time;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import lombok.Getter;
+import org.jetbrains.annotations.NotNull;
+import pl.pse.psi.omsw.common.DateTimeUtils;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+
+
+/**
+ * This class is used in keep track of timezone and detect invalid time conversions.
+ *
+ * Timezone is NOT used to generate proper "ZonedDateTime".
+ *
+ * Timezone is used to detect invalid operations (e.g. we compare timestamps created from different timezones)
+ */
+@JsonDeserialize(using = ZonedDateDeserializer.class)
+@JsonSerialize(using = ZonedDateSerializer.class)
+public class ZonedDate implements Serializable, Comparable<ZonedDate> {
+
+    public static final ZonedDate MIN_UTC = new ZonedDate(DateTimeUtils.ZONE_0, LocalDate.MIN);
+
+    public static final ZonedDate MAX_UTC = new ZonedDate(DateTimeUtils.ZONE_0, LocalDate.MAX);
+
+    @Getter
+    private final ZoneId zone;
+
+    @Getter
+    private final LocalDate timestamp;
+
+    private ZonedDate(ZoneId zone, LocalDate timestamp) {
+        this.zone = zone;
+        this.timestamp = timestamp;
+    }
+
+    public static ZonedDate from(ZonedDateTime source) {
+        return source == null ? null : new ZonedDate(source.getZone(), source.toLocalDate());
+    }
+
+    public static ZonedDate from(ZonedDateTime source, ZoneId target) {
+        return source == null ? null : from(source.withZoneSameInstant(target));
+    }
+
+    public static ZonedDate from(Timestamp source) {
+        return source == null ? null : new ZonedDate(DateTimeUtils.ZONE_0, source.toLocalDateTime().toLocalDate());
+    }
+
+    public static ZonedDate max(ZoneId zone) {
+        return from(ZonedDateTime.of(4000, 1, 1, 0, 0, 0, 0, zone));
+    }
+
+    public static ZonedDate min(ZoneId zone) {
+        return from(ZonedDateTime.of(1900, 1, 1, 0, 0, 0, 0, zone));
+    }
+
+    public static ZonedDate of(int year, Month month, int day) {
+        return from(ZonedDateTime.of(year, month.getValue(), day, 0, 0, 0, 0, ZoneId.systemDefault()));
+    }
+
+    @Override
+    public int compareTo(@NotNull ZonedDate that) {
+        if(!that.zone.equals(zone)) {
+            throw new IllegalArgumentException();
+        }
+        return timestamp.compareTo(that.timestamp);
+    }
+
+    public ZonedDateTime withTime(ZonedTime time) {
+        if(!time.zone.equals(zone)) {
+            throw new IllegalArgumentException();
+        }
+        return ZonedDateTime.of(timestamp, time.timestamp, zone);
+    }
+
+    public ZonedDateTime withMidnight() {
+        return ZonedDateTime.of(timestamp, LocalTime.MIDNIGHT, zone);
+    }
+
+    public ZonedDateTime withMin() {
+        return ZonedDateTime.of(timestamp, LocalTime.MIN, zone);
+    }
+
+    public ZonedDateTime withMax() {
+        return ZonedDateTime.of(timestamp, LocalTime.MAX, zone);
+    }
+
+    @Deprecated
+    public ZonedDateTime atStartOfDay() {
+        return withMidnight();
+    }
+
+    @Deprecated
+    public ZonedDateTime atEndOfDay() {
+        return withMidnight();
+    }
+
+    public boolean isBefore(ZonedDate other) {
+        return withMidnight().isBefore(other.withMidnight());
+    }
+
+    public boolean isBefore(ZonedDateTime other) {
+        return withMidnight().isBefore(other.with(LocalTime.MIDNIGHT));
+    }
+
+    public boolean isAfter(ZonedDate other) {
+        return withMidnight().isAfter(other.withMidnight());
+    }
+
+    public boolean isAfter(ZonedDateTime other) {
+        return withMidnight().isAfter(other.with(LocalTime.MIDNIGHT));
+    }
+
+    public ZonedDate plusDays(int days) {
+        return new ZonedDate(zone, timestamp.plusDays(days));
+    }
+
+    public Object format(DateTimeFormatter dateFormatter) {
+        return timestamp.format(dateFormatter);
+    }
+
+    public ZonedDate minusDays(int v) {
+        return new ZonedDate(zone, timestamp.minusDays(v));
+    }
+
+    public int getYear() {
+        return timestamp.getYear();
+    }
+
+    public boolean isSameDate(ZonedDateTime periodDateTimeStart) {
+        return periodDateTimeStart.withZoneSameInstant(zone).toLocalDate().equals(timestamp);
+    }
+
+    public boolean isEqual(ZonedDate time) {
+        if(!time.zone.equals(zone)) {
+            throw new IllegalArgumentException();
+        }
+        return time.timestamp.isEqual(timestamp);
+    }
+}

+ 41 - 0
assira.drafts/src/main/java/net/ranides/assira/datetime/ZonedDateRange.java

@@ -0,0 +1,41 @@
+package net.ranides.assira.time;
+
+import lombok.Builder;
+
+import java.time.ZonedDateTime;
+import java.time.temporal.TemporalField;
+
+@Builder
+public class ZonedDateRange {
+
+    private final TemporalField precision;
+    private final boolean inclusive;
+    private final ZonedDate begin;
+    private final ZonedDate end;
+
+    private ZonedDateRange(TemporalField precision, boolean inclusive, ZonedDate begin, ZonedDate end) {
+        this.precision = precision;
+        this.inclusive = inclusive;
+        this.begin = begin;
+        this.end = end;
+    }
+
+    public boolean between(ZonedDateTime testedDate) {
+        // eliminate dates "before"
+        if(begin != null && begin.isBefore(testedDate)) {
+            return false;
+        }
+        if(end == null) {
+            return true;
+        }
+
+        return inclusive ? !end.isAfter(testedDate) : end.isBefore(testedDate);
+    }
+
+    public boolean between(ZonedDate testedDate) {
+        return false;
+    }
+
+
+
+}

+ 80 - 0
assira.drafts/src/main/java/net/ranides/assira/datetime/ZonedDateTimeRange.java

@@ -0,0 +1,80 @@
+package net.ranides.assira.time;
+
+import java.io.Serializable;
+import java.time.ZonedDateTime;
+
+public abstract class ZonedDateTimeRange implements Serializable {
+
+    public static ZonedDateTimeRange of(ZonedDateTime begin, ZonedDateTime end) {
+        if(begin == null) {
+            return new BeginRange(end);
+        }
+        if(end == null) {
+            return new BeginRange(end);
+        }
+        return new FullRange(begin, end);
+    }
+
+    public abstract boolean between(ZonedDateTime testedDate);
+
+    public abstract boolean between(ZonedDate testedDate);
+
+    private static class EndRange extends ZonedDateTimeRange {
+        private final ZonedDateTime end;
+
+        private EndRange(ZonedDateTime end) {
+            this.end = end;
+        }
+
+        @Override
+        public boolean between(ZonedDateTime testedDate) {
+            return testedDate.isBefore(end);
+        }
+
+        @Override
+        public boolean between(ZonedDate testedDate) {
+            if(!testedDate.getZone().equals(end.getZone())) {
+                throw new IllegalArgumentException();
+            }
+            return testedDate.withMidnight().isBefore(end);
+        }
+    }
+
+    private static class BeginRange extends ZonedDateTimeRange {
+        private final ZonedDateTime begin;
+
+        private BeginRange(ZonedDateTime begin) {
+            this.begin = begin;
+        }
+
+        @Override
+        public boolean between(ZonedDateTime testedDate) {
+            return !testedDate.isBefore(begin);
+        }
+
+        @Override
+        public boolean between(ZonedDate testedDate) {
+            return !testedDate.withMidnight().isBefore(begin);
+        }
+    }
+
+    private static class FullRange extends ZonedDateTimeRange {
+        private final ZonedDateTime begin;
+        private final ZonedDateTime end;
+
+        public FullRange(ZonedDateTime begin, ZonedDateTime end) {
+            this.begin = begin;
+            this.end = end;
+        }
+
+        @Override
+        public boolean between(ZonedDateTime testedDate) {
+            return false;
+        }
+
+        @Override
+        public boolean between(ZonedDate testedDate) {
+            return false;
+        }
+    }
+}

+ 67 - 0
assira.drafts/src/main/java/net/ranides/assira/datetime/ZonedTime.java

@@ -0,0 +1,67 @@
+package net.ranides.assira.time;
+
+import lombok.Getter;
+
+import java.io.Serializable;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+/**
+ * This class is used in keep track of timezone and detect invalid time conversions.
+ *
+ * Timezone is NOT used to generate proper "ZonedDateTime".
+ *
+ * Timezone is used to detect invalid operations (e.g. we compare timestamps created from different timezones)
+ */
+public class ZonedTime implements Serializable {
+
+    @Getter
+    final ZoneId zone;
+
+    @Getter
+    final LocalTime timestamp;
+
+    private ZonedTime(ZoneId zone, LocalTime timestamp) {
+        this.zone = zone;
+        this.timestamp = timestamp;
+    }
+
+    public static ZonedTime from(ZonedDateTime source) {
+        return new ZonedTime(source.getZone(), source.toLocalTime());
+    }
+
+    public static ZonedTime of(int h, int m, int s) {
+        return new ZonedTime(ZoneId.systemDefault(), LocalTime.of(h,m,s));
+    }
+
+    public ZonedDateTime withDate(ZonedDate date) {
+        return date.withTime(this);
+    }
+
+    @Deprecated
+    public boolean isBefore(LocalTime value) {
+        return timestamp.isBefore(value);
+    }
+
+    @Deprecated
+    public boolean isAfter(LocalTime value) {
+        return timestamp.isAfter(value);
+    }
+
+    public boolean isBefore(ZonedTime value) {
+        if(!zone.equals(value.zone)) {
+            throw new IllegalArgumentException();
+        }
+        return timestamp.isBefore(value.timestamp);
+    }
+
+    @Deprecated
+    public boolean isAfter(ZonedTime value) {
+        if(!zone.equals(value.zone)) {
+            throw new IllegalArgumentException();
+        }
+        return timestamp.isAfter(value.timestamp);
+    }
+
+}