如果删除了 space,DateTimeFormatter 解析带有可选时间部分的字符串将失败
DateTimeFormatter parsing string with optional time part fails if space removed
在 之后,尝试使用模式 yyyyMMdd[HHmmss] 解析字符串 20120301122133 时出现错误。奇怪的是,使用模式 yyyyMMdd[ HHmmss] 解析 20120301 122133 效果很好。
所以这段代码工作正常
LocalDateTime.parse(
"19940513 230000",
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd[ HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
)
而这个失败了
LocalDateTime.parse(
"19940513230000",
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd[HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
)
我应该如何解析格式为 yyyyMMdd[HHmmss] 的字符串,即使用 java 8 时间 API 的格式为 yyyyMMddHHmmss 的可选时间部分?
解析模式是一个可配置选项,因此仅在运行时已知。所以我不能用硬编码的 DateTimeFormatterBuilder 调用替换字符串模式。
问题在于模式表达式"yyyy"并不表示固定的four-digit-year而是至少4位(或更多,所以解析器是贪婪的)。但您可以执行以下操作:
LocalDateTime ldt =
LocalDateTime.parse(
"19940513230000",
new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendPattern("MMdd[HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter());
System.out.println(ldt); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse(
"19940513230000",
new DateTimeFormatterBuilder()
.appendPattern("[uuuuMMddHHmmss][uuuuMMdd]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
));
这会打印:
1994-05-13T23:00
如果我尝试解析仅包含日期 "19940513"
的字符串,我会得到
1994-05-13T00:00
它也适用于 yyyy
而不是 uuuu
。假设你所有的年份都在这个时代(第 1 年或更晚),你使用哪个时代没有任何区别。通常 uuuu
也会接受负数年份,0 表示公元前 1 年,-1 表示公元前 2 年,依此类推。
出现这种情况是因为年份没有固定值,通常限制为 19 位数字。
如果您创建以下格式化程序(不需要分钟和秒)并使用 toString()
方法:
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd[ HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter()
.toString();
您可以看到以下内容:
"Value(YearOfEra,4,19,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)[' 'Value(HourOfDay,2)Value(MinuteOfHour,2)Value(SecondOfMinute,2)]java.time.format.DateTimeFormatterBuilder$DefaultValueParser@32eff876"
在这里你可以看到 YearOfEra
的最小宽度为 4,最大宽度为 19。
您可以使用 Meno 或 Ole 的答案之一。
然而,如果您需要接收格式和日期作为参数,并且希望能够以更简单的方式指定日期格式(例如 yyyyMMdd[HHmmSS]
而不是 [...][...]
),您可以预处理其中一个值(日期格式)。
您可以创建 'dinamically' 格式化程序,因此每个 yyyy
仅被解释为 4 位数年份。
自定义格式生成器可能类似于(可以改进):
public static DateTimeFormatter createFixed4DigitYearFormatter(String format) {
DateTimeFormatterBuilder formatBuilder = new DateTimeFormatterBuilder();
Arrays.stream(format.split("yyyy", -1))
.flatMap(cur -> Stream.of("yyyy", cur)).skip(1)
.filter(str -> !str.isEmpty())
.forEach(pattern -> {
if ("yyyy".equals(pattern)) formatBuilder
.appendValue(ChronoField.YEAR_OF_ERA, 4);
else formatBuilder.appendPattern(pattern);
});
return formatBuilder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).toFormatter();
}
此格式化程序按字符串 "yyyy"
拆分格式,然后将每个非 "yyyy"
添加为模式(使用 appendPattern(..)
)并将 "yyyy"
添加为YEAR_OF_ERA
类型的值,具有固定的 4 位数字(appendValue(..)
)。
终于可以使用多种格式的格式化程序了:
System.out.println(LocalDateTime.parse("19940513230000",
createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse("19940513",
createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T00:00
System.out.println(LocalDateTime.parse("1994-05-13 23:00:00",
createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse("1994-05-13",
createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T00:00
在
所以这段代码工作正常
LocalDateTime.parse(
"19940513 230000",
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd[ HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
)
而这个失败了
LocalDateTime.parse(
"19940513230000",
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd[HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
)
我应该如何解析格式为 yyyyMMdd[HHmmss] 的字符串,即使用 java 8 时间 API 的格式为 yyyyMMddHHmmss 的可选时间部分?
解析模式是一个可配置选项,因此仅在运行时已知。所以我不能用硬编码的 DateTimeFormatterBuilder 调用替换字符串模式。
问题在于模式表达式"yyyy"并不表示固定的four-digit-year而是至少4位(或更多,所以解析器是贪婪的)。但您可以执行以下操作:
LocalDateTime ldt =
LocalDateTime.parse(
"19940513230000",
new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendPattern("MMdd[HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter());
System.out.println(ldt); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse(
"19940513230000",
new DateTimeFormatterBuilder()
.appendPattern("[uuuuMMddHHmmss][uuuuMMdd]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter()
));
这会打印:
1994-05-13T23:00
如果我尝试解析仅包含日期 "19940513"
的字符串,我会得到
1994-05-13T00:00
它也适用于 yyyy
而不是 uuuu
。假设你所有的年份都在这个时代(第 1 年或更晚),你使用哪个时代没有任何区别。通常 uuuu
也会接受负数年份,0 表示公元前 1 年,-1 表示公元前 2 年,依此类推。
出现这种情况是因为年份没有固定值,通常限制为 19 位数字。
如果您创建以下格式化程序(不需要分钟和秒)并使用 toString()
方法:
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMdd[ HHmmss]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter()
.toString();
您可以看到以下内容:
"Value(YearOfEra,4,19,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)[' 'Value(HourOfDay,2)Value(MinuteOfHour,2)Value(SecondOfMinute,2)]java.time.format.DateTimeFormatterBuilder$DefaultValueParser@32eff876"
在这里你可以看到 YearOfEra
的最小宽度为 4,最大宽度为 19。
您可以使用 Meno 或 Ole 的答案之一。
然而,如果您需要接收格式和日期作为参数,并且希望能够以更简单的方式指定日期格式(例如 yyyyMMdd[HHmmSS]
而不是 [...][...]
),您可以预处理其中一个值(日期格式)。
您可以创建 'dinamically' 格式化程序,因此每个 yyyy
仅被解释为 4 位数年份。
自定义格式生成器可能类似于(可以改进):
public static DateTimeFormatter createFixed4DigitYearFormatter(String format) {
DateTimeFormatterBuilder formatBuilder = new DateTimeFormatterBuilder();
Arrays.stream(format.split("yyyy", -1))
.flatMap(cur -> Stream.of("yyyy", cur)).skip(1)
.filter(str -> !str.isEmpty())
.forEach(pattern -> {
if ("yyyy".equals(pattern)) formatBuilder
.appendValue(ChronoField.YEAR_OF_ERA, 4);
else formatBuilder.appendPattern(pattern);
});
return formatBuilder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).toFormatter();
}
此格式化程序按字符串 "yyyy"
拆分格式,然后将每个非 "yyyy"
添加为模式(使用 appendPattern(..)
)并将 "yyyy"
添加为YEAR_OF_ERA
类型的值,具有固定的 4 位数字(appendValue(..)
)。
终于可以使用多种格式的格式化程序了:
System.out.println(LocalDateTime.parse("19940513230000",
createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse("19940513",
createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T00:00
System.out.println(LocalDateTime.parse("1994-05-13 23:00:00",
createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T23:00
System.out.println(LocalDateTime.parse("1994-05-13",
createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T00:00