|
|
@@ -7,7 +7,9 @@ import net.ranides.assira.generic.ValueUtils;
|
|
|
import net.ranides.assira.time.DateTimeUtils;
|
|
|
import net.ranides.assira.time.ZonedDate;
|
|
|
import net.ranides.assira.time.ZonedTime;
|
|
|
+import org.jetbrains.annotations.NotNull;
|
|
|
|
|
|
+import java.text.ParsePosition;
|
|
|
import java.time.DateTimeException;
|
|
|
import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
@@ -18,14 +20,73 @@ import java.time.ZonedDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.time.format.DateTimeFormatterBuilder;
|
|
|
import java.time.format.DateTimeParseException;
|
|
|
+import java.time.format.SignStyle;
|
|
|
+import java.time.temporal.ChronoField;
|
|
|
import java.time.temporal.TemporalAccessor;
|
|
|
import java.time.temporal.TemporalQueries;
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.Locale;
|
|
|
+import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
|
|
|
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
|
|
|
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
|
|
|
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
|
|
|
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
|
|
|
+import static java.time.temporal.ChronoField.YEAR;
|
|
|
+
|
|
|
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
|
|
public class DateTimeReader {
|
|
|
|
|
|
+ private static final DateTimeFormatter ECMA_PARSER;
|
|
|
+ static {
|
|
|
+ Map<Long, String> dow = new HashMap<>();
|
|
|
+ dow.put(1L, "Mon");
|
|
|
+ dow.put(2L, "Tue");
|
|
|
+ dow.put(3L, "Wed");
|
|
|
+ dow.put(4L, "Thu");
|
|
|
+ dow.put(5L, "Fri");
|
|
|
+ dow.put(6L, "Sat");
|
|
|
+ dow.put(7L, "Sun");
|
|
|
+ Map<Long, String> moy = new HashMap<>();
|
|
|
+ moy.put(1L, "Jan");
|
|
|
+ moy.put(2L, "Feb");
|
|
|
+ moy.put(3L, "Mar");
|
|
|
+ moy.put(4L, "Apr");
|
|
|
+ moy.put(5L, "May");
|
|
|
+ moy.put(6L, "Jun");
|
|
|
+ moy.put(7L, "Jul");
|
|
|
+ moy.put(8L, "Aug");
|
|
|
+ moy.put(9L, "Sep");
|
|
|
+ moy.put(10L, "Oct");
|
|
|
+ moy.put(11L, "Nov");
|
|
|
+ moy.put(12L, "Dec");
|
|
|
+
|
|
|
+ ECMA_PARSER = new DateTimeFormatterBuilder()
|
|
|
+ .parseCaseInsensitive()
|
|
|
+ .parseLenient()
|
|
|
+ .appendText(ChronoField.DAY_OF_WEEK, dow)
|
|
|
+ .appendLiteral(' ')
|
|
|
+ .appendText(MONTH_OF_YEAR, moy)
|
|
|
+ .appendLiteral(' ')
|
|
|
+ .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
|
|
|
+ .appendLiteral(' ')
|
|
|
+ .appendValue(YEAR, 4)
|
|
|
+ .appendLiteral(' ')
|
|
|
+ .appendValue(HOUR_OF_DAY, 2)
|
|
|
+ .appendLiteral(':')
|
|
|
+ .appendValue(MINUTE_OF_HOUR, 2)
|
|
|
+ .optionalStart()
|
|
|
+ .appendLiteral(':')
|
|
|
+ .appendValue(SECOND_OF_MINUTE, 2)
|
|
|
+ .optionalEnd()
|
|
|
+ .appendLiteral(" GMT")
|
|
|
+ .appendOffset("+HHMM", "GMT") // should handle UT/Z/EST/EDT/CST/CDT/MST/MDT/PST/MDT
|
|
|
+ .appendLiteral(" (")
|
|
|
+ .toFormatter();
|
|
|
+ }
|
|
|
+
|
|
|
private static final DateTimeFormatter DATETIME_PARSER = new DateTimeFormatterBuilder()
|
|
|
.parseCaseInsensitive()
|
|
|
.append(DateTimeFormatter.ISO_LOCAL_DATE)
|
|
|
@@ -78,9 +139,11 @@ public class DateTimeReader {
|
|
|
.optionalEnd()
|
|
|
.toFormatter();
|
|
|
|
|
|
+ public static final DateTimeReader ISO = new DateTimeReader(DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
|
|
+
|
|
|
public static final DateTimeReader RFC = new DateTimeReader(DateTimeFormatter.RFC_1123_DATE_TIME);
|
|
|
|
|
|
- public static final DateTimeReader ISO = new DateTimeReader(DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
|
|
+ public static final DateTimeReader ECMA262 = new DateTimeReader(ECMA_PARSER).withParseAll(false);
|
|
|
|
|
|
public static final DateTimeReader DATETIME = new DateTimeReader(DATETIME_PARSER);
|
|
|
|
|
|
@@ -98,16 +161,54 @@ public class DateTimeReader {
|
|
|
@Getter
|
|
|
private final ZoneId targetZone;
|
|
|
|
|
|
- public DateTimeReader(String pattern) {
|
|
|
+ private final boolean parseAll;
|
|
|
+
|
|
|
+ public static DateTimeReader of(String... list) {
|
|
|
+ return of(0, list);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DateTimeReader of(DateTimeFormatter... list) {
|
|
|
+ return of(0, list);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DateTimeReader of(DateTimeReader... list) {
|
|
|
+ return of(0, list);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static DateTimeReader of(int offset, DateTimeReader... list) {
|
|
|
+ if(offset == list.length - 1) {
|
|
|
+ return list[offset];
|
|
|
+ } else {
|
|
|
+ return list[offset].withFallback(of(offset + 1, list));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static DateTimeReader of(int offset, DateTimeFormatter... list) {
|
|
|
+ if(offset == list.length - 1) {
|
|
|
+ return new DateTimeReader(list[offset]);
|
|
|
+ } else {
|
|
|
+ return new DateTimeReader(list[offset]).withFallback(of(offset + 1, list));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static DateTimeReader of(int offset, String... list) {
|
|
|
+ if(offset == list.length - 1) {
|
|
|
+ return new DateTimeReader(list[offset]);
|
|
|
+ } else {
|
|
|
+ return new DateTimeReader(list[offset]).withFallback(of(offset + 1, list));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private DateTimeReader(String pattern) {
|
|
|
this(DateTimeFormatter.ofPattern(pattern));
|
|
|
}
|
|
|
|
|
|
private DateTimeReader(DateTimeFormatter format) {
|
|
|
- this(null, format, null, null);
|
|
|
+ this(null, format, null, null, true);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withFallback(DateTimeReader fallback) {
|
|
|
- return new DateTimeReader(fallback, parser, sourceZone, targetZone);
|
|
|
+ return new DateTimeReader(fallback, parser, sourceZone, targetZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withFormat(String newFormat) {
|
|
|
@@ -115,11 +216,11 @@ public class DateTimeReader {
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withFormat(DateTimeFormatter newFormat) {
|
|
|
- return new DateTimeReader(fallback, newFormat, sourceZone, targetZone);
|
|
|
+ return new DateTimeReader(fallback, newFormat, sourceZone, targetZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withLocale(Locale newLocale) {
|
|
|
- return new DateTimeReader(fallback, parser.withLocale(newLocale), sourceZone, targetZone);
|
|
|
+ return new DateTimeReader(fallback, parser.withLocale(newLocale), sourceZone, targetZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withLocale(String newLocale) {
|
|
|
@@ -131,7 +232,7 @@ public class DateTimeReader {
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withZone(ZoneId newZone) {
|
|
|
- return new DateTimeReader(fallback, parser, newZone, newZone);
|
|
|
+ return new DateTimeReader(fallback, parser, newZone, newZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withZone(String newZone) {
|
|
|
@@ -139,11 +240,11 @@ public class DateTimeReader {
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withoutZone() {
|
|
|
- return new DateTimeReader(fallback, parser, null, null);
|
|
|
+ return new DateTimeReader(fallback, parser, null, null, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withSourceZone(ZoneId newZone) {
|
|
|
- return new DateTimeReader(fallback, parser, newZone, targetZone);
|
|
|
+ return new DateTimeReader(fallback, parser, newZone, targetZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withSourceZone(String newZone) {
|
|
|
@@ -151,11 +252,11 @@ public class DateTimeReader {
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withoutSourceZone() {
|
|
|
- return new DateTimeReader(fallback, parser, null, targetZone);
|
|
|
+ return new DateTimeReader(fallback, parser, null, targetZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withTargetZone(ZoneId newZone) {
|
|
|
- return new DateTimeReader(fallback, parser, sourceZone, newZone);
|
|
|
+ return new DateTimeReader(fallback, parser, sourceZone, newZone, parseAll);
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withTargetZone(String newZone) {
|
|
|
@@ -163,7 +264,11 @@ public class DateTimeReader {
|
|
|
}
|
|
|
|
|
|
public DateTimeReader withoutTargetZone() {
|
|
|
- return new DateTimeReader(fallback, parser, sourceZone, null);
|
|
|
+ return new DateTimeReader(fallback, parser, sourceZone, null, parseAll);
|
|
|
+ }
|
|
|
+
|
|
|
+ DateTimeReader withParseAll(boolean newParseAll) {
|
|
|
+ return new DateTimeReader(fallback, parser, sourceZone, targetZone, newParseAll);
|
|
|
}
|
|
|
|
|
|
public ZonedDateTime parse(String input) {
|
|
|
@@ -171,7 +276,7 @@ public class DateTimeReader {
|
|
|
return null;
|
|
|
}
|
|
|
try {
|
|
|
- return newDateTime(parser.parse(input, v -> v));
|
|
|
+ return newDateTime(parse0(input));
|
|
|
} catch (DateTimeParseException e) {
|
|
|
if(fallback != null) {
|
|
|
return fallback.parse(input);
|
|
|
@@ -185,7 +290,7 @@ public class DateTimeReader {
|
|
|
return null;
|
|
|
}
|
|
|
try {
|
|
|
- return newDate(parser.parse(input, v -> v));
|
|
|
+ return newDate(parse0(input));
|
|
|
} catch (DateTimeParseException e) {
|
|
|
if(fallback != null) {
|
|
|
return fallback.parseDate(input);
|
|
|
@@ -199,7 +304,7 @@ public class DateTimeReader {
|
|
|
return null;
|
|
|
}
|
|
|
try {
|
|
|
- return newTime(parser.parse(input, v -> v));
|
|
|
+ return newTime(parse0(input));
|
|
|
} catch (DateTimeParseException e) {
|
|
|
if(fallback != null) {
|
|
|
return fallback.parseTime(input);
|
|
|
@@ -208,6 +313,12 @@ public class DateTimeReader {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ @NotNull
|
|
|
+ private TemporalAccessor parse0(String input) {
|
|
|
+ return parseAll ? parser.parse(input) : parser.parse(input, new ParsePosition(0));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public ZonedDateTime newDateTime(TemporalAccessor temporal) {
|
|
|
ZoneOffset offset = temporal.query(TemporalQueries.offset());
|
|
|
ZoneId zone = temporal.query(TemporalQueries.zoneId());
|
|
|
@@ -232,7 +343,7 @@ public class DateTimeReader {
|
|
|
return adjustZone(ZonedDateTime.of(datetime, ValueUtils.or(zone, offset)));
|
|
|
}
|
|
|
|
|
|
- public ZonedDate newDate(TemporalAccessor temporal) {
|
|
|
+ private ZonedDate newDate(TemporalAccessor temporal) {
|
|
|
LocalTime time = temporal.query(TemporalQueries.localTime());
|
|
|
if(time != null) {
|
|
|
return ZonedDate.of(newDateTime(temporal));
|
|
|
@@ -268,7 +379,7 @@ public class DateTimeReader {
|
|
|
return ZonedDate.of(zone, date);
|
|
|
}
|
|
|
|
|
|
- public ZonedTime newTime(TemporalAccessor temporal) {
|
|
|
+ private ZonedTime newTime(TemporalAccessor temporal) {
|
|
|
LocalDate date = temporal.query(TemporalQueries.localDate());
|
|
|
if(date != null) {
|
|
|
return ZonedTime.of(newDateTime(temporal));
|