在Java中将字符串解析为Date的最佳方法是什么?该Date可以采用任何有效的ISO 8601格式或Unix纪元毫秒?例如,它需要能够解析以下内容(所有这些都是有效的ISO 8601或Unix纪元毫秒):
我发现以下代码可以解决大多数情况,但不能解决所有情况,因为提供的java.time中没有DateTimeFormatters可以处理所有ISO 8601情况:
try {
return Date.from(Instant.ofEpochMilli(Long.parseLong(time)));
} catch (NumberFormatException e) {
return Date.from(Instant.parse(time));
}
答案 0 :(得分:0)
解析所有这些格式的一种方法是编写一个正则表达式,然后从解析的值中创建适当的Temporal
对象。
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);
}
测试
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());
}
输出
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
您当然可以选择始终返回ZonedDateTime
,在未指定区域的情况下使用JVM默认时区替换语句,如下所示:
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());
测试
private static void test(String text) {
System.out.printf("%-25s -> %s%n", text, parse(text));
}
输出
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]
答案 1 :(得分:-2)
我有类似的任务。我编写了一个处理此问题的实用程序。不幸的是,我没有该实用程序本身,但是写了一篇文章,描述了该解决方案的想法。这是文章的链接:Java 8 java.time package: parsing any string to date。尽管有标题,但该想法也可以在早于8的版本中实现。基本上,这个想法是将所有可能的格式放在配置文件中,并尝试一个一个地解析String直到成功。格式的顺序很重要,因为有时可以用不同的格式成功解析字符串并导致不同的Date值。因此,首先放置更重要的格式。阅读文章了解详情