Skip to content

Commit

Permalink
Updated tests that had incorrect GMT testing dates (copy-paste bug). …
Browse files Browse the repository at this point in the history
…Added 2nd parse() API that returns a ZonedDateTime. The 2nd option allows the caller to specify a default timeZone (ZoneId) to use if no timezone is specified in the date-time. The 2nd option also has a flag that allows the caller to specify if they want the date to be isolated/alone or is allowed to exist anywhere within the String.
  • Loading branch information
jdereg committed Jan 23, 2024
1 parent 412de58 commit e8f9a3a
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 33 deletions.
26 changes: 12 additions & 14 deletions src/main/java/com/cedarsoftware/util/DateUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public final class DateUtilities {
private static final String tz_Hh_MM = "[+-]\\d{1,2}:\\d{2}";
private static final String tz_HHMM = "[+-]\\d{4}";
private static final String tz_Hh = "[+-]\\d{1,2}";
private static final String tzNamed = ws + "\\[?[A-Za-z][A-Za-z0-9~/._+-]+]?";
private static final String tzNamed = wsOp + "\\[?[A-Za-z][A-Za-z0-9~\\/._+-]+]?";

// Patterns defined in BNF-style using above named elements
private static final Pattern isoDatePattern = Pattern.compile( // Regex's using | (OR)
Expand Down Expand Up @@ -153,7 +153,8 @@ private DateUtilities() {
}

/**
* Main API. Retrieve date-time from passed in String.
* 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.
* @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
Expand All @@ -163,20 +164,21 @@ public static Date parseDate(String dateStr) {
if (StringUtilities.isEmpty(dateStr)) {
return null;
}
ZonedDateTime zonedDateTime = parseDate(dateStr, false);
ZonedDateTime zonedDateTime = parseDate(dateStr, ZoneId.systemDefault(), false);
return new Date(zonedDateTime.toInstant().toEpochMilli());
}

/**
* Main API. Retrieve date-time from passed in String. The boolean enSureSoloDate, if set true, ensures that
* no other non-date content existed in the String. That requires additional time to verify.
* @param dateStr String containing a date. See DateUtilities class Javadoc for all the supported formats.
* @param ensureSoloDate If true, if there is excess non-Date content, it will throw an IllegalArgument exception.
* @param defaultZoneId ZoneId to use if no timezone or timezone offset is given.
* @param ensureDateTimeAlone If true, if there is excess non-Date content, it will throw an IllegalArgument exception.
* @return ZonedDateTime instance converted from the passed in date String. See comments at top of class for supported
* formats. This API is intended to be super flexible in terms of what it can parse.
*/
public static ZonedDateTime parseDate(String dateStr, boolean ensureSoloDate) {
Convention.throwIfNullOrEmpty(dateStr, "dateString must not be null or empty String.");
public static ZonedDateTime parseDate(String dateStr, ZoneId defaultZoneId, boolean ensureDateTimeAlone) {
Convention.throwIfNullOrEmpty(dateStr, "'dateStr' must not be null or empty String.");
dateStr = dateStr.trim();

if (allDigits.matcher(dateStr).matches()) {
Expand Down Expand Up @@ -255,23 +257,19 @@ public static ZonedDateTime parseDate(String dateStr, boolean ensureSoloDate) {
if (matcher.group(5) != null) {
tz = stripBrackets(matcher.group(5).trim());
}
} else {
noTime = true; // indicates no "time" portion
}

if (ensureSoloDate) {
if (ensureDateTimeAlone) {
verifyNoGarbageLeft(remnant);
}

// Set Timezone into Calendar
ZoneId zoneId = getTimeZone(tz);
ZonedDateTime zonedDateTime = getDate(dateStr, zoneId, noTime, year, month, day, hour, min, sec, milli);
ZoneId zoneId = StringUtilities.isEmpty(tz) ? defaultZoneId : getTimeZone(tz);
ZonedDateTime zonedDateTime = getDate(dateStr, zoneId, year, month, day, hour, min, sec, milli);
return zonedDateTime;
}

private static ZonedDateTime getDate(String dateStr,
ZoneId zoneId,
boolean noTime,
String year,
int month,
String day,
Expand All @@ -290,7 +288,7 @@ private static ZonedDateTime getDate(String dateStr,
throw new IllegalArgumentException("Day must be between 1 and 31 inclusive, date: " + dateStr);
}

if (noTime) { // no [valid] time portion
if (hour == null) { // no [valid] time portion
return ZonedDateTime.of(y, month, d, 0, 0, 0, 0, zoneId);
} else {
// Regex prevents these from ever failing to parse.
Expand Down
42 changes: 23 additions & 19 deletions src/test/java/com/cedarsoftware/util/TestDateUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
Expand Down Expand Up @@ -347,12 +348,12 @@ void testDayOfWeek()
DateUtilities.parseDate(" Dec 25, 2014, thursday ");
}
try {
ZonedDateTime date = DateUtilities.parseDate("text Dec 25, 2014", true);
ZonedDateTime date = DateUtilities.parseDate("text Dec 25, 2014", ZoneId.systemDefault(), true);
fail();
} catch (Exception ignored) { }

try {
DateUtilities.parseDate("Dec 25, 2014 text", true);
DateUtilities.parseDate("Dec 25, 2014 text", ZoneId.systemDefault(), true);
fail();
} catch (Exception ignored) { }
}
Expand Down Expand Up @@ -716,11 +717,11 @@ void testInconsistentDateSeparators()
@Test
void testBadTimeSeparators()
{
assertThatThrownBy(() -> DateUtilities.parseDate("12/24/1996 12.49.58", true))
assertThatThrownBy(() -> DateUtilities.parseDate("12/24/1996 12.49.58", ZoneId.systemDefault(), true))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Issue parsing date-time, other characters present: 12.49.58");

assertThatThrownBy(() -> DateUtilities.parseDate("12.49.58 12/24/1996", true))
assertThatThrownBy(() -> DateUtilities.parseDate("12.49.58 12/24/1996", ZoneId.systemDefault(), true))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Issue parsing date-time, other characters present: 12.49.58");

Expand All @@ -730,7 +731,7 @@ void testBadTimeSeparators()
calendar.set(1996, Calendar.DECEMBER, 24, 12, 49, 58);
assertEquals(calendar.getTime(), date);

assertThatThrownBy(() -> DateUtilities.parseDate("12/24/1996 12-49-58", true))
assertThatThrownBy(() -> DateUtilities.parseDate("12/24/1996 12-49-58", ZoneId.systemDefault(), true))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Issue parsing date-time, other characters present: 12-49-58");
}
Expand Down Expand Up @@ -765,8 +766,8 @@ void testEpochMillis()
private static Stream provideTimeZones()
{
return Stream.of(
Arguments.of("2024-01-19T15:30:45[Europe/London]", 1705696245000L),
Arguments.of("2024-01-19T10:15:30[Asia/Tokyo]", 1705677330000L),
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),
Expand All @@ -791,8 +792,8 @@ private static Stream provideTimeZones()
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", 1705667400000L),
Arguments.of("2024-01-19T07:30[GMT]", 1705667400000L),
Arguments.of("2024-01-19T07:30GMT", 1705649400000L),
Arguments.of("2024-01-19T07:30[GMT]", 1705649400000L),
Arguments.of("2024-01-19T07:30 GMT", 1705649400000L),
Arguments.of("2024-01-19T07:30 [GMT]", 1705649400000L),
Arguments.of("2024-01-19T07:30 GMT", 1705649400000L),
Expand All @@ -801,23 +802,23 @@ private static Stream provideTimeZones()
Arguments.of("2024-01-19T07:30 GMT ", 1705649400000L),
Arguments.of("2024-01-19T07:30:01 GMT", 1705649401000L),
Arguments.of("2024-01-19T07:30:01 [GMT]", 1705649401000L),
Arguments.of("2024-01-19T07:30:01GMT", 1705667401000L),
Arguments.of("2024-01-19T07:30:01[GMT]", 1705667401000L),
Arguments.of("2024-01-19T07:30:01GMT", 1705649401000L),
Arguments.of("2024-01-19T07:30:01[GMT]", 1705649401000L),
Arguments.of("2024-01-19T07:30:01.1 GMT", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.1 [GMT]", 1705649401100L),
Arguments.of("2024-01-19T07:30:01.1GMT", 1705667401100L),
Arguments.of("2024-01-19T07:30:01.1[GMT]", 1705667401100L),
Arguments.of("2024-01-19T07:30:01.12GMT", 1705667401120L),
Arguments.of("2024-01-19T07:30:01.1GMT", 1705649401100L),
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:01.12[GMT]", 1705667401120L),
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),
Arguments.of("2024-01-19T07:30:01.123GMT", 1705667401123L),
Arguments.of("2024-01-19T07:30:01.123[GMT]", 1705667401123L),
Arguments.of("2024-01-19T07:30:01.123GMT", 1705649401123L),
Arguments.of("2024-01-19T07:30:01.123[GMT]", 1705649401123L),
Arguments.of("2024-01-19T07:30:01.123 GMT", 1705649401123L),
Arguments.of("2024-01-19T07:30:01.123 [GMT]", 1705649401123L),
Arguments.of("2024-01-19T07:30:01.1234GMT", 1705667401123L),
Arguments.of("2024-01-19T07:30:01.1234[GMT]", 1705667401123L),
Arguments.of("2024-01-19T07:30:01.1234GMT", 1705649401123L),
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.1234 [GMT]", 1705649401123L),
Expand Down Expand Up @@ -884,6 +885,9 @@ void testTimeZoneParsing(String exampleZone, Long epochMilli)
for (int i=0; i < 1; i++) {
Date date = DateUtilities.parseDate(exampleZone);
assertEquals(date.getTime(), epochMilli);

ZonedDateTime date2 = DateUtilities.parseDate(exampleZone, ZoneId.systemDefault(), true);
assertEquals(date2.toInstant().toEpochMilli(), epochMilli);
}
}
}

0 comments on commit e8f9a3a

Please sign in to comment.