One way to parse all those formats is to write a regex, then create appropriate Temporal
object from the parsed values.
private static Temporal parse(String text) {
String regex = "(?:" +
"(\\d{9,})" + // 1: millis
"|" +
"(\\d{4})" + // 2: year
"(?:" +
"-?(\\d{3})" + // 3: day-of-year
"|" +
"(-?)W(\\d{2})" + // 5: week-of-year
"(?:\\4(\\d))?" + // 6: day-of-week (optional)
"|" +
"(-?)(\\d{2})" + // 8: month-of-year
"\\7(\\d{2})" + // 9: day-of-month
")" +
"(?:T(\\d{2})" + // 10: hour (optional)
"(?:(:?)(\\d{2})" + // 12: minute (optional)
"(?:\\11(\\d{2})" + // 13: second (optional)
"(?:\\.(\\d{1,9}))?" + // 14: fractional (optional)
")?" +
")?" +
"(?:" +
"(Z)" + // 15: Zulu
"|" +
"([+-]\\d{2})" + // 16: Offset hour (signed)
":?(\\d{2})" + // 17: Offset minute
")?" +
")?" +
")";
Matcher m = Pattern.compile(regex).matcher(text);
if (! m.matches())
throw new DateTimeParseException("Invalid date string", text, 0);
// Handle millis
if (m.start(1) != -1)
return Instant.ofEpochMilli(Long.parseLong(m.group(1)));
// Parse local date
LocalDate localDate;
if (m.start(3) != -1)
localDate = LocalDate.ofYearDay(Integer.parseInt(m.group(2)), Integer.parseInt(m.group(3)));
else if (m.start(5) != -1)
localDate = LocalDate.parse(m.group(2) + "-W" + m.group(5) + "-" + (m.start(6) == -1 ? "1" : m.group(6)),
DateTimeFormatter.ISO_WEEK_DATE);
else
localDate = LocalDate.of(Integer.parseInt(m.group(2)), Integer.parseInt(m.group(8)), Integer.parseInt(m.group(9)));
if (m.start(10) == -1)
return localDate;
// Parse local time
int hour = Integer.parseInt(m.group(10));
int minute = (m.start(12) == -1 ? 0 : Integer.parseInt(m.group(12)));
int second = (m.start(13) == -1 ? 0 : Integer.parseInt(m.group(13)));
int nano = (m.start(14) == -1 ? 0 : Integer.parseInt((m.group(14) + "00000000").substring(0, 9)));
LocalTime localTime = LocalTime.of(hour, minute, second, nano);
// Return date/time
if (m.start(15) != -1)
return ZonedDateTime.of(localDate, localTime, ZoneOffset.UTC);
if (m.start(16) == -1)
return LocalDateTime.of(localDate, localTime);
ZoneOffset zone = ZoneOffset.ofHoursMinutes(Integer.parseInt(m.group(16)), Integer.parseInt(m.group(17)));
return ZonedDateTime.of(localDate, localTime, zone);
}
Test
public static void main(String[] args) {
test("1534251817666");
test("2017-01-01");
test("2017-01-01T00");
test("2017-01-01T00:03");
test("2017-01-01T00:03:00.5"); // modified
test("2017-01-01T03:03:00+00:00");
test("2017-01-01T03:03:00-05:00");
test("2017-01-01T03:03:00+0500");
test("2017-01-01T03:03:00Z");
test("20170101T030300Z");
test("2017-W01-1");
test("2017W011");
test("2017-001");
test("2017001");
}
private static void test(String text) {
Temporal parsed = parse(text);
System.out.printf("%-25s -> %-25s %s%n", text, parsed, parsed.getClass().getSimpleName());
}
Output
1534251817666 -> 2018-08-14T13:03:37.666Z Instant
2017-01-01 -> 2017-01-01 LocalDate
2017-01-01T00 -> 2017-01-01T00:00 LocalDateTime
2017-01-01T00:03 -> 2017-01-01T00:03 LocalDateTime
2017-01-01T00:03:00.5 -> 2017-01-01T00:03:00.500 LocalDateTime
2017-01-01T03:03:00+00:00 -> 2017-01-01T03:03Z ZonedDateTime
2017-01-01T03:03:00-05:00 -> 2017-01-01T03:03-05:00 ZonedDateTime
2017-01-01T03:03:00+0500 -> 2017-01-01T03:03+05:00 ZonedDateTime
2017-01-01T03:03:00Z -> 2017-01-01T03:03Z ZonedDateTime
20170101T030300Z -> 2017-01-01T03:03Z ZonedDateTime
2017-W01-1 -> 2017-01-02 LocalDate
2017W011 -> 2017-01-02 LocalDate
2017-001 -> 2017-01-01 LocalDate
2017001 -> 2017-01-01 LocalDate
You can of course choose to always return a ZonedDateTime
, using JVM default time zone when zone is not given, replacing statements as follows:
private static Temporal parse(String text) {
private static ZonedDateTime parse(String text) {
return Instant.ofEpochMilli(Long.parseLong(m.group(1)));
return Instant.ofEpochMilli(Long.parseLong(m.group(1))).atZone(ZoneOffset.UTC);
return localDate;
return ZonedDateTime.of(localDate, LocalTime.MIDNIGHT, ZoneId.systemDefault());
return LocalDateTime.of(localDate, localTime);
return ZonedDateTime.of(localDate, localTime, ZoneId.systemDefault());
Test
private static void test(String text) {
System.out.printf("%-25s -> %s%n", text, parse(text));
}
Output
1534251817666 -> 2018-08-14T13:03:37.666Z
2017-01-01 -> 2017-01-01T00:00-05:00[America/New_York]
2017-01-01T00 -> 2017-01-01T00:00-05:00[America/New_York]
2017-01-01T00:03 -> 2017-01-01T00:03-05:00[America/New_York]
2017-01-01T00:03:00.5 -> 2017-01-01T00:03:00.500-05:00[America/New_York]
2017-01-01T03:03:00+00:00 -> 2017-01-01T03:03Z
2017-01-01T03:03:00-05:00 -> 2017-01-01T03:03-05:00
2017-01-01T03:03:00+0500 -> 2017-01-01T03:03+05:00
2017-01-01T03:03:00Z -> 2017-01-01T03:03Z
20170101T030300Z -> 2017-01-01T03:03Z
2017-W01-1 -> 2017-01-02T00:00-05:00[America/New_York]
2017W011 -> 2017-01-02T00:00-05:00[America/New_York]
2017-001 -> 2017-01-01T00:00-05:00[America/New_York]
2017001 -> 2017-01-01T00:00-05:00[America/New_York]