Browse Source

zonedatetime

Ranides Atterwim 1 year ago
parent
commit
ac4212a9f7

+ 10 - 10
assira.commons/src/main/java/net/ranides/assira/time2/DateTimeFormatters.java

@@ -16,16 +16,16 @@ public class DateTimeFormatters {
 
     public static final String ISO_ZONED_PATTERN           = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
 
-    public static final DateTimeFormatter ISO_ZONED        = DateTimeFormatter.ofPattern(ISO_ZONED_PATTERN).withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter COMPRESSED       = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter DATE             = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter DATETIME         = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter DATETIME_PRECISE = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter TIME             = DateTimeFormatter.ofPattern("HH:mm").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter TIME_PRECISE     = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter YEAR             = DateTimeFormatter.ofPattern("yyyy").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter DATENAME         = DateTimeFormatter.ofPattern("dd.MM EE").withZone(DateTimeUtils.ZONE_PL);
-    public static final DateTimeFormatter DAY_NAME         = DateTimeFormatter.ofPattern("EEEE").withZone(DateTimeUtils.ZONE_PL);
+    public static final DateTimeFormatter ISO_ZONED        = DateTimeFormatter.ofPattern(ISO_ZONED_PATTERN);
+    public static final DateTimeFormatter COMPRESSED       = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
+    public static final DateTimeFormatter DATE             = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    public static final DateTimeFormatter DATETIME         = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+    public static final DateTimeFormatter DATETIME_PRECISE = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    public static final DateTimeFormatter TIME             = DateTimeFormatter.ofPattern("HH:mm");
+    public static final DateTimeFormatter TIME_PRECISE     = DateTimeFormatter.ofPattern("HH:mm:ss");
+    public static final DateTimeFormatter YEAR             = DateTimeFormatter.ofPattern("yyyy");
+    public static final DateTimeFormatter DATENAME         = DateTimeFormatter.ofPattern("dd.MM EE");
+    public static final DateTimeFormatter DAY_NAME         = DateTimeFormatter.ofPattern("EEEE");
 
     @ZoneExplicit
     @Contract("!null -> !null; null -> null")

+ 26 - 2
assira.commons/src/main/java/net/ranides/assira/time2/DateTimeLiteral.java

@@ -12,8 +12,12 @@ public class DateTimeLiteral {
 
     public static final ZonedDateTime MAX = datetime(4000, 1, 1, 0, 0, 0);
 
+    private static final int BASE_Y = 1900;
+    private static final int BASE_M = 1;
+    private static final int BASE_D = 1;
+
     public static ZonedTime time(int h, int m, int s) {
-        return ZonedTime.of(datetime(1900, 1, 1, 0, 0));
+        return ZonedTime.of(datetime(BASE_Y, BASE_M, BASE_D, h, m, s));
     }
 
     public static ZonedDate date(int y, int m, int d) {
@@ -30,7 +34,12 @@ public class DateTimeLiteral {
 
     @ZoneExplicit
     public static ZonedTime timeUtc(int h, int m, int s) {
-        return ZonedTime.of(datetimeUtc(1900, 1, 1, 0, 0));
+        return ZonedTime.of(datetimeUtc(BASE_Y, BASE_M, BASE_D, h, m, s));
+    }
+
+    @ZoneExplicit
+    public static ZonedTime timePolish(int h, int m, int s) {
+        return ZonedTime.of(datetimePolish(BASE_Y, BASE_M, BASE_D, h, m, s));
     }
 
     @ZoneExplicit
@@ -38,13 +47,28 @@ public class DateTimeLiteral {
         return ZonedDate.of(datetimeUtc(y, m, d, 0, 0));
     }
 
+    @ZoneExplicit
+    public static ZonedDate datePolish(int y, int m, int d) {
+        return ZonedDate.of(datetimePolish(y, m, d, 0, 0));
+    }
+
     @ZoneExplicit
     public static ZonedDateTime datetimeUtc(int y, int m, int d, int H, int M) {
         return datetimeUtc(y, m, d, H, M, 0);
     }
 
+    @ZoneExplicit
+    public static ZonedDateTime datetimePolish(int y, int m, int d, int H, int M) {
+        return datetimePolish(y, m, d, H, M, 0);
+    }
+
     @ZoneExplicit
     public static ZonedDateTime datetimeUtc(int y, int m, int d, int H, int M, int S) {
         return ZonedDateTime.of(y, m, d, H, M, S, 0, DateTimeUtils.ZONE_UTC);
     }
+
+    @ZoneExplicit
+    public static ZonedDateTime datetimePolish(int y, int m, int d, int H, int M, int S) {
+        return ZonedDateTime.of(y, m, d, H, M, S, 0, DateTimeUtils.ZONE_PL);
+    }
 }

+ 82 - 10
assira.commons/src/main/java/net/ranides/assira/time2/DateTimeParsers.java

@@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.OffsetTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
@@ -20,7 +21,7 @@ public class DateTimeParsers {
 
     public static final DateTimeFormatter ISO_ZONED = DateTimeFormatter.ISO_ZONED_DATE_TIME;
 
-    public static final DateTimeFormatter ISO_OPTIONAL = new DateTimeFormatterBuilder()
+    public static final DateTimeFormatter DATETIME = new DateTimeFormatterBuilder()
         .parseCaseInsensitive()
         .append(DateTimeFormatter.ISO_LOCAL_DATE)
         .appendLiteral('T')
@@ -36,6 +37,41 @@ public class DateTimeParsers {
         .optionalEnd()
         .toFormatter();
 
+    public static final DateTimeFormatter DATE = new DateTimeFormatterBuilder()
+        .parseCaseInsensitive()
+        .append(DateTimeFormatter.ISO_LOCAL_DATE)
+        .optionalStart() // time is optional
+        .appendLiteral('T')
+        .append(DateTimeFormatter.ISO_LOCAL_TIME)
+        .optionalStart()  // zone and offset made optional
+        .appendOffsetId()
+        .optionalStart()
+        .appendLiteral('[')
+        .parseCaseSensitive()
+        .appendZoneRegionId()
+        .appendLiteral(']')
+        .optionalEnd()
+        .optionalEnd()
+        .optionalEnd()
+        .toFormatter();
+
+    public static final DateTimeFormatter TIME = new DateTimeFormatterBuilder()
+        .parseCaseInsensitive()
+        .optionalStart() // date is optional
+        .append(DateTimeFormatter.ISO_LOCAL_DATE)
+        .appendLiteral('T')
+        .optionalEnd()
+        .append(DateTimeFormatter.ISO_LOCAL_TIME)
+        .optionalStart()  // zone and offset made optional
+        .appendOffsetId()
+        .optionalStart()
+        .appendLiteral('[')
+        .parseCaseSensitive()
+        .appendZoneRegionId()
+        .appendLiteral(']')
+        .optionalEnd()
+        .optionalEnd()
+        .toFormatter();
 
     @Contract("!null -> !null; null -> null")
     public static @Nullable ZonedDateTime parseDateTime(@Nullable String input) {
@@ -50,13 +86,13 @@ public class DateTimeParsers {
     @ZoneExplicit
     @Contract("!null -> !null; null -> null")
     public static @Nullable ZonedDateTime parseUtcDateTime(@Nullable String input) {
-        return parseDateTime(DateTimeUtils.ZONE_UTC, ISO_OPTIONAL, input);
+        return parseDateTime(DateTimeUtils.ZONE_UTC, DATETIME, input);
     }
 
     @ZoneExplicit
     @Contract("!null -> !null; null -> null")
     public static @Nullable ZonedDateTime parsePolishDateTime(@Nullable String input) {
-        return parseDateTime(DateTimeUtils.ZONE_PL, ISO_OPTIONAL, input);
+        return parseDateTime(DateTimeUtils.ZONE_PL, DATETIME, input);
     }
 
     @ZoneExplicit
@@ -105,7 +141,24 @@ public class DateTimeParsers {
         if(Objects.isNull(input)) {
             return null;
         }
-        return ZonedDate.of(zone, LocalDate.parse(input, formatter));
+
+        TemporalAccessor output = formatter.parseBest(
+            input,
+            ZonedDateTime::from,
+            LocalDateTime::from,
+            LocalDate::from
+        );
+
+        if(output instanceof ZonedDateTime) {
+            return ZonedDate.of((ZonedDateTime)output).withZone(zone);
+        }
+        if(output instanceof LocalDateTime) {
+            return ZonedDate.of(zone, ((LocalDateTime)output).toLocalDate());
+        }
+        if(output instanceof LocalDate) {
+            return ZonedDate.of(zone, ((LocalDate)output));
+        }
+        throw new IllegalArgumentException();
     }
 
     @ZoneExplicit
@@ -114,7 +167,7 @@ public class DateTimeParsers {
         if(Objects.isNull(input)) {
             return null;
         }
-        return ZonedDate.of(zone, LocalDate.parse(input, DateTimeFormatters.DATE));
+        return parseDate(DATE, zone, input);
     }
 
     @ZoneExplicit
@@ -135,16 +188,35 @@ public class DateTimeParsers {
         if(Objects.isNull(input)) {
             return null;
         }
-        return ZonedTime.of(zone, LocalTime.parse(input, formatter));
+
+        TemporalAccessor output = formatter.parseBest(
+            input,
+            ZonedDateTime::from,
+            LocalDateTime::from,
+            OffsetTime::from,
+            LocalTime::from
+        );
+
+        if(output instanceof ZonedDateTime) {
+            return ZonedTime.of((ZonedDateTime) output).withZone(zone);
+        }
+        if(output instanceof LocalDateTime) {
+            return ZonedTime.of(zone, ((LocalDateTime)output).toLocalTime());
+        }
+        if(output instanceof OffsetTime) {
+            return ZonedTime.of(((OffsetTime) output).getOffset(), ((OffsetTime)output).toLocalTime());
+        }
+        if(output instanceof LocalTime) {
+            return ZonedTime.of(zone, (LocalTime)output);
+        }
+
+        throw new IllegalArgumentException();
     }
 
     @ZoneExplicit
     @Contract("_,!null -> !null; _,null -> null")
     public static @Nullable ZonedTime parseTime(@NotNull ZoneId zone, @Nullable String input) {
-        if(Objects.isNull(input)) {
-            return null;
-        }
-        return ZonedTime.of(zone, LocalTime.parse(input, DateTimeFormatters.TIME));
+        return parseTime(TIME, zone, input);
     }
 
     @ZoneExplicit

+ 48 - 4
assira.commons/src/main/java/net/ranides/assira/time2/ZonedDate.java

@@ -12,6 +12,7 @@ import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoField;
 import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalField;
 import java.util.Objects;
 
 
@@ -19,12 +20,8 @@ import java.util.Objects;
 @JsonSerialize(using = ZonedDateSerializer.class)
 public abstract class ZonedDate implements Comparable<ZonedDate> {
 
-    // @todo withMonth, withDay, withYear, plusMonths, plusYears, minusMonths, minusYears
-
     // @todo configure spring
     //      https://stackoverflow.com/questions/7854030/configuring-objectmapper-in-spring
-    // @todo parse ZonedDate from full ISO time
-    // @todo json serializer: ZonedDate from full ISO time
 
     public static @NotNull ZonedDate of(@NotNull ZonedDateTime input) {
         return new Complete(input);
@@ -95,6 +92,21 @@ public abstract class ZonedDate implements Comparable<ZonedDate> {
 
     public abstract boolean isZoneConvertible(ZoneId zone);
 
+    public abstract ZonedDate with(TemporalField field, long amount);
+
+    public ZonedDate withYear(int value) {
+        return with(ChronoField.YEAR, value);
+    }
+
+    public ZonedDate withMonth(int value) {
+        return with(ChronoField.MONTH_OF_YEAR, value);
+    }
+
+    public ZonedDate withDay(int value) {
+        return with(ChronoField.DAY_OF_MONTH, value);
+    }
+
+
     @ZoneExplicit
     @ZoneHazard
     public abstract ZonedDate withZone(ZoneId zone);
@@ -160,10 +172,26 @@ public abstract class ZonedDate implements Comparable<ZonedDate> {
         return plus(-amount, unit);
     }
 
+    public final ZonedDate plusYears(int years) {
+        return plus(years, ChronoUnit.YEARS);
+    }
+
+    public final ZonedDate plusMonths(int months) {
+        return plus(months, ChronoUnit.MONTHS);
+    }
+
     public final ZonedDate plusDays(int days) {
         return plus(days, ChronoUnit.DAYS);
     }
 
+    public final ZonedDate minusYears(int years) {
+        return minus(years, ChronoUnit.YEARS);
+    }
+
+    public final ZonedDate minusMonths(int months) {
+        return minus(months, ChronoUnit.MONTHS);
+    }
+
     public final ZonedDate minusDays(int days) {
         return minus(days, ChronoUnit.DAYS);
     }
@@ -223,6 +251,14 @@ public abstract class ZonedDate implements Comparable<ZonedDate> {
             return true;
         }
 
+        @Override
+        public ZonedDate with(TemporalField field, long amount) {
+            if(!field.isDateBased()) {
+                throw new IllegalArgumentException();
+            }
+            return new Complete(value.with(field, amount));
+        }
+
         @Override
         public ZonedDate withZone(ZoneId zone) {
             return new Complete(value.withZoneSameInstant(zone));
@@ -285,6 +321,14 @@ public abstract class ZonedDate implements Comparable<ZonedDate> {
             return false;
         }
 
+        @Override
+        public ZonedDate with(TemporalField field, long amount) {
+            if(!field.isDateBased()) {
+                throw new IllegalArgumentException();
+            }
+            return new Incomplete(zone, value.with(field, amount));
+        }
+
         @Override
         public ZonedDate withZone(ZoneId zone) {
             if(!isSameZone(zone)) {

+ 48 - 3
assira.commons/src/main/java/net/ranides/assira/time2/ZonedTime.java

@@ -15,14 +15,13 @@ import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoField;
 import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalField;
+import java.util.Objects;
 
 @JsonDeserialize(using = ZonedTimeDeserializer.class)
 @JsonSerialize(using = ZonedTimeSerializer.class)
 public abstract class ZonedTime implements Comparable<ZonedTime> {
 
-    // @todo withHour, withMinute, withSecond, withNano, plusNanos, minusNanos
-    // @todo parse ZonedTime from full ISO time
-    // @todo json serializer: ZonedTime from full ISO time
     // @todo SQL serializer
     // @todo SQL timestamp test
 
@@ -87,6 +86,14 @@ public abstract class ZonedTime implements Comparable<ZonedTime> {
         return minus(seconds, ChronoUnit.SECONDS);
     }
 
+    public final ZonedTime plusNanos(int nanos) {
+        return plus(nanos, ChronoUnit.NANOS);
+    }
+
+    public final ZonedTime minusNanos(int nanos) {
+        return minus(nanos, ChronoUnit.NANOS);
+    }
+
     public abstract int getHour();
 
     public abstract int getMinute();
@@ -111,6 +118,16 @@ public abstract class ZonedTime implements Comparable<ZonedTime> {
         return toNanoOfDay() < other.withZoneSameInstant(getZone()).toLocalTime().toNanoOfDay();
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(toNanoOfDay());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return (obj instanceof ZonedTime) && isEqual((ZonedTime)obj);
+    }
+
     @ZoneHazard
     public abstract boolean isEqual(ZonedTime other);
 
@@ -148,6 +165,24 @@ public abstract class ZonedTime implements Comparable<ZonedTime> {
         return withZone(DateTimeUtils.ZONE_PL);
     }
 
+    public abstract ZonedTime with(TemporalField field, long amount);
+
+    public ZonedTime withHour(int value) {
+        return with(ChronoField.HOUR_OF_DAY, value);
+    }
+
+    public ZonedTime withMinute(int value) {
+        return with(ChronoField.MINUTE_OF_HOUR, value);
+    }
+
+    public ZonedTime withSecond(int value) {
+        return with(ChronoField.SECOND_OF_MINUTE, value);
+    }
+
+    public ZonedTime withNano(int value) {
+        return with(ChronoField.NANO_OF_SECOND, value);
+    }
+
     @ZoneHazard
     public abstract ZonedDateTime withDate(ZonedDate time);
 
@@ -229,6 +264,11 @@ public abstract class ZonedTime implements Comparable<ZonedTime> {
             return new Complete(value.withZoneSameInstant(zone));
         }
 
+        @Override
+        public ZonedTime with(TemporalField field, long amount) {
+            return new Complete(value.with(field, amount));
+        }
+
         @Override
         public ZonedDateTime withDate(ZonedDate date) {
             return value
@@ -321,6 +361,11 @@ public abstract class ZonedTime implements Comparable<ZonedTime> {
             }
         }
 
+        @Override
+        public ZonedTime with(TemporalField field, long amount) {
+            return new Incomplete(zone, value.with(field, amount));
+        }
+
         @Override
         public ZonedDateTime withDate(ZonedDate date) {
             return date

+ 10 - 8
assira.commons/src/main/java/net/ranides/assira/time2/json/ZonedDateDeserializer.java

@@ -16,19 +16,22 @@ import net.ranides.assira.time2.DateTimeUtils;
 import net.ranides.assira.time2.ZonedDate;
 
 import java.io.IOException;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 
 public class ZonedDateDeserializer extends StdDeserializer<ZonedDate> implements ContextualDeserializer {
 
     private final DateTimeFormatter formatter;
+    private final ZoneId zone;
 
     protected ZonedDateDeserializer() {
-        this(DateTimeFormatters.DATE.withZone(DateTimeUtils.ZONE_PL));
+        this(DateTimeParsers.DATE, DateTimeUtils.ZONE_UTC);
     }
 
-    public ZonedDateDeserializer(DateTimeFormatter formatter) {
+    public ZonedDateDeserializer(DateTimeFormatter formatter, ZoneId zone) {
         super(ZonedDate.class);
         this.formatter = formatter;
+        this.zone = zone;
     }
 
     @Override
@@ -40,24 +43,23 @@ public class ZonedDateDeserializer extends StdDeserializer<ZonedDate> implements
 
         DateTimeFormatter formatter1 = format.hasPattern()
             ? DateTimeFormatter.ofPattern(format.getPattern())
-            : DateTimeFormatters.DATE;
+            : DateTimeParsers.DATE;
 
         DateTimeFormatter formatter2 = formatter1
-            .withLocale(format.hasLocale() ? format.getLocale() : DateTimeFormatters.LOCALE_PL)
-            .withZone(format.hasTimeZone() ? format.getTimeZone().toZoneId() : DateTimeUtils.ZONE_UTC);
+            .withLocale(format.hasLocale() ? format.getLocale() : DateTimeFormatters.LOCALE_PL);
 
-        return new ZonedDateDeserializer(formatter2);
+        return new ZonedDateDeserializer(formatter2, format.hasTimeZone() ? format.getTimeZone().toZoneId() : DateTimeUtils.ZONE_UTC);
     }
 
     @Override
     public ZonedDate deserialize(JsonParser parser,
         DeserializationContext context) throws IOException, JacksonException {
         if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
-            return DateTimeParsers.parseDate(formatter, formatter.getZone(), parser.getText());
+            return DateTimeParsers.parseDate(formatter, zone, parser.getText());
         }
         if (parser.hasTokenId(JsonTokenId.ID_NUMBER_INT)) {
             int seconds = parser.getNumberValueExact().intValue();
-            return ZonedDate.ofEpochSecond(formatter.getZone(), seconds);
+            return ZonedDate.ofEpochSecond(zone, seconds);
         }
         throw JsonMappingException.from(parser, "Invalid date: " + parser.getValueAsString());
     }

+ 10 - 8
assira.commons/src/main/java/net/ranides/assira/time2/json/ZonedTimeDeserializer.java

@@ -16,19 +16,22 @@ import net.ranides.assira.time2.DateTimeUtils;
 import net.ranides.assira.time2.ZonedTime;
 
 import java.io.IOException;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 
 public class ZonedTimeDeserializer extends StdDeserializer<ZonedTime> implements ContextualDeserializer {
 
     private final DateTimeFormatter formatter;
+    private final ZoneId zone;
 
     protected ZonedTimeDeserializer() {
-        this(DateTimeFormatters.TIME_PRECISE.withZone(DateTimeUtils.ZONE_PL));
+        this(DateTimeParsers.TIME, DateTimeUtils.ZONE_PL);
     }
 
-    public ZonedTimeDeserializer(DateTimeFormatter formatter) {
+    public ZonedTimeDeserializer(DateTimeFormatter formatter, ZoneId zone) {
         super(ZonedTime.class);
         this.formatter = formatter;
+        this.zone = zone;
     }
 
     @Override
@@ -41,24 +44,23 @@ public class ZonedTimeDeserializer extends StdDeserializer<ZonedTime> implements
 
         DateTimeFormatter formatter1 = format.hasPattern()
             ? DateTimeFormatter.ofPattern(format.getPattern())
-            : DateTimeFormatters.TIME;
+            : DateTimeParsers.TIME;
 
         DateTimeFormatter formatter2 = formatter1
-            .withLocale(format.hasLocale() ? format.getLocale() : DateTimeFormatters.LOCALE_PL)
-            .withZone(format.hasTimeZone() ? format.getTimeZone().toZoneId() : DateTimeUtils.ZONE_UTC);
+            .withLocale(format.hasLocale() ? format.getLocale() : DateTimeFormatters.LOCALE_PL);
 
-        return new ZonedTimeDeserializer(formatter2);
+        return new ZonedTimeDeserializer(formatter2, format.hasTimeZone() ? format.getTimeZone().toZoneId() : DateTimeUtils.ZONE_UTC);
     }
 
     @Override
     public ZonedTime deserialize(JsonParser parser,
         DeserializationContext context) throws IOException, JacksonException {
         if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
-            return DateTimeParsers.parseTime(formatter, formatter.getZone(), parser.getText());
+            return DateTimeParsers.parseTime(formatter, zone, parser.getText());
         }
         if (parser.hasTokenId(JsonTokenId.ID_NUMBER_INT)) {
             int seconds = parser.getNumberValueExact().intValue();
-            return ZonedTime.ofEpochSecond(formatter.getZone(), seconds);
+            return ZonedTime.ofEpochSecond(zone, seconds);
         }
         throw JsonMappingException.from(parser, "Invalid date: " + parser.getValueAsString());
     }

+ 38 - 38
assira.commons/src/test/java/net/ranides/assira/time2/DateTimeParsersTest.java

@@ -3,6 +3,7 @@ package net.ranides.assira.time2;
 import net.ranides.assira.junit.NewAssert;
 import org.junit.Test;
 
+import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeParseException;
 
@@ -41,42 +42,6 @@ public class DateTimeParsersTest {
         System.out.println(g);
     }
 
-    @SuppressWarnings({"timezone-explicit", "timezone-hazard"})
-    @Test
-    public void testParsePolishDateTimeSupressed() {
-
-        ZonedDate date1 = ZonedDate.now().withZoneUtc();
-
-        ZonedDate date2 = ZonedDate.now().plusDays(25);
-        ZonedDateTime date3 = ZonedDateTime.now().plusDays(40);
-        ZonedDate date4 = ZonedDate.ofEpochSecond(DateTimeUtils.ZONE_PL, 11_800_150L);
-
-        date1.isAfter(date2);
-        date1.isAfter(date3);
-
-        ZonedTime time1 = ZonedTime.now();
-        ZonedTime time2 = ZonedTime.now().plusMinutes(15);
-        ZonedDateTime time3 = ZonedDateTime.now().plusMinutes(30);
-
-        time1.isAfter(time2);
-        time1.isAfter(time3);
-
-        ZonedDateTime a = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00+02:00[Poland]");
-        ZonedDateTime b = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00Z");
-        ZonedDateTime c = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00+06:00[Europe/Warsaw]");
-        ZonedDateTime d = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00+17:00[Europe/Warsaw]");
-        ZonedDateTime e = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00+06:00");
-        ZonedDateTime f = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00+17:00");
-        ZonedDateTime g = DateTimeParsers.parsePolishDateTime("2020-08-24T08:45:00");
-        System.out.println(a);
-        System.out.println(b);
-        System.out.println(c);
-        System.out.println(d);
-        System.out.println(e);
-        System.out.println(f);
-        System.out.println(g);
-    }
-
     @Test
     public void testParseDateTime() {
         ZonedDateTime a = DateTimeParsers.parseDateTime("2020-08-24T08:45:00+02:00[Poland]");
@@ -97,13 +62,48 @@ public class DateTimeParsersTest {
         System.out.println(d);
         System.out.println(e);
         System.out.println(f);
+        System.out.println("f");
     }
 
-
     @Test
     public void testParseDate() {
-        ZonedDate a = DateTimeParsers.parseUtcDate("2024-10-16");
+        ZoneId zone = ZoneId.of("Pacific/Funafuti");
+
+        ZonedDate a = DateTimeParsers.parseDate(zone, "2024-10-16");
+        ZonedDate b = DateTimeParsers.parseDate(zone, "2020-08-16T20:45:00+02:00[Poland]");
+        ZonedDate c = DateTimeParsers.parseDate(zone, "2020-08-16T20:45:00Z");
+        ZonedDate d = DateTimeParsers.parseDate(zone, "2020-08-16T20:45:00+06:00");
+        ZonedDate e = DateTimeParsers.parseDate(zone, "2020-08-16T20:45:00+18:00");
+        ZonedDate f = DateTimeParsers.parseDate(zone, "2020-08-16T20:45:00");
+
+        System.out.println(a);
+        System.out.println(b);
+        System.out.println(c);
+        System.out.println(d);
+        System.out.println(e);
+        System.out.println(f);
+    }
+
+    @Test
+    public void testParseTime() {
+        ZoneId zone = ZoneId.of("Pacific/Funafuti");
+
+        ZonedTime a = DateTimeParsers.parseTime(zone, "10:45");
+        ZonedTime b = DateTimeParsers.parseTime(zone, "2020-08-16T20:45:00+02:00[Poland]");
+        ZonedTime c = DateTimeParsers.parseTime(zone, "2020-08-16T20:45:00Z");
+        ZonedTime d = DateTimeParsers.parseTime(zone, "2020-08-16T20:45:00+06:00");
+        ZonedTime e = DateTimeParsers.parseTime(zone, "2020-08-16T20:45:00+18:00");
+        ZonedTime f = DateTimeParsers.parseTime(zone, "2020-08-16T20:45:00");
+        ZonedTime g = DateTimeParsers.parseTime(zone, "20:45:00+06:00");
+        ZonedTime h = DateTimeParsers.parseTime(zone, "20:45:00+04:00[Europe/Astrakhan]");
 
         System.out.println(a);
+        System.out.println(b);
+        System.out.println(c);
+        System.out.println(d);
+        System.out.println(e);
+        System.out.println(f);
+        System.out.println(g);
+        System.out.println(h);
     }
 }

+ 23 - 1
assira.commons/src/test/java/net/ranides/assira/time2/ZonedTimeTest.java

@@ -4,6 +4,7 @@ import org.junit.Test;
 
 import java.time.LocalDate;
 import java.time.LocalTime;
+import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoUnit;
 
@@ -11,7 +12,6 @@ import static org.junit.Assert.*;
 
 public class ZonedTimeTest {
 
-
     @Test
     public void testWithDate() {
         ZonedTime time = ZonedTime.of(DateTimeUtils.ZONE_PL, LocalTime.of(14, 25, 31));
@@ -45,4 +45,26 @@ public class ZonedTimeTest {
         System.out.println(DateTimeUtils.diff(datetimeC1, datetimeC2));
 
     }
+
+    @Test
+    public void testWithTime() {
+        ZoneId zone = ZoneId.of("Pacific/Funafuti");
+        ZonedDate base1 = DateTimeLiteral.dateUtc(1980, 1, 1);
+        ZonedDate base2 = DateTimeLiteral.dateUtc(1980, 1, 1).withZone(zone);
+        ZonedDate base3 = DateTimeLiteral.dateUtc(2010, 1, 1).withZone(zone);
+
+        ZonedTime time1 = DateTimeLiteral.timeUtc(6, 45, 0);
+        ZonedTime time2 = DateTimeLiteral.timeUtc(6, 45, 0).withZone(zone);
+
+        System.out.println(time1.withDate(base1));
+        System.out.println(time1.withDate(base2));
+        System.out.println(time1.withDate(base3)); // WTF?
+
+        System.out.println("---");
+
+        System.out.println(time2.withDate(base1)); // WTF ze strefą i wartością
+        System.out.println(time2.withDate(base2)); // WTF ze strefą i wartością
+        System.out.println(time2.withDate(base3)); // WTF ze strefą i wartością
+
+    }
 }

+ 61 - 0
assira.commons/src/test/java/net/ranides/assira/time2/json/ZonedDateDeserializerTest.java

@@ -0,0 +1,61 @@
+package net.ranides.assira.time2.json;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Data;
+import net.ranides.assira.time2.DateTimeLiteral;
+import net.ranides.assira.time2.DateTimeParsers;
+import net.ranides.assira.time2.ZonedDate;
+import org.junit.Test;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.TemporalAccessor;
+
+import static org.junit.Assert.*;
+
+public class ZonedDateDeserializerTest {
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Test
+    public void testShortDate() throws JsonProcessingException {
+        MyItem item = mapper.readValue("{ \"date1\" : \"2024-04-10\", \"date2\" : \"2024-04-10\", \"date3\" : \"2024-04-10\", \"date4\" : \"2024-04-10\" }", MyItem.class);
+
+        assertEquals(DateTimeLiteral.dateUtc(2024, 4, 10), item.getDate1());
+        assertEquals(DateTimeLiteral.dateUtc(2024, 4, 10), item.getDate2());
+        assertEquals(DateTimeLiteral.datePolish(2024, 4, 10), item.getDate3());
+        assertEquals(ZonedDate.of(ZoneId.of("Indian/Antananarivo"), LocalDate.of(2024, 4, 10)), item.getDate4());
+
+    }
+
+    @Test
+    public void testLongDate() throws JsonProcessingException {
+        MyItem item = mapper.readValue("{ \"date1\" : \"2024-04-10T01:15:00\", \"date2\" : \"2024-04-10T08:45:00+14:00\", \"date3\" : \"2024-04-10T08:45:00-14:00\", \"date4\" : \"2024-04-10T08:45:00\" }", MyItem.class);
+
+        assertEquals(DateTimeLiteral.dateUtc(2024, 4, 10), item.getDate1());
+        assertEquals(DateTimeLiteral.dateUtc(2024, 4, 9), item.getDate2());
+        assertEquals(DateTimeLiteral.datePolish(2024, 4, 11), item.getDate3());
+        assertEquals(ZonedDate.of(ZoneId.of("Indian/Antananarivo"), LocalDate.of(2024, 4, 10)), item.getDate4());
+
+    }
+
+    @Data
+    public static final class MyItem {
+
+        ZonedDate date1;
+
+        @JsonFormat(timezone = "GMT")
+        ZonedDate date2;
+
+        @JsonFormat(timezone = "Poland")
+        ZonedDate date3;
+
+        @JsonFormat(timezone = "Indian/Antananarivo")
+        ZonedDate date4;
+
+    }
+}

+ 49 - 0
assira.commons/src/test/java/net/ranides/assira/time2/json/ZonedTimeDeserializerTest.java

@@ -0,0 +1,49 @@
+package net.ranides.assira.time2.json;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.Data;
+import net.ranides.assira.time2.DateTimeFormatters;
+import net.ranides.assira.time2.DateTimeLiteral;
+import net.ranides.assira.time2.ZonedDate;
+import net.ranides.assira.time2.ZonedTime;
+import org.junit.Test;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+import static org.junit.Assert.*;
+
+public class ZonedTimeDeserializerTest {
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Test
+    public void testLongTime() throws JsonProcessingException {
+        MyItem item = mapper.readValue("{ \"time1\" : \"2024-04-10T01:15:00\", \"time2\" : \"2024-04-10T19:45:00+14:00\", \"time3\" : \"2024-04-10T08:45:00-10:00\", \"time4\" : \"2024-04-10T08:45:00\" }", MyItem.class);
+
+        assertEquals(DateTimeLiteral.timeUtc(1, 15, 0), item.getTime1());
+        assertEquals(DateTimeLiteral.timeUtc(5, 45, 0), item.getTime2());
+        assertEquals(DateTimeLiteral.timePolish(20, 45, 0), item.getTime3());
+        assertEquals(ZonedTime.of(ZoneId.of("Indian/Antananarivo"), LocalTime.of(8, 45, 0)), item.getTime4());
+    }
+
+    @Data
+    public static final class MyItem {
+
+        ZonedTime time1;
+
+        @JsonFormat(timezone = "GMT")
+        ZonedTime time2;
+
+        @JsonFormat(timezone = "Poland")
+        ZonedTime time3;
+
+        @JsonFormat(timezone = "Indian/Antananarivo")
+        ZonedTime time4;
+
+    }
+}

BIN
doc/zones.ods