如何解析任何ISO 8601格式的Java字符串?
在Java中,将字符串解析为任何有效ISO 8601格式或Unix格式的日期的最佳方法是什么?例如,它需要能够解析以下内容(所有内容都是有效的ISO 8601或Unix):如何解析任何ISO 8601格式的Java字符串?,java,date,datetime,Java,Date,Datetime,在Java中,将字符串解析为任何有效ISO 8601格式或Unix格式的日期的最佳方法是什么?例如,它需要能够解析以下内容(所有内容都是有效的ISO 8601或Unix): 1534251817666 2017-01-01 2017-01-01T00 2017-01-01T00:03 2017-01-01T00:03,5 2017-01-01T00:03.5 2017-01-01T03:03:00+00:00 2017-01-01T03:03:00-05:00 2017-01-01T03:03
- 1534251817666
- 2017-01-01
- 2017-01-01T00
- 2017-01-01T00:03
- 2017-01-01T00:03,5
- 2017-01-01T00:03.5
- 2017-01-01T03:03:00+00:00
- 2017-01-01T03:03:00-05:00
- 2017-01-01T03:03:00+0500
- 2017-01-01T03:03:00Z
- 20170101T030300Z
- 2017-W01-1
- 2017W011
- 2017-001
- 2017001
try {
return Date.from(Instant.ofEpochMilli(Long.parseLong(time)));
} catch (NumberFormatException e) {
return Date.from(Instant.parse(time));
}
解析所有这些格式的一种方法是编写一个正则表达式,然后根据解析的值创建适当的
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);
}
试验
输出
1534251817666->2018-08-14T13:03:37.666Z瞬间
2017-01-01->2017-01-01本地日期
2017-01-01T00->2017-01-01T00:00本地日期时间
2017-01-01T00:03->2017-01-01T00:03本地日期时间
2017-01-01T00:03:00.5->2017-01-01T00:03:00.500本地日期时间
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-02本地日期
2017W011->2017-01-02本地日期
2017-001->2017-01-01本地日期
2017001->2017-01-01本地日期
当然,您可以选择始终返回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());
试验
输出
1534251817666->2018-08-14T13:03:37.666Z
2017-01-01->2017-01-01T00:00-05:00[美国/纽约]
2017-01-01T00->2017-01-01T00:00-05:00[美国/纽约]
2017-01-01T00:03->2017-01-01T00:03-05:00[美国/纽约]
2017-01-01T00:03:00.5->2017-01-01T00:03:00.500-05:00[美国/纽约]
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[美国/纽约]
2017W011->2017-01-02T00:00-05:00[美国/纽约]
2017-001->2017-01-01T00:00-05:00[美国/纽约]
2017001->2017-01-01T00:00-05:00[美国/纽约]
我也有类似的任务。我写了一个实用程序来处理这个问题。不幸的是,我没有这个工具本身,但我写了一篇文章描述了解决方案的想法。以下是文章的链接:。尽管有标题,这个想法也可以在8之前的版本中实现。基本上,这个想法是将所有可能的格式放在一个配置文件中,并尝试逐个解析字符串,直到成功为止。格式的顺序很重要,因为有时一个字符串可以被不同的格式成功解析并产生不同的日期值。因此,将更重要的格式放在首位。阅读文章以了解详细信息找出哪些情况它无法解决,并使用字符串理解将其更改为它可以解决的情况,然后再将其提交给try/catch。这难道不认为2017001是一个大问题吗?因此,必须对长值进行范围检查。格式化程序很容易制作。@OleV.V。尽管SimpleDateFormat属于旧时的类,但它确实是如此,@JoopEggen,我当然应该警告它。然而,链接的问题也有两个java.time答案,和。我建议研究这些。@Andreas根据“一个十进制标记,逗号或点(没有任何偏好,如2003年第22届大会CGPM决议10所述,[24],但根据ISO 8601:2004,偏好逗号)[25]用作时间元素及其分数之间的分隔符。表示“14小时30分半”…将其表示为“14:30,5”、“1430,5”、“14:30.5”或“1430.5”。“我想知道为什么所有的反对票都没有解释。这是在运行函数代码来满足问题的要求。我不确定,@Andreas,但我接受了它,因为它对我有效。”。
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));
}