如何解析 Java 8 中不包含标点符号的 ISO-8601 格式字符串?
How do I parse an ISO-8601 formatted string that contains no punctuation in Java 8?
如何将以下字符串解析为 LocalDateTime
-Object?
20200203092315000000
我总是得到以下异常,但我不明白:
java.time.format.DateTimeParseException: Text '20200203092315000000' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at de.x.struct.type.LocalDateTimeStructField.setBytesValue(LocalDateTimeStructField.java:44)
at de.x.struct.Struct.bytesToStruct(Struct.java:110)
at de.x.struct.StructTest.testStringToStruct(StructTest.java:60)
我的应用程序代码如下所示:
LocalDateTime ldt = LocalDateTime.parse("20200203092315000000", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSS"));
您的输入无效且超出范围。
当您输入正确时使用此代码。
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(your input * 1000);
System.out.println(cal.getTime());
看起来像是一个已知问题...
bug_id=JDK-8031085
bug_id=JDK-8138676
Workaround:
DateTimeFormatter dtf = new
DateTimeFormatterBuilder().appendPattern("yyyyMMddHHmmss").appendValue(ChronoField.MILLI_OF_SECOND,
3).toFormatter()
或
CUSTOMER SUBMITTED WORKAROUND : use the following format (mind the
'.'): "yyyyMMddHHmmss.SSS"
LocalDateTime.parse("20150910121314987",
DateTimeFormatter.ofPattern("yyyyMMddHHmmss.SSS"))
or alternatively use jodatime library
我已经使用以下代码将近三年了,它对我很有帮助。
ISO 8601 允许两种格式:基本格式和扩展格式。基本格式不包含任何标点符号,而扩展格式包含。例如,扩展格式的 2017-12-10T09:47:00-04:00 等同于基本格式的 20171210T094700-0400。
由于 Java 8 中的行为是仅接受扩展格式,因此我通常使用接受这两种格式的新 DateTimeFormatter。
此格式不能准确表示 ISO 8601 的要求。规则是时间戳的文本表示要么是完全基本的(即没有标点符号)要么是完全扩展的(即所有标点符号都存在)。仅使用一些标点符号是无效的,但我发现某些图书馆不遵守此规则。具体来说,Gson 生成时区说明符中缺少冒号的时间戳。因为我希望在我接受的东西上自由,所以我完全忽略所有标点符号。
Java 8 也无法准确处理年份,尤其是在缺少标点符号的情况下。 ISO 8601 要求年份中正好有四位数字,但如果双方就明确的数字位数达成一致,则允许额外的数字。在这种情况下,年份之前必须有一个符号。但是,Java 8 不强制要求有符号并接受四到九位数字。虽然这种方法更自由,并且与我通常尝试完成的目标保持一致,但由于我不知道应该出现多少位数字,因此无法解析一年。在这种情况下,我收紧了格式以符合 ISO 8601,尽管如果需要可以使用替代方法。例如,您可以预先解析文本,了解您期望的位数,并在位数过多时添加符号。
我建议只在解析时使用此格式化程序,而不是在序列化时使用,因为我更喜欢严格控制我生成的内容。
下面的代码应该接受您的格式,有或没有时区偏移。它只与您所拥有的有所不同,因为它接受九位小数秒而不是六位。您可以根据需要进行调整。
// somewhere above
import java.time.format.SignStyle;
import static java.time.temporal.ChronoField.*;
static DateTimeFormatter ISO_8601_LENIENT_FORMAT = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
.optionalStart().appendLiteral('-').optionalEnd() // Basic format has no punctuation
.appendValue(MONTH_OF_YEAR, 2)
.optionalStart().appendLiteral('-').optionalEnd()
.appendValue(DAY_OF_MONTH, 2)
.optionalStart().appendLiteral('T').optionalEnd() // permitted to omit the 'T' character by mutual agreement
.appendValue(HOUR_OF_DAY, 2)
.optionalStart().appendLiteral(':').optionalEnd()
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart() // seconds are optional
.optionalStart().appendLiteral(':').optionalEnd()
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart().appendFraction(NANO_OF_SECOND, 0, 9, false).optionalEnd()
.optionalEnd()
.optionalStart().appendOffset("+HH:MM", "Z").optionalEnd()
.optionalStart().appendOffset("+HHMM", "Z").optionalEnd()
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
.toFormatter()
;
更严格的版本拒绝所有标点符号,假设年份为非负数,将小数秒限制为六位数,并省略时区信息。
static DateTimeFormatter ISO_8601_NO_PUNCTUATION = new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 4, SignStyle.NEVER)
.appendValue(MONTH_OF_YEAR, 2)
.appendValue(DAY_OF_MONTH, 2)
.appendValue(HOUR_OF_DAY, 2)
.appendValue(MINUTE_OF_HOUR, 2)
.appendValue(SECOND_OF_MINUTE, 2)
.appendFraction(NANO_OF_SECOND, 6, 6, false)
.toFormatter()
;
如何将以下字符串解析为 LocalDateTime
-Object?
20200203092315000000
我总是得到以下异常,但我不明白:
java.time.format.DateTimeParseException: Text '20200203092315000000' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at de.x.struct.type.LocalDateTimeStructField.setBytesValue(LocalDateTimeStructField.java:44)
at de.x.struct.Struct.bytesToStruct(Struct.java:110)
at de.x.struct.StructTest.testStringToStruct(StructTest.java:60)
我的应用程序代码如下所示:
LocalDateTime ldt = LocalDateTime.parse("20200203092315000000", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSSSSS"));
您的输入无效且超出范围。
当您输入正确时使用此代码。
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(your input * 1000);
System.out.println(cal.getTime());
看起来像是一个已知问题...
bug_id=JDK-8031085 bug_id=JDK-8138676
Workaround:
DateTimeFormatter dtf = new DateTimeFormatterBuilder().appendPattern("yyyyMMddHHmmss").appendValue(ChronoField.MILLI_OF_SECOND, 3).toFormatter()
或
CUSTOMER SUBMITTED WORKAROUND : use the following format (mind the '.'): "yyyyMMddHHmmss.SSS"
LocalDateTime.parse("20150910121314987", DateTimeFormatter.ofPattern("yyyyMMddHHmmss.SSS"))
or alternatively use jodatime library
我已经使用以下代码将近三年了,它对我很有帮助。
ISO 8601 允许两种格式:基本格式和扩展格式。基本格式不包含任何标点符号,而扩展格式包含。例如,扩展格式的 2017-12-10T09:47:00-04:00 等同于基本格式的 20171210T094700-0400。
由于 Java 8 中的行为是仅接受扩展格式,因此我通常使用接受这两种格式的新 DateTimeFormatter。
此格式不能准确表示 ISO 8601 的要求。规则是时间戳的文本表示要么是完全基本的(即没有标点符号)要么是完全扩展的(即所有标点符号都存在)。仅使用一些标点符号是无效的,但我发现某些图书馆不遵守此规则。具体来说,Gson 生成时区说明符中缺少冒号的时间戳。因为我希望在我接受的东西上自由,所以我完全忽略所有标点符号。
Java 8 也无法准确处理年份,尤其是在缺少标点符号的情况下。 ISO 8601 要求年份中正好有四位数字,但如果双方就明确的数字位数达成一致,则允许额外的数字。在这种情况下,年份之前必须有一个符号。但是,Java 8 不强制要求有符号并接受四到九位数字。虽然这种方法更自由,并且与我通常尝试完成的目标保持一致,但由于我不知道应该出现多少位数字,因此无法解析一年。在这种情况下,我收紧了格式以符合 ISO 8601,尽管如果需要可以使用替代方法。例如,您可以预先解析文本,了解您期望的位数,并在位数过多时添加符号。
我建议只在解析时使用此格式化程序,而不是在序列化时使用,因为我更喜欢严格控制我生成的内容。
下面的代码应该接受您的格式,有或没有时区偏移。它只与您所拥有的有所不同,因为它接受九位小数秒而不是六位。您可以根据需要进行调整。
// somewhere above
import java.time.format.SignStyle;
import static java.time.temporal.ChronoField.*;
static DateTimeFormatter ISO_8601_LENIENT_FORMAT = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(YEAR, 4, 4, SignStyle.EXCEEDS_PAD)
.optionalStart().appendLiteral('-').optionalEnd() // Basic format has no punctuation
.appendValue(MONTH_OF_YEAR, 2)
.optionalStart().appendLiteral('-').optionalEnd()
.appendValue(DAY_OF_MONTH, 2)
.optionalStart().appendLiteral('T').optionalEnd() // permitted to omit the 'T' character by mutual agreement
.appendValue(HOUR_OF_DAY, 2)
.optionalStart().appendLiteral(':').optionalEnd()
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart() // seconds are optional
.optionalStart().appendLiteral(':').optionalEnd()
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart().appendFraction(NANO_OF_SECOND, 0, 9, false).optionalEnd()
.optionalEnd()
.optionalStart().appendOffset("+HH:MM", "Z").optionalEnd()
.optionalStart().appendOffset("+HHMM", "Z").optionalEnd()
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
.toFormatter()
;
更严格的版本拒绝所有标点符号,假设年份为非负数,将小数秒限制为六位数,并省略时区信息。
static DateTimeFormatter ISO_8601_NO_PUNCTUATION = new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 4, SignStyle.NEVER)
.appendValue(MONTH_OF_YEAR, 2)
.appendValue(DAY_OF_MONTH, 2)
.appendValue(HOUR_OF_DAY, 2)
.appendValue(MINUTE_OF_HOUR, 2)
.appendValue(SECOND_OF_MINUTE, 2)
.appendFraction(NANO_OF_SECOND, 6, 6, false)
.toFormatter()
;