diff --git a/src/main/java/com/cedarsoftware/util/DateUtilities.java b/src/main/java/com/cedarsoftware/util/DateUtilities.java index 539472ac4..f4085433f 100644 --- a/src/main/java/com/cedarsoftware/util/DateUtilities.java +++ b/src/main/java/com/cedarsoftware/util/DateUtilities.java @@ -112,7 +112,7 @@ public final class DateUtilities { Pattern.CASE_INSENSITIVE); private static final Pattern timePattern = Pattern.compile( - "(" + d2 + "):(" + d2 + ")(?::(" + d2 + ")(" + nano + ")?)?(" + tz_Hh_MM + "|" + tz_HHMM + "|" + tz_Hh + "|Z|" + tzNamed + ")?", + "(" + d2 + "):(" + d2 + ")(?::(" + d2 + ")(" + nano + ")?)?(" + tz_Hh_MM + "|" + tz_HHMM + "|" + tz_Hh + "|Z)?(" + tzNamed + ")?", Pattern.CASE_INSENSITIVE); private static final Pattern dayPattern = Pattern.compile("\\b(" + days + ")\\b", Pattern.CASE_INSENSITIVE); @@ -150,9 +150,9 @@ private DateUtilities() { } /** - * Original API. If the date-time given does not include a timezone offset, then ZoneId.systemDefault() will be used. - * We recommend using the parseDate(3 args) version, so you can control the default timezone used when one is not - * specified. + * Original API. If the date-time given does not include a timezone offset or name, then ZoneId.systemDefault() + * will be used. We recommend using the parseDate(3 args) version, so you can control the default timezone used + * when one is not specified. * @param dateStr String containing a date. If there is excess content, it will throw an IllegalArgumentException. * @return Date instance that represents the passed in date. See comments at top of class for supported * formats. This API is intended to be super flexible in terms of what it can parse. If a null or empty String is @@ -185,7 +185,7 @@ public static ZonedDateTime parseDate(String dateStr, ZoneId defaultZoneId, bool dateStr = dateStr.trim(); if (allDigits.matcher(dateStr).matches()) { - return Instant.ofEpochMilli(Long.parseLong(dateStr)).atZone(defaultZoneId); + return Instant.ofEpochMilli(Long.parseLong(dateStr)).atZone(ZoneId.of("UTC")); } String year, day, remains, tz = null; @@ -246,7 +246,6 @@ public static ZonedDateTime parseDate(String dateStr, ZoneId defaultZoneId, bool remains = remains.trim(); matcher = timePattern.matcher(remains); remnant = matcher.replaceFirst(""); - boolean noTime = false; if (remnant.length() < remains.length()) { hour = matcher.group(1); @@ -258,7 +257,12 @@ public static ZonedDateTime parseDate(String dateStr, ZoneId defaultZoneId, bool fracSec = "0" + matcher.group(4); } if (matcher.group(5) != null) { - tz = stripBrackets(matcher.group(5).trim()); + tz = matcher.group(5).trim(); + } + if (matcher.group(6) != null) { + if (StringUtilities.isEmpty(tz)) { // Only use timezone name when offset is not used + tz = stripBrackets(matcher.group(6).trim()); + } } } diff --git a/src/test/java/com/cedarsoftware/util/TestDateUtilities.java b/src/test/java/com/cedarsoftware/util/TestDateUtilities.java index 94ca4621d..443aef0c7 100644 --- a/src/test/java/com/cedarsoftware/util/TestDateUtilities.java +++ b/src/test/java/com/cedarsoftware/util/TestDateUtilities.java @@ -870,7 +870,7 @@ void testTimeBetterThanMilliResolution() assertEquals(-60*60*5, zonedDateTime.getOffset().getTotalSeconds()); } - private static Stream provideBadFormats() { + private static Stream provideRedundantFormats() { return Stream.of( Arguments.of("2024-01-19T12:00:00-08:00[America/Los_Angeles]"), Arguments.of("2024-01-19T22:30:00+01:00[Europe/Paris]"), @@ -890,14 +890,11 @@ private static Stream provideBadFormats() { Arguments.of("2024-01-19T22:30:00+01:00 Europe/Paris"), Arguments.of("2024-01-19T23:59:59Z UTC"), Arguments.of("2024-01-19T23:59:59Z[UTC]"), - Arguments.of("2024-01-19T07:30:01[UTC] America/New_York"), Arguments.of("2024-01-19T07:30:01.123+0100GMT"), Arguments.of("2024-01-19T07:30:01.123+0100[GMT]"), Arguments.of("2024-01-19T07:30:01.123+0100 GMT"), Arguments.of("2024-01-19T07:30:01.123+0100 [GMT]"), Arguments.of("2024-01-19T07:30:01.123-1000GMT"), - Arguments.of("2024-01-19T07:30:01.123-1000[GMT ]"), - Arguments.of("2024-01-19T07:30:01.123-1000[ GMT ]"), Arguments.of("2024-01-19T07:30:01.123-1000 GMT"), Arguments.of("2024-01-19T07:30:01.123-1000 [GMT]"), Arguments.of("2024-01-19T07:30:01.123+2 GMT"), @@ -929,20 +926,14 @@ private static Stream provideBadFormats() { Arguments.of("07:30:01.123-11:00 [EST] January 21, 2024, Sunday"), Arguments.of("07:30:01.123-11:00 [America/New_York] January 21, 2024, Sunday"), Arguments.of("07:30:01.123-11:00 [Africa/Cairo] 21 Jan 2024 Sun"), - Arguments.of("07:30:01.123-11:00 [Africa/Cairo] 2024 Jan 21st Sat"), - Arguments.of("12.17.1965 07:05:"), - Arguments.of("12.17.1965 07:05:.123"), - Arguments.of("12.17.1965 07:05.123"), - Arguments.of("12.17.1965 07:05.12-0500") + Arguments.of("07:30:01.123-11:00 [Africa/Cairo] 2024 Jan 21st Sat") ); } @ParameterizedTest - @MethodSource("provideBadFormats") + @MethodSource("provideRedundantFormats") void testFormatsThatShouldNotWork(String badFormat) { - assertThatThrownBy(() -> DateUtilities.parseDate(badFormat, ZoneId.systemDefault(), true)) - .isInstanceOf(java.lang.IllegalArgumentException.class) - .hasMessageContaining("Issue parsing date-time, other characters present:"); + DateUtilities.parseDate(badFormat, ZoneId.systemDefault(), true); } } \ No newline at end of file