Skip to content

Commit

Permalink
DateUtilities - Dates must be isolated by default (original behavior)…
Browse files Browse the repository at this point in the history
…. Specifying offset and timezone now throws an IllegalArgumentException.
  • Loading branch information
jdereg committed Jan 24, 2024
1 parent e9e7f7a commit 764be4f
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 85 deletions.
3 changes: 0 additions & 3 deletions src/main/java/com/cedarsoftware/util/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,6 @@ public static AtomicBoolean convertToAtomicBoolean(Object fromInstance)
* @param localDate A Java LocalDate
* @return a long representing the localDate as the number of milliseconds since the
* number of milliseconds since Jan 1, 1970
* @deprecated use convert(localDate, long.class);
*/

public static long localDateToMillis(LocalDate localDate)
Expand All @@ -469,7 +468,6 @@ public static long localDateToMillis(LocalDate localDate)
* @param localDateTime A Java LocalDateTime
* @return a long representing the localDateTime as the number of milliseconds since the
* number of milliseconds since Jan 1, 1970
* @deprecated use convert(localDateTime, long.class);
*/
public static long localDateTimeToMillis(LocalDateTime localDateTime)
{
Expand All @@ -480,7 +478,6 @@ public static long localDateTimeToMillis(LocalDateTime localDateTime)
* @param zonedDateTime A Java ZonedDateTime
* @return a long representing the zonedDateTime as the number of milliseconds since the
* number of milliseconds since Jan 1, 1970
* @deprecated use convert(zonedDateTime, long.class);
*/
public static long zonedDateTimeToMillis(ZonedDateTime zonedDateTime)
{
Expand Down
27 changes: 8 additions & 19 deletions src/main/java/com/cedarsoftware/util/DateUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public final class DateUtilities {
// Patterns defined in BNF influenced style using above named elements
private static final Pattern isoDatePattern = Pattern.compile( // Regex's using | (OR)
"(" + yr + ")(" + sep + ")(" + d1or2 + ")" + "\\2" + "(" + d1or2 + ")|" + // 2024/01/21 (yyyy/mm/dd -or- yyyy-mm-dd -or- yyyy.mm.dd) [optional time, optional day of week] \2 references 1st separator (ensures both same)
"(" + d1or2 + ")(" + sep + ")(" + d1or2 + ")" + "\\6(" + yr + ")"); // 01/21/2024 (mm/dd/yyyy -or- mm-dd-yyyy -or- mm.dd.yyyy) [optional time, optional day of week] \6 references 1st separator (ensures both same)
"(" + d1or2 + ")(" + sep + ")(" + d1or2 + ")" + "\\6(" + yr + ")"); // 01/21/2024 (mm/dd/yyyy -or- mm-dd-yyyy -or- mm.dd.yyyy) [optional time, optional day of week] \6 references 2nd 1st separator (ensures both same)

private static final Pattern alphaMonthPattern = Pattern.compile(
"\\b(" + mos + ")\\b" + wsOrComma + "(" + d1or2 + ")(" + ord + ")?" + wsOrComma + "(" + yr + ")|" + // Jan 21st, 2024 (comma optional between all, day of week optional, time optional, ordinal text optional [st, nd, rd, th])
Expand All @@ -111,9 +111,9 @@ 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);
private static final Map<String, Integer> months = new ConcurrentHashMap<>();

Expand Down Expand Up @@ -151,7 +151,7 @@ private DateUtilities() {
/**
* Main API. Retrieve date-time from passed in String. If the date-time given does not include a timezone or
* timezone offset, then ZoneId.systemDefault() will be used.
* @param dateStr String containing a date. If there is excess content, it will be ignored.
* @param dateStr String containing a date. If there is excess content, it will throw an IlllegalArgumentException.
* @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
* passed in, null will be returned.
Expand All @@ -160,7 +160,7 @@ public static Date parseDate(String dateStr) {
if (StringUtilities.isEmpty(dateStr)) {
return null;
}
ZonedDateTime zonedDateTime = parseDate(dateStr, ZoneId.systemDefault(), false);
ZonedDateTime zonedDateTime = parseDate(dateStr, ZoneId.systemDefault(), true);
return new Date(zonedDateTime.toInstant().toEpochMilli());
}

Expand Down Expand Up @@ -340,24 +340,13 @@ private static void verifyNoGarbageLeft(String remnant) {
if (StringUtilities.length(remnant) > 0) {
Matcher dayMatcher = dayPattern.matcher(remnant);
remnant = dayMatcher.replaceFirst("").trim();
if (remnant.startsWith("T")) {
remnant = remnant.substring(1);
}
}

// Verify that nothing or "," or timezone name is all that remains
// Verify that nothing, "T" or "," is all that remains
if (StringUtilities.length(remnant) > 0) {
remnant = remnant.replaceAll(",|\\[.*?\\]", "").trim();
remnant = remnant.replaceAll("T|,", "").trim();
if (!remnant.isEmpty()) {
try {
ZoneId.of(remnant);
}
catch (Exception e) {
TimeZone timeZone = TimeZone.getTimeZone(remnant);
if (timeZone.getRawOffset() == 0) {
throw new IllegalArgumentException("Issue parsing date-time, other characters present: " + remnant);
}
}
throw new IllegalArgumentException("Issue parsing date-time, other characters present: " + remnant);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/cedarsoftware/util/ProxyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@Deprecated
public final class ProxyFactory
{
/**
Expand Down
162 changes: 99 additions & 63 deletions src/test/java/com/cedarsoftware/util/TestDateUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -769,29 +769,9 @@ private static Stream provideTimeZones()
Arguments.of("2024-01-19T15:30:45[Europe/London]", 1705678245000L),
Arguments.of("2024-01-19T10:15:30[Asia/Tokyo]", 1705626930000L),
Arguments.of("2024-01-19T20:45:00[America/New_York]", 1705715100000L),
Arguments.of("2024-01-19T12:00:00-08:00[America/Los_Angeles]", 1705694400000L),
Arguments.of("2024-01-19T22:30:00+01:00[Europe/Paris]", 1705699800000L),
Arguments.of("2024-01-19T18:15:45+10:00[Australia/Sydney]", 1705652145000L),
Arguments.of("2024-01-19T05:00:00-03:00[America/Sao_Paulo]", 1705651200000L),
Arguments.of("2024-01-19T23:59:59Z[UTC]", 1705708799000L),
Arguments.of("2024-01-19T14:30:00+05:30[Asia/Kolkata]", 1705654800000L),
Arguments.of("2024-01-19T21:45:00-05:00[America/Toronto]", 1705718700000L),

Arguments.of("2024-01-19T16:00:00+02:00[Africa/Cairo]", 1705672800000L),
Arguments.of("2024-01-19T07:30:00-07:00[America/Denver]", 1705674600000L),
Arguments.of("2024-01-19T15:30:45 Europe/London", 1705678245000L),
Arguments.of("2024-01-19T10:15:30 Asia/Tokyo", 1705626930000L),
Arguments.of("2024-01-19T20:45:00 America/New_York", 1705715100000L),
Arguments.of("2024-01-19T12:00:00-08:00 America/Los_Angeles", 1705694400000L),
Arguments.of("2024-01-19T22:30:00+01:00 Europe/Paris", 1705699800000L),
Arguments.of("2024-01-19T18:15:45+10:00 Australia/Sydney", 1705652145000L),
Arguments.of("2024-01-19T05:00:00-03:00 America/Sao_Paulo", 1705651200000L),
Arguments.of("2024-01-19T23:59:59Z UTC", 1705708799000L),

Arguments.of("2024-01-19T14:30:00+05:30 Asia/Kolkata", 1705654800000L),
Arguments.of("2024-01-19T21:45:00-05:00 America/Toronto", 1705718700000L),
Arguments.of("2024-01-19T16:00:00+02:00 Africa/Cairo", 1705672800000L),
Arguments.of("2024-01-19T07:30:00-07:00 America/Denver", 1705674600000L),
Arguments.of("2024-01-19T07:30GMT", 1705649400000L),
Arguments.of("2024-01-19T07:30[GMT]", 1705649400000L),
Arguments.of("2024-01-19T07:30 GMT", 1705649400000L),
Expand All @@ -810,6 +790,27 @@ private static Stream provideTimeZones()
Arguments.of("2024-01-19T07:30:01.1[GMT]", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12GMT", 1705649401120L),

Arguments.of("2024-01-19T07:30:01Z", 1705649401000L),
Arguments.of("2024-01-19T07:30:01.1Z", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12Z", 1705649401120L),
Arguments.of("2024-01-19T07:30:01UTC", 1705649401000L),
Arguments.of("2024-01-19T07:30:01.1UTC", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12UTC", 1705649401120L),
Arguments.of("2024-01-19T07:30:01[UTC]", 1705649401000L),
Arguments.of("2024-01-19T07:30:01.1[UTC]", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12[UTC]", 1705649401120L),
Arguments.of("2024-01-19T07:30:01 UTC", 1705649401000L),

Arguments.of("2024-01-19T07:30:01.1 UTC", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12 UTC", 1705649401120L),
Arguments.of("2024-01-19T07:30:01 [UTC]", 1705649401000L),
Arguments.of("2024-01-19T07:30:01.1 [UTC]", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12 [UTC]", 1705649401120L),
Arguments.of("2024-01-19T07:30:01.1 UTC", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12 UTC", 1705649401120L),
Arguments.of("2024-01-19T07:30:01.1 [UTC]", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.12 [UTC]", 1705649401120L),

Arguments.of("2024-01-19T07:30:01.12[GMT]", 1705649401120L),
Arguments.of("2024-01-19T07:30:01.12 GMT", 1705649401120L),
Arguments.of("2024-01-19T07:30:01.12 [GMT]", 1705649401120L),
Expand All @@ -822,34 +823,7 @@ private static Stream provideTimeZones()
Arguments.of("2024-01-19T07:30:01.1234 GMT", 1705649401123L),

Arguments.of("2024-01-19T07:30:01.1234 [GMT]", 1705649401123L),
Arguments.of("2024-01-19T07:30:01.123+0100GMT", 1705645801123L), // intentional redundancy on down because ISO 8601 allows it.
Arguments.of("2024-01-19T07:30:01.123+0100[GMT]", 1705645801123L),
Arguments.of("2024-01-19T07:30:01.123+0100 GMT", 1705645801123L),
Arguments.of("2024-01-19T07:30:01.123+0100 [GMT]", 1705645801123L),
Arguments.of("2024-01-19T07:30:01.123-1000GMT", 1705685401123L),
Arguments.of("2024-01-19T07:30:01.123-1000[GMT ]", 1705685401123L),
Arguments.of("2024-01-19T07:30:01.123-1000[ GMT ]", 1705685401123L),
Arguments.of("2024-01-19T07:30:01.123-1000 GMT", 1705685401123L),
Arguments.of("2024-01-19T07:30:01.123-1000 [GMT]", 1705685401123L),

Arguments.of("2024-01-19T07:30:01.123+2 GMT", 1705642201123L), // 18 is max, anything larger of smaller is a java.time exception
Arguments.of("2024-01-19T07:30:01.123+2 [GMT]", 1705642201123L),
Arguments.of("2024-01-19T07:30:01.123-2 GMT", 1705656601123L),
Arguments.of("2024-01-19T07:30:01.123-2 [GMT]", 1705656601123L),
Arguments.of("2024-01-19T07:30:01.123+2GMT", 1705642201123L),
Arguments.of("2024-01-19T07:30:01.123+2[GMT]", 1705642201123L),
Arguments.of("2024-01-19T07:30:01.123-2GMT", 1705656601123L),
Arguments.of("2024-01-19T07:30:01.123-2[GMT]", 1705656601123L),
Arguments.of("2024-01-19T07:30:01.123+18 GMT", 1705584601123L),
Arguments.of("2024-01-19T07:30:01.123+18 [GMT]", 1705584601123L),

Arguments.of("2024-01-19T07:30:01.123-18 GMT", 1705714201123L),
Arguments.of("2024-01-19T07:30:01.123-18 [GMT]", 1705714201123L),
Arguments.of("2024-01-19T07:30:01.123+18:00 GMT", 1705584601123L),
Arguments.of("2024-01-19T07:30:01.123+18:00 [GMT]", 1705584601123L),
Arguments.of("2024-01-19T07:30:01.123-18:00 GMT", 1705714201123L),
Arguments.of("2024-01-19T07:30:01.123-18:00 [GMT]", 1705714201123L),
Arguments.of("2024-01-19T07:30:00+10 EST", 1705613400000L),

Arguments.of("07:30EST 2024-01-19", 1705667400000L),
Arguments.of("07:30[EST] 2024-01-19", 1705667400000L),
Arguments.of("07:30 EST 2024-01-19", 1705667400000L),
Expand All @@ -860,21 +834,7 @@ private static Stream provideTimeZones()
Arguments.of("07:30:01 EST 2024-01-19", 1705667401000L),
Arguments.of("07:30:01 [EST] 2024-01-19", 1705667401000L),
Arguments.of("07:30:01.123 EST 2024-01-19", 1705667401123L),
Arguments.of("07:30:01.123 [EST] 2024-01-19", 1705667401123L),
Arguments.of("07:30:01.123+1100 EST 2024-01-19", 1705609801123L),
Arguments.of("07:30:01.123-1100 [EST] 2024-01-19", 1705689001123L),
Arguments.of("07:30:01.123+11:00 [EST] 2024-01-19", 1705609801123L),

Arguments.of("07:30:01.123-11:00 [EST] 2024-01-19", 1705689001123L),
Arguments.of("Wed 07:30:01.123-11:00 [EST] 2024-01-19", 1705689001123L),
Arguments.of("07:30:01.123-11:00 [EST] 2024-01-19 Wed", 1705689001123L),
Arguments.of("07:30:01.123-11:00 [EST] Sunday, January 21, 2024", 1705861801123L),
Arguments.of("07:30:01.123-11:00 [EST] Sunday January 21, 2024", 1705861801123L),
Arguments.of("07:30:01.123-11:00 [EST] January 21, 2024 Sunday", 1705861801123L),
Arguments.of("07:30:01.123-11:00 [EST] January 21, 2024, Sunday", 1705861801123L),
Arguments.of("07:30:01.123-11:00 [America/New_York] January 21, 2024, Sunday", 1705861801123L),
Arguments.of("07:30:01.123-11:00 [Africa/Cairo] 21 Jan 2024 Sun", 1705861801123L),
Arguments.of("07:30:01.123-11:00 [Africa/Cairo] 2024 Jan 21st Sat", 1705861801123L) // day of week should be ignored
Arguments.of("07:30:01.123 [EST] 2024-01-19", 1705667401123L)
);
}

Expand Down Expand Up @@ -906,4 +866,80 @@ void testTimeBetterThanMilliResolution()
assertEquals(ZoneId.of("GMT-0500"), zonedDateTime.getZone());
assertEquals(-60*60*5, zonedDateTime.getOffset().getTotalSeconds());
}

private static Stream provideBadFormats() {
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]"),
Arguments.of("2024-01-19T18:15:45+10:00[Australia/Sydney]"),
Arguments.of("2024-01-19T05:00:00-03:00[America/Sao_Paulo]"),
Arguments.of("2024-01-19T14:30:00+05:30[Asia/Kolkata]"),
Arguments.of("2024-01-19T21:45:00-05:00[America/Toronto]"),
Arguments.of("2024-01-19T16:00:00+02:00[Africa/Cairo]"),
Arguments.of("2024-01-19T07:30:00-07:00[America/Denver]"),
Arguments.of("2024-01-19T18:15:45+10:00 Australia/Sydney"),
Arguments.of("2024-01-19T05:00:00-03:00 America/Sao_Paulo"),
Arguments.of("2024-01-19T14:30:00+05:30 Asia/Kolkata"),
Arguments.of("2024-01-19T21:45:00-05:00 America/Toronto"),
Arguments.of("2024-01-19T16:00:00+02:00 Africa/Cairo"),
Arguments.of("2024-01-19T07:30:00-07:00 America/Denver"),
Arguments.of("2024-01-19T12:00:00-08:00 America/Los_Angeles"),
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"),
Arguments.of("2024-01-19T07:30:01.123+2 [GMT]"),
Arguments.of("2024-01-19T07:30:01.123-2 GMT"),
Arguments.of("2024-01-19T07:30:01.123-2 [GMT]"),
Arguments.of("2024-01-19T07:30:01.123+2GMT"),
Arguments.of("2024-01-19T07:30:01.123+2[GMT]"),
Arguments.of("2024-01-19T07:30:01.123-2GMT"),
Arguments.of("2024-01-19T07:30:01.123-2[GMT]"),
Arguments.of("2024-01-19T07:30:01.123+18 GMT"),
Arguments.of("2024-01-19T07:30:01.123+18 [GMT]"),
Arguments.of("2024-01-19T07:30:01.123-18 GMT"),
Arguments.of("2024-01-19T07:30:01.123-18 [GMT]"),
Arguments.of("2024-01-19T07:30:01.123+18:00 GMT"),
Arguments.of("2024-01-19T07:30:01.123+18:00 [GMT]"),
Arguments.of("2024-01-19T07:30:01.123-18:00 GMT"),
Arguments.of("2024-01-19T07:30:01.123-18:00 [GMT]"),
Arguments.of("2024-01-19T07:30:00+10 EST"),
Arguments.of("07:30:01.123+1100 EST 2024-01-19"),
Arguments.of("07:30:01.123-1100 [EST] 2024-01-19"),
Arguments.of("07:30:01.123+11:00 [EST] 2024-01-19"),
Arguments.of("07:30:01.123-11:00 [EST] 2024-01-19"),
Arguments.of("Wed 07:30:01.123-11:00 [EST] 2024-01-19"),
Arguments.of("07:30:01.123-11:00 [EST] 2024-01-19 Wed"),
Arguments.of("07:30:01.123-11:00 [EST] Sunday, January 21, 2024"),
Arguments.of("07:30:01.123-11:00 [EST] Sunday January 21, 2024"),
Arguments.of("07:30:01.123-11:00 [EST] January 21, 2024 Sunday"),
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")
);
}

@ParameterizedTest
@MethodSource("provideBadFormats")
void testFormatsThatShouldNotWork(String badFormat)
{
assertThatThrownBy(() -> DateUtilities.parseDate(badFormat, ZoneId.systemDefault(), true))
.isInstanceOf(java.lang.IllegalArgumentException.class)
.hasMessageContaining("Issue parsing date-time, other characters present:");
}
}

0 comments on commit 764be4f

Please sign in to comment.