From f2dffed3ba0ff3084cc12780318cd5188a62ff80 Mon Sep 17 00:00:00 2001 From: michaeloffner Date: Wed, 23 Oct 2024 00:08:18 +0200 Subject: [PATCH] improve parsing with DateTimeFormatter --- .../java/lucee/commons/date/DateTimeUtil.java | 8 +- .../lucee/commons/i18n/DateFormatPool.java | 6 +- .../java/lucee/commons/i18n/FormatUtil.java | 244 ++++++++++++------ .../lucee/commons/i18n/FormatterWrapper.java | 20 +- .../io/log/log4j2/layout/DataDogLayout.java | 2 +- .../java/lucee/commons/lang/SystemOut.java | 2 +- .../runtime/converter/JSONDateFormat.java | 2 +- .../java/lucee/runtime/dump/DumpUtil.java | 2 +- .../java/lucee/runtime/format/DateFormat.java | 2 +- .../displayFormatting/DateTimeFormat.java | 8 +- .../international/LSParseDateTime.java | 29 ++- .../lucee/runtime/net/smtp/SMTPClient.java | 2 +- .../lucee/runtime/op/date/DateCaster.java | 20 +- .../java/lucee/runtime/type/dt/DateImpl.java | 2 +- .../java/lucee/runtime/type/dt/TimeImpl.java | 2 +- .../apache/taglibs/datetime/FormatTag.java | 8 +- loader/build.xml | 2 +- loader/pom.xml | 2 +- 18 files changed, 246 insertions(+), 117 deletions(-) diff --git a/core/src/main/java/lucee/commons/date/DateTimeUtil.java b/core/src/main/java/lucee/commons/date/DateTimeUtil.java index 11e5d4edaf..3e8f9f7385 100644 --- a/core/src/main/java/lucee/commons/date/DateTimeUtil.java +++ b/core/src/main/java/lucee/commons/date/DateTimeUtil.java @@ -18,12 +18,12 @@ **/ package lucee.commons.date; -import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import lucee.commons.i18n.FormatUtil; +import lucee.commons.i18n.FormatterWrapper; import lucee.commons.lang.StringUtil; import lucee.runtime.PageContext; import lucee.runtime.engine.ThreadLocalPageContext; @@ -34,7 +34,7 @@ public abstract class DateTimeUtil { - private final static DateTimeFormatter HTTP_TIME_STRING_FORMAT = FormatUtil.getDateTimeFormatter(Locale.ENGLISH, "EE, dd-MMM-yyyy HH:mm:ss zz"); + private final static FormatterWrapper HTTP_TIME_STRING_FORMAT = FormatUtil.getDateTimeFormatter(Locale.ENGLISH, "EE, dd-MMM-yyyy HH:mm:ss zz"); private static final double DAY_MILLIS = 86400000D; private static final long CF_UNIX_OFFSET = 2209161600000L; @@ -266,9 +266,9 @@ public static String toHTTPTimeString(long time, boolean oldFormat) { */ public static String toHTTPTimeString(Date date, boolean oldFormat) { if (oldFormat) { - return StringUtil.replace(FormatUtil.format(HTTP_TIME_STRING_FORMAT, date, TimeZoneConstants.GMT), "+00:00", "", true); + return StringUtil.replace(FormatUtil.format(HTTP_TIME_STRING_FORMAT.formatter, date, TimeZoneConstants.GMT), "+00:00", "", true); } - return StringUtil.replace(FormatUtil.format(HTTP_TIME_STRING_FORMAT, date, TimeZoneConstants.UTC), "+00:00", "", true); + return StringUtil.replace(FormatUtil.format(HTTP_TIME_STRING_FORMAT.formatter, date, TimeZoneConstants.UTC), "+00:00", "", true); } public static String format(long time, Locale l, TimeZone tz) { diff --git a/core/src/main/java/lucee/commons/i18n/DateFormatPool.java b/core/src/main/java/lucee/commons/i18n/DateFormatPool.java index 75813844b1..ce2e5b27c2 100644 --- a/core/src/main/java/lucee/commons/i18n/DateFormatPool.java +++ b/core/src/main/java/lucee/commons/i18n/DateFormatPool.java @@ -43,10 +43,10 @@ public static String format(Locale locale, String pattern, Date date) { public static String format(Locale locale, TimeZone timeZone, String pattern, Date date) { if (timeZone == null) timeZone = ThreadLocalPageContext.getTimeZone(); - return FormatUtil.format(getSimpleDateFormat(locale, pattern), date, timeZone); + return FormatUtil.format(getSimpleDateFormat(locale, pattern, timeZone), date, timeZone); } - private static DateTimeFormatter getSimpleDateFormat(Locale locale, String pattern) { + private static DateTimeFormatter getSimpleDateFormat(Locale locale, String pattern, TimeZone timeZone) { if (locale == null) locale = ThreadLocalPageContext.getLocale(); String key = locale.toString() + '-' + pattern; @@ -58,7 +58,7 @@ private static DateTimeFormatter getSimpleDateFormat(Locale locale, String patte ref = datax.get(key); sdf = ref == null ? null : ref.get(); if (sdf == null) { - sdf = FormatUtil.getDateTimeFormatter(locale, pattern); + sdf = FormatUtil.getDateTimeFormatter(locale, pattern, timeZone).formatter; datax.put(key, new SoftReference<>(sdf)); } } diff --git a/core/src/main/java/lucee/commons/i18n/FormatUtil.java b/core/src/main/java/lucee/commons/i18n/FormatUtil.java index c1998d3b24..f215bde80a 100644 --- a/core/src/main/java/lucee/commons/i18n/FormatUtil.java +++ b/core/src/main/java/lucee/commons/i18n/FormatUtil.java @@ -23,7 +23,9 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -56,74 +58,81 @@ public class FormatUtil { public static final short FORMAT_TYPE_DATE_TIME = 3; public static final short FORMAT_TYPE_DATE_ALL = 4; + private static final LocalTime DEFAULT_TIME = LocalTime.of(0, 0, 0); + private static final LocalDate DEFAULT_DATE = LocalDate.of(1970, 1, 1); + private final static Map> formats = new ConcurrentHashMap>(); private final static Map>> cfmlFormats = new ConcurrentHashMap<>(); // "EEEE, MMMM d, yyyy, h:mm:ss a 'Coordinated Universal Time'" - private final static String[] strCfmlFormats = new String[] { + private final static Pattern[] strCfmlFormats = new Pattern[] { + + new Pattern("dd/MM/yyyy", FORMAT_TYPE_DATE), - "dd-MMM-yyyy", + new Pattern("dd-MMM-yyyy", FORMAT_TYPE_DATE), - "dd-MMM-yy HH:mm a", + new Pattern("dd-MMM-yy HH:mm a", FORMAT_TYPE_DATE_TIME), - "dd-MMMM-yy HH:mm a", + new Pattern("dd-MMMM-yy HH:mm a", FORMAT_TYPE_DATE_TIME), - "dd MMM, yyyy HH:mm:ss", + new Pattern("dd MMM, yyyy HH:mm:ss", FORMAT_TYPE_DATE_TIME), - "dd MMM yyyy HH:mm:ss zz", + new Pattern("dd MMM yyyy HH:mm:ss zz", FORMAT_TYPE_DATE_TIME), - "MMMM d yyyy HH:mm", + new Pattern("MMMM d yyyy HH:mm", FORMAT_TYPE_DATE_TIME), - "MMMM d yyyy HH:mm:ss", + new Pattern("MMMM d yyyy HH:mm:ss", FORMAT_TYPE_DATE_TIME), - "MMM dd, yyyy HH:mm:ss", + new Pattern("MMM dd, yyyy HH:mm:ss", FORMAT_TYPE_DATE_TIME), - "MMMM, dd yyyy HH:mm:ss", + new Pattern("MMMM, dd yyyy HH:mm:ss", FORMAT_TYPE_DATE_TIME), - "MMMM d yyyy HH:mm:ssZ", + new Pattern("MMMM d yyyy HH:mm:ssZ", FORMAT_TYPE_DATE_TIME), - "MMM dd, yyyy HH:mm:ss a", + new Pattern("MMM dd, yyyy HH:mm:ss a", FORMAT_TYPE_DATE_TIME), - "MMMM, dd yyyy HH:mm:ssZ", + new Pattern("MMMM, dd yyyy HH:mm:ssZ", FORMAT_TYPE_DATE_TIME), - "MMMM, dd yyyy HH:mm:ss Z", + new Pattern("MMMM, dd yyyy HH:mm:ss Z", FORMAT_TYPE_DATE_TIME), - "MMMM dd, yyyy HH:mm:ss a zzz", + new Pattern("MMMM dd, yyyy HH:mm:ss a zzz", FORMAT_TYPE_DATE_TIME), - "EEE, MMM dd, yyyy HH:mm:ss", + new Pattern("EEE, MMM dd, yyyy HH:mm:ss", FORMAT_TYPE_DATE_TIME), - "EEE MMM dd HH:mm:ss z yyyy", + new Pattern("EEE MMM dd HH:mm:ss z yyyy", FORMAT_TYPE_DATE_TIME), - "EE, dd-MMM-yyyy HH:mm:ss zz", + new Pattern("EE, dd-MMM-yyyy HH:mm:ss zz", FORMAT_TYPE_DATE_TIME), - "EE, dd MMM yyyy HH:mm:ss zz", + new Pattern("EE, dd MMM yyyy HH:mm:ss zz", FORMAT_TYPE_DATE_TIME), - "EEE d, MMM yyyy HH:mm:ss zz", + new Pattern("EEE d, MMM yyyy HH:mm:ss zz", FORMAT_TYPE_DATE_TIME), - "EEE, dd MMM yyyy HH:mm:ss Z", + new Pattern("EEE, dd MMM yyyy HH:mm:ss Z", FORMAT_TYPE_DATE_TIME), - "EEE, MMM dd, yyyy HH:mm:ssZ", + new Pattern("EEE, MMM dd, yyyy HH:mm:ssZ", FORMAT_TYPE_DATE_TIME), - "EEE, dd MMM yyyy HH:mm:ss Z", + new Pattern("EEE, dd MMM yyyy HH:mm:ss Z", FORMAT_TYPE_DATE_TIME), - "EEEE, MMMM d, yyyy, h:mm:ss a z", + new Pattern("EEEE, MMMM d, yyyy, h:mm:ss a z", FORMAT_TYPE_DATE_TIME), - "EEEE, MMMM d, yyyy, h:mm:ss a zzzz", + new Pattern("EEEE, MMMM d, yyyy, h:mm:ss a zzzz", FORMAT_TYPE_DATE_TIME), - "EEE MMM dd yyyy HH:mm:ss 'GMT'ZZ (z)", + new Pattern("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZ (z)", FORMAT_TYPE_DATE_TIME), - "EEE MMM dd yyyy HH:mm:ss 'GMT'ZZ (zzzz)", + new Pattern("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZ (zzzz)", FORMAT_TYPE_DATE_TIME), - "yyyy/MM/dd HH:mm:ss zz", + new Pattern("yyyy/MM/dd HH:mm:ss zz", FORMAT_TYPE_DATE_TIME), - "yyyy-MM-dd HH:mm:ss zz", + new Pattern("yyyy-MM-dd HH:mm:ss zz", FORMAT_TYPE_DATE_TIME), - "yyyy-MM-dd'T'HH:mm:ssXXX", + new Pattern("yyyy-MM-dd'T'HH:mm:ssXXX", FORMAT_TYPE_DATE_TIME), - "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + new Pattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", FORMAT_TYPE_DATE_TIME) }; + private static final Map> dateTimeFormatter = new ConcurrentHashMap<>(); + public static List getAllFormats(Locale locale, TimeZone timeZone, boolean lenient) { String key = "all:" + locale.toString() + "-" + timeZone.getID() + ":" + lenient; SoftReference> sr = cfmlFormats.get(key); @@ -166,11 +175,15 @@ public static List getCFMLFormats(Locale locale, TimeZone time ZoneId zone = timeZone.toZoneId(); formatter = new ArrayList<>(); DateTimeFormatterBuilder builder; - for (String f: strCfmlFormats) { - builder = new DateTimeFormatterBuilder().appendPattern(f); + for (Pattern p: strCfmlFormats) { + builder = new DateTimeFormatterBuilder().appendPattern(p.pattern); if (lenient) builder.parseCaseInsensitive(); else builder.parseCaseSensitive(); - formatter.add(new FormatterWrapper(builder.toFormatter(locale).withZone(zone))); + + DateTimeFormatter dtf; + if (p.type == FORMAT_TYPE_DATE_TIME) dtf = builder.toFormatter(locale).withZone(zone); + else dtf = builder.toFormatter(locale); + formatter.add(new FormatterWrapper(dtf, p.pattern, p.type, zone, true)); } cfmlFormats.put(key, new SoftReference(formatter)); } @@ -190,25 +203,41 @@ public static List getDateTimeFormats(Locale locale, TimeZone if (df == null) { ZoneId zone = tz.toZoneId(); df = new ArrayList<>(); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.LONG).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.SHORT).withLocale(locale).withZone(zone))); - - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.FULL).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.LONG).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.MEDIUM).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT).withLocale(locale).withZone(zone))); - - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.FULL).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.LONG).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).withLocale(locale).withZone(zone))); - - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.FULL).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.LONG).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.MEDIUM).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT).withLocale(locale).withZone(zone))); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL).withLocale(locale).withZone(zone), "FULL_FULL", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.LONG).withLocale(locale).withZone(zone), "FULL_LONG", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM).withLocale(locale).withZone(zone), "FULL_MEDIUM", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.SHORT).withLocale(locale).withZone(zone), "FULL_SHORT", + FORMAT_TYPE_DATE_TIME, zone)); + + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.FULL).withLocale(locale).withZone(zone), "LONG_FULL", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.LONG).withLocale(locale).withZone(zone), "LONG_LONG", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.MEDIUM).withLocale(locale).withZone(zone), "LONG_MEDIUM", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT).withLocale(locale).withZone(zone), "LONG_SHORT", + FORMAT_TYPE_DATE_TIME, zone)); + + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.FULL).withLocale(locale).withZone(zone), "MEDIUM_FULL", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.LONG).withLocale(locale).withZone(zone), "MEDIUM_LONG", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM).withLocale(locale).withZone(zone), "MEDIUM_MEDIUM", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT).withLocale(locale).withZone(zone), "MEDIUM_SHORT", + FORMAT_TYPE_DATE_TIME, zone)); + + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.FULL).withLocale(locale).withZone(zone), "SHORT_FULL", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.LONG).withLocale(locale).withZone(zone), "SHORT_LONG", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.MEDIUM).withLocale(locale).withZone(zone), "SHORT_MEDIUM", + FORMAT_TYPE_DATE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT).withLocale(locale).withZone(zone), "SHORT_SHORT", + FORMAT_TYPE_DATE_TIME, zone)); cfmlFormats.put(key, new SoftReference>(df)); } @@ -260,7 +289,6 @@ public static DateFormat[] getDateTimeFormatsOld(Locale locale, TimeZone tz, boo } public static List getDateFormats(Locale locale, TimeZone tz, boolean lenient) { - String key = "d-" + locale.toString() + "-" + tz.getID() + "-" + lenient; SoftReference> tmp = cfmlFormats.get(key); List df = tmp == null ? null : tmp.get(); @@ -270,10 +298,10 @@ public static List getDateFormats(Locale locale, TimeZone tz, if (df == null) { ZoneId zone = tz.toZoneId(); df = new ArrayList<>(); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale).withZone(zone))); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale).withZone(zone), "FULL", FORMAT_TYPE_DATE, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(locale).withZone(zone), "LONG", FORMAT_TYPE_DATE, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale).withZone(zone), "MEDIUM", FORMAT_TYPE_DATE, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale).withZone(zone), "SHORT", FORMAT_TYPE_DATE, zone)); cfmlFormats.put(key, new SoftReference>(df)); } @@ -511,10 +539,10 @@ public static List getTimeFormats(Locale locale, TimeZone tz, if (df == null) { ZoneId zone = tz.toZoneId(); df = new ArrayList<>(); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(locale).withZone(zone))); - df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(locale).withZone(zone))); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL).withLocale(locale).withZone(zone), "FULL", FORMAT_TYPE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG).withLocale(locale).withZone(zone), "LONG", FORMAT_TYPE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM).withLocale(locale).withZone(zone), "MEDIUM", FORMAT_TYPE_TIME, zone)); + df.add(new FormatterWrapper(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(locale).withZone(zone), "SHORT", FORMAT_TYPE_TIME, zone)); cfmlFormats.put(key, new SoftReference>(df)); } @@ -553,22 +581,45 @@ public static DateFormat getDateTimeFormat(Locale locale, TimeZone tz, String ma return df; } - public static DateTimeFormatter getDateTimeFormatter(Locale locale, String mask) { - DateTimeFormatter formatter; - if (mask.equalsIgnoreCase("short")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); - else if (mask.equalsIgnoreCase("medium")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); - else if (mask.equalsIgnoreCase("long")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); - else if (mask.equalsIgnoreCase("full")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL); - else if (mask.equalsIgnoreCase("iso8601") || mask.equalsIgnoreCase("iso")) { - formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); - } - else if (mask.equalsIgnoreCase("isoms") || mask.equalsIgnoreCase("isoMillis") || mask.equalsIgnoreCase("javascript")) { - formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); - } - else formatter = DateTimeFormatter.ofPattern(mask); + public static FormatterWrapper getDateTimeFormatter(Locale locale, String mask) { + return getDateTimeFormatter(locale, mask, (ZoneId) null); + } - if (locale != null) formatter = formatter.withLocale(locale); - return formatter; + public static FormatterWrapper getDateTimeFormatter(Locale locale, String mask, TimeZone tz) { + return getDateTimeFormatter(locale, mask, tz == null ? null : tz.toZoneId()); + } + + public static FormatterWrapper getDateTimeFormatter(Locale locale, String mask, ZoneId zone) { + String key = locale.toString() + ":" + mask; + SoftReference ref = dateTimeFormatter.get(key); + FormatterWrapper fw = ref == null ? null : ref.get(); + if (fw == null) { + synchronized (SystemUtil.createToken("getDateTimeFormatter", key)) { + ref = dateTimeFormatter.get(key); + fw = ref == null ? null : ref.get(); + if (fw == null) { + // TODO cache + DateTimeFormatter formatter; + if (mask.equalsIgnoreCase("short")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + else if (mask.equalsIgnoreCase("medium")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); + else if (mask.equalsIgnoreCase("long")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); + else if (mask.equalsIgnoreCase("full")) formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL); + else if (mask.equalsIgnoreCase("iso8601") || mask.equalsIgnoreCase("iso")) { + formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); + } + else if (mask.equalsIgnoreCase("isoms") || mask.equalsIgnoreCase("isoMillis") || mask.equalsIgnoreCase("javascript")) { + formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + } + else formatter = DateTimeFormatter.ofPattern(mask); + + if (locale != null) formatter = formatter.withLocale(locale); + + fw = new FormatterWrapper(formatter, mask, FORMAT_TYPE_DATE_TIME, zone); + dateTimeFormatter.put(key, new SoftReference(fw)); + } + } + } + return fw; } public static String format(DateTimeFormatter formatter, Date date, TimeZone timeZone) { @@ -583,7 +634,7 @@ public static long parseSimple(DateTimeFormatter formatter, String date, TimeZon return ZonedDateTime.parse(date, formatter).withZoneSameInstant(timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault()).toInstant().toEpochMilli(); } - public static long parse(DateTimeFormatter formatter, String date, TimeZone timeZone) throws DateTimeParseException { + public static long parseX(DateTimeFormatter formatter, String date, TimeZone timeZone) throws DateTimeParseException { // Parse the date using the formatter (no time zone assumption yet) ZonedDateTime zonedDateTime = null; @@ -600,4 +651,41 @@ public static long parse(DateTimeFormatter formatter, String date, TimeZone time // Convert the parsed ZonedDateTime to the desired time zone and return epoch milliseconds return zonedDateTime.withZoneSameInstant(timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault()).toInstant().toEpochMilli(); } + + public static long parse(FormatterWrapper fw, String date, ZoneId zone) { + return parse(fw.formatter, date, fw.type, zone); + } + + public static long parse(DateTimeFormatter formatter, String date, short type, ZoneId zone) { + + if (type == FormatUtil.FORMAT_TYPE_DATE_TIME) { + return ZonedDateTime.parse(date, formatter).toInstant().toEpochMilli(); + } + else if (type == FormatUtil.FORMAT_TYPE_DATE) { + return getEpochMillis(LocalDate.parse(date, formatter), DEFAULT_TIME, zone); + + } + return getEpochMillis(DEFAULT_DATE, LocalTime.parse(date, formatter), zone); + } + + private static long getEpochMillis(LocalDate localDate, LocalTime localTime, ZoneId zoneId) { + // Combine LocalDate and LocalTime into LocalDateTime + LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + + // Convert LocalDateTime to ZonedDateTime with the specified time zone + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + + // Convert to Instant and get epoch millis + return zonedDateTime.toInstant().toEpochMilli(); + } + + private static class Pattern { + public final String pattern; + public final short type; + + Pattern(String pattern, short type) { + this.pattern = pattern; + this.type = type; + } + } } \ No newline at end of file diff --git a/core/src/main/java/lucee/commons/i18n/FormatterWrapper.java b/core/src/main/java/lucee/commons/i18n/FormatterWrapper.java index d6c97cbace..2702895843 100644 --- a/core/src/main/java/lucee/commons/i18n/FormatterWrapper.java +++ b/core/src/main/java/lucee/commons/i18n/FormatterWrapper.java @@ -1,13 +1,31 @@ package lucee.commons.i18n; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; public class FormatterWrapper { public final DateTimeFormatter formatter; public int successCount; + public final String pattern; + public final boolean custom; + public final short type; + public final ZoneId zone; - FormatterWrapper(DateTimeFormatter formatter) { + FormatterWrapper(DateTimeFormatter formatter, String pattern, short type, ZoneId zone) { this.formatter = formatter; this.successCount = 0; + this.pattern = pattern; + this.type = type; + this.zone = zone; + this.custom = false; + } + + FormatterWrapper(DateTimeFormatter formatter, String pattern, short type, ZoneId zone, boolean custom) { + this.formatter = formatter; + this.successCount = 0; + this.pattern = pattern; + this.type = type; + this.zone = zone; + this.custom = custom; } } \ No newline at end of file diff --git a/core/src/main/java/lucee/commons/io/log/log4j2/layout/DataDogLayout.java b/core/src/main/java/lucee/commons/io/log/log4j2/layout/DataDogLayout.java index b6d67b807f..85677c4ce7 100644 --- a/core/src/main/java/lucee/commons/io/log/log4j2/layout/DataDogLayout.java +++ b/core/src/main/java/lucee/commons/io/log/log4j2/layout/DataDogLayout.java @@ -43,7 +43,7 @@ public DataDogLayout() { super(CharsetUtil.UTF8, new byte[0], new byte[0]); engine = CFMLEngineFactory.getInstance(); caster = engine.getCastUtil(); - format = FormatUtil.getDateTimeFormatter(null, "yyyy-MM-dd HH:mm:ss"); + format = FormatUtil.getDateTimeFormatter(null, "yyyy-MM-dd HH:mm:ss").formatter; } @Override diff --git a/core/src/main/java/lucee/commons/lang/SystemOut.java b/core/src/main/java/lucee/commons/lang/SystemOut.java index da0b2c0aba..e3b28d5455 100755 --- a/core/src/main/java/lucee/commons/lang/SystemOut.java +++ b/core/src/main/java/lucee/commons/lang/SystemOut.java @@ -35,7 +35,7 @@ public final class SystemOut { - public static final DateTimeFormatter FORMAT = FormatUtil.getDateTimeFormatter(null, "yyyy-MM-dd HH:mm:ss.S"); + public static final DateTimeFormatter FORMAT = FormatUtil.getDateTimeFormatter(null, "yyyy-MM-dd HH:mm:ss.S").formatter; /** * logs a value diff --git a/core/src/main/java/lucee/runtime/converter/JSONDateFormat.java b/core/src/main/java/lucee/runtime/converter/JSONDateFormat.java index 3d4846e81c..3a323243ee 100644 --- a/core/src/main/java/lucee/runtime/converter/JSONDateFormat.java +++ b/core/src/main/java/lucee/runtime/converter/JSONDateFormat.java @@ -52,7 +52,7 @@ public static String format(Date date, TimeZone tz, String pattern) { tmp = map.get(id); format = tmp == null ? null : tmp.get(); if (format == null) { - format = FormatUtil.getDateTimeFormatter(locale, pattern); + format = FormatUtil.getDateTimeFormatter(locale, pattern).formatter; map.put(id, new SoftReference(format)); } } diff --git a/core/src/main/java/lucee/runtime/dump/DumpUtil.java b/core/src/main/java/lucee/runtime/dump/DumpUtil.java index d2032f1fa9..09d5d432f0 100644 --- a/core/src/main/java/lucee/runtime/dump/DumpUtil.java +++ b/core/src/main/java/lucee/runtime/dump/DumpUtil.java @@ -116,7 +116,7 @@ public static DumpData toDumpData(Object o, PageContext pageContext, int maxleve // Calendar if (o instanceof Calendar) { Calendar c = (Calendar) o; - DateTimeFormatter formatter = FormatUtil.getDateTimeFormatter(Locale.ENGLISH, "EE, dd MMM yyyy HH:mm:ss zz"); + DateTimeFormatter formatter = FormatUtil.getDateTimeFormatter(Locale.ENGLISH, "EE, dd MMM yyyy HH:mm:ss zz").formatter; DumpTable table = new DumpTable("date", "#ff9900", "#ffcc00", "#000000"); table.setTitle("java.util.Calendar"); table.appendRow(1, new SimpleDumpData("Timezone"), new SimpleDumpData(TimeZoneUtil.toString(c.getTimeZone()))); diff --git a/core/src/main/java/lucee/runtime/format/DateFormat.java b/core/src/main/java/lucee/runtime/format/DateFormat.java index 9f087affff..446a9caecb 100644 --- a/core/src/main/java/lucee/runtime/format/DateFormat.java +++ b/core/src/main/java/lucee/runtime/format/DateFormat.java @@ -82,7 +82,7 @@ public String format(long time, String mask, TimeZone tz) { else if (lcMask.equals("long")) return getAsString(calendar, java.text.DateFormat.LONG, tz); else if (lcMask.equals("full")) return getAsString(calendar, java.text.DateFormat.FULL, tz); else if ("iso8601".equals(lcMask) || "iso".equals(lcMask)) { - DateTimeFormatter formatter = FormatUtil.getDateTimeFormatter(null, "yyyy-MM-dd"); + DateTimeFormatter formatter = FormatUtil.getDateTimeFormatter(null, "yyyy-MM-dd").formatter; return FormatUtil.format(formatter, calendar.getTime(), tz); } diff --git a/core/src/main/java/lucee/runtime/functions/displayFormatting/DateTimeFormat.java b/core/src/main/java/lucee/runtime/functions/displayFormatting/DateTimeFormat.java index 6b9c0162d5..d0f94d7291 100644 --- a/core/src/main/java/lucee/runtime/functions/displayFormatting/DateTimeFormat.java +++ b/core/src/main/java/lucee/runtime/functions/displayFormatting/DateTimeFormat.java @@ -18,11 +18,11 @@ **/ package lucee.runtime.functions.displayFormatting; -import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.TimeZone; import lucee.commons.i18n.FormatUtil; +import lucee.commons.i18n.FormatterWrapper; import lucee.commons.lang.StringUtil; import lucee.runtime.PageContext; import lucee.runtime.engine.ThreadLocalPageContext; @@ -93,7 +93,7 @@ else if ("epochms".equalsIgnoreCase(mask)) { return epoch; } - DateTimeFormatter formatter; + FormatterWrapper formatter; if (mask != null && ( mask.equalsIgnoreCase("short") || @@ -123,7 +123,7 @@ else if ("epochms".equalsIgnoreCase(mask)) { else { formatter = FormatUtil.getDateTimeFormatter(locale, convertMask(mask)); - String result = FormatUtil.format(formatter, datetime, tz); + String result = FormatUtil.format(formatter.formatter, datetime, tz); if (!StringUtil.isEmpty(result)) { int start, end = 0; String content; @@ -139,7 +139,7 @@ else if ("epochms".equalsIgnoreCase(mask)) { } return result; } - return FormatUtil.format(formatter, datetime, tz); + return FormatUtil.format(formatter.formatter, datetime, tz); } @Override diff --git a/core/src/main/java/lucee/runtime/functions/international/LSParseDateTime.java b/core/src/main/java/lucee/runtime/functions/international/LSParseDateTime.java index 4a799f2fb9..58b37a9f8f 100644 --- a/core/src/main/java/lucee/runtime/functions/international/LSParseDateTime.java +++ b/core/src/main/java/lucee/runtime/functions/international/LSParseDateTime.java @@ -26,6 +26,7 @@ import java.util.Locale; import java.util.TimeZone; +import lucee.print; import lucee.commons.date.TimeZoneUtil; import lucee.commons.i18n.FormatUtil; import lucee.commons.lang.ExceptionUtil; @@ -79,17 +80,25 @@ private static lucee.runtime.type.dt.DateTime _call(PageContext pc, Object oDate if (locale == null) locale = pc.getLocale(); DateFormat df = FormatUtil.getDateTimeFormat(locale, tz, format); try { - - return new DateTimeImpl(df.parse(strDate)); - // old.rocks - // return new DateTimeImpl(FormatUtil.parse(FormatUtil.getDateTimeFormatter(locale, format), - // strDate, tz)); + return new DateTimeImpl(FormatUtil.parse(FormatUtil.getDateTimeFormatter(locale, format), strDate, tz.toZoneId())); } - catch (Exception e) { - ExpressionException ee = new ExpressionException("could not parse the date [" + strDate + "] with the format [" + format + "] with the locale [" + locale - + "] and the timezone [" + (tz == null ? "" : tz.getID()) + "]"); - ExceptionUtil.initCauseEL(ee, e); - throw ee; + catch (Exception ex) { + try { + + DateTimeImpl res = new DateTimeImpl(df.parse(strDate)); + print.e("--------- old rocks --------"); + print.e(ex); + return res; + // old.rocks + // return new DateTimeImpl(FormatUtil.parse(FormatUtil.getDateTimeFormatter(locale, format), + // strDate, tz)); + } + catch (Exception e) { + ExpressionException ee = new ExpressionException("could not parse the date [" + strDate + "] with the format [" + format + "] with the locale [" + locale + + "] and the timezone [" + (tz == null ? "" : tz.getID()) + "]"); + ExceptionUtil.initCauseEL(ee, e); + throw ee; + } } } diff --git a/core/src/main/java/lucee/runtime/net/smtp/SMTPClient.java b/core/src/main/java/lucee/runtime/net/smtp/SMTPClient.java index 550c93ea4f..2589f939da 100644 --- a/core/src/main/java/lucee/runtime/net/smtp/SMTPClient.java +++ b/core/src/main/java/lucee/runtime/net/smtp/SMTPClient.java @@ -172,7 +172,7 @@ public static String getNow(TimeZone tz) { SoftReference tmp = formatters.get(tz); DateTimeFormatter df = tmp == null ? null : tmp.get(); if (df == null) { - df = FormatUtil.getDateTimeFormatter(Locale.US, "EEE, d MMM yyyy HH:mm:ss Z (z)"); + df = FormatUtil.getDateTimeFormatter(Locale.US, "EEE, d MMM yyyy HH:mm:ss Z (z)").formatter; formatters.put(tz, new SoftReference(df)); } return FormatUtil.format(df, new Date(), tz); diff --git a/core/src/main/java/lucee/runtime/op/date/DateCaster.java b/core/src/main/java/lucee/runtime/op/date/DateCaster.java index 0fa9922596..5585168891 100755 --- a/core/src/main/java/lucee/runtime/op/date/DateCaster.java +++ b/core/src/main/java/lucee/runtime/op/date/DateCaster.java @@ -23,7 +23,6 @@ import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.time.Instant; -import java.time.ZonedDateTime; import java.util.Calendar; import java.util.Comparator; import java.util.Date; @@ -31,6 +30,7 @@ import java.util.Locale; import java.util.TimeZone; +import lucee.print; import lucee.commons.date.DateTimeUtil; import lucee.commons.date.JREDateTimeUtil; import lucee.commons.date.TimeZoneConstants; @@ -42,6 +42,7 @@ import lucee.runtime.engine.ThreadLocalPageContext; import lucee.runtime.exp.ExpressionException; import lucee.runtime.exp.PageException; +import lucee.runtime.i18n.LocaleConstant; import lucee.runtime.i18n.LocaleFactory; import lucee.runtime.op.Castable; import lucee.runtime.op.Caster; @@ -212,7 +213,10 @@ public static DateTime toDateTime(Locale locale, String str, TimeZone tz, boolea DateTime dt = toDateTimeNew(locale, str, tz, null, useCommomDateParserAsWell); if (dt == null) { dt = toDateTimeOld(locale, str, tz, null, false); - // if (dt != null) print.e("old.rocks(" + locale + "):" + str); + if (dt != null) { + print.e("--------- old rockx --------"); + print.e(locale + "-" + str + "-" + tz.getID() + ":" + useCommomDateParserAsWell); + } } if (dt == null) { String prefix = locale.getLanguage() + "-" + locale.getCountry() + "-"; @@ -225,6 +229,10 @@ public static DateTime toDateTime(Locale locale, String str, TimeZone tz, boolea return dt; } + public static void main(String[] args) throws PageException { + toDateTime(LocaleConstant.ENGLISH_UNITED_KINDOM, "31/12/2008", TimeZoneConstants.CET, false); + } + /** * parse a string to a Datetime Object, returns null if can't convert * @@ -311,12 +319,18 @@ public static DateTime toDateTimeNew(Locale locale, String str, TimeZone tz, Dat try { for (FormatterWrapper fw: all) { + + // if (fw.custom && fw.pattern.length() != str.length()) continue; try { - DateTimeImpl res = new DateTimeImpl(Date.from(ZonedDateTime.parse(str, fw.formatter).toInstant()).getTime(), false); + DateTimeImpl res = new DateTimeImpl(FormatUtil.parse(fw.formatter, str, fw.type, fw.zone)); fw.successCount++; return res; } catch (Exception e) { + if (fw.custom) { + print.e("---- " + fw.pattern + " ------"); + print.e(e); + } } } } diff --git a/core/src/main/java/lucee/runtime/type/dt/DateImpl.java b/core/src/main/java/lucee/runtime/type/dt/DateImpl.java index b1815f79a4..7389a9499b 100755 --- a/core/src/main/java/lucee/runtime/type/dt/DateImpl.java +++ b/core/src/main/java/lucee/runtime/type/dt/DateImpl.java @@ -39,7 +39,7 @@ */ public final class DateImpl extends Date implements SimpleValue { - private static DateTimeFormatter luceeFormatter = FormatUtil.getDateTimeFormatter(Locale.US, "yyyy-MM-dd"); + private static DateTimeFormatter luceeFormatter = FormatUtil.getDateTimeFormatter(Locale.US, "yyyy-MM-dd").formatter; // private TimeZone timezone; diff --git a/core/src/main/java/lucee/runtime/type/dt/TimeImpl.java b/core/src/main/java/lucee/runtime/type/dt/TimeImpl.java index 7412bafa71..2d96f68823 100755 --- a/core/src/main/java/lucee/runtime/type/dt/TimeImpl.java +++ b/core/src/main/java/lucee/runtime/type/dt/TimeImpl.java @@ -40,7 +40,7 @@ */ public final class TimeImpl extends Time implements SimpleValue { - private static DateTimeFormatter luceeFormatter = FormatUtil.getDateTimeFormatter(Locale.US, "HH:mm:ss"); + private static DateTimeFormatter luceeFormatter = FormatUtil.getDateTimeFormatter(Locale.US, "HH:mm:ss").formatter; public TimeImpl() { super(System.currentTimeMillis()); diff --git a/core/src/main/java/org/apache/taglibs/datetime/FormatTag.java b/core/src/main/java/org/apache/taglibs/datetime/FormatTag.java index 013fdac74a..91f3410336 100644 --- a/core/src/main/java/org/apache/taglibs/datetime/FormatTag.java +++ b/core/src/main/java/org/apache/taglibs/datetime/FormatTag.java @@ -115,7 +115,7 @@ public final int doEndTag() throws PageException { if (pat == null) { pat = new SimpleDateFormat().toPattern(); // TODO find a better way for this - formatter = FormatUtil.getDateTimeFormatter(null, pat); + formatter = FormatUtil.getDateTimeFormatter(null, pat).formatter; } // Get a DateFormatSymbols @@ -129,13 +129,13 @@ public final int doEndTag() throws PageException { if (locale == null) { throw new ApplicationException("datetime format tag could not find locale for localeRef \"" + localeRef + "\"."); } - formatter = FormatUtil.getDateTimeFormatter(locale, pat); + formatter = FormatUtil.getDateTimeFormatter(locale, pat).formatter; } else if (locale_flag) { - formatter = FormatUtil.getDateTimeFormatter(pageContext.getRequest().getLocale(), pat); + formatter = FormatUtil.getDateTimeFormatter(pageContext.getRequest().getLocale(), pat).formatter; } else { - formatter = FormatUtil.getDateTimeFormatter(null, pat); + formatter = FormatUtil.getDateTimeFormatter(null, pat).formatter; } // See if there is a timeZone diff --git a/loader/build.xml b/loader/build.xml index 38808b64f1..99d9cd120b 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index 2a107207ef..4e26f0188b 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 6.2.0.124-SNAPSHOT + 6.2.0.125-SNAPSHOT jar Lucee Loader Build