Java8 时间格式化 DateTimeFormatter

本文最后更新于:2020年12月30日 凌晨

场景

吾辈在使用 Java8 的 LocalDateTime 想要根据某种格式格式化字符串为日期时间,本以来会简单的事情,事实上却出乎预料!

问题

想要格式化一个字符串为日期时间。例如常见的 yyyy-MM-dd hh:mm:ss 格式的 2017-12-11 10:11:05,吾辈习惯性的写出以下代码

1
2
3
4
final String text = "2017-12-11 10:11:05";
final String pattern = "yyyy-MM-dd hh:mm:ss";
final LocalDateTime dateTime = LocalDateTime.parse(text, DateTimeFormatter.ofPattern(pattern));
System.out.println(dateTime);

谜之音:JVM 不想理你,并抛给了你一个 Error

1
java.time.format.DateTimeParseException: Text '2017-12-11 10:11:05' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MinuteOfHour=11, MilliOfSecond=0, MicroOfSecond=0, SecondOfMinute=5, HourOfAmPm=10, NanoOfSecond=0},ISO resolved to 2017-12-11 of type java.time.format.Parsed

大意便是无法解析,去 Google 了一下,在 StackOverflow 上发现了这个问题:DateTimeParseException: Text could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor

里面的答案说是使用 HH(每小时)而非 hh(每小时上午时钟),所以吾辈修改了代码,变成了下面这样

1
2
3
4
5
final String text = "2017-12-11 10:11:05";
// 只改了这里的格式
final String pattern = "yyyy-MM-dd HH:mm:ss";
final LocalDateTime dateTime = LocalDateTime.parse(text, DateTimeFormatter.ofPattern(pattern));
System.out.println(dateTime);

然而确实能正常解析了!??**#黑人问号**

根源

去看了一下 Java8 的 DateTimeFormatter 日期时间格式化类,发现了 class 上一段有趣的注释

All letters ‘A’ to ‘Z’ and ‘a’ to ‘z’ are reserved as pattern letters. The
following pattern letters are defined:

Symbol Meaning Presentation Examples
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset ‘Z’ for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal ‘
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use

是的,这是一个日期格式的说明,在这里确实可以看到 HH 才代表的是 24 小时,而 hh 则是将小时分为 am/pm(上午/下午)。

附:这里吐槽一下,Java 的格式化规则居然和标准的有偏差,uuuu 也能当作更好的 yyyy 使用(主要针对负的年份)

所以现在也只能将格式化时间的 pattern 修改为 uuuu-MM-dd HH:mm:ss 便能正常匹配了!

解决

那么,既然知道日期时间格式化模式的规则,那么接下来就可以直接写模式字符串了

  • 日期:uuuu-MM-dd
  • 时间:HH:mm:ss
  • 常见的日期时间:uuuu-MM-dd HH:mm:ss
  • 标准的日期时间:DateTimeFormatter.ISO_DATE_TIME(Java8 time 库中已存在)

将字符串转换为日期时间大致有两种方式

1
2
3
4
5
final String text = "2019-02-12T01:24:07.425Z";
// 先解析为时间在转换为具体的日期时间类
final LocalDateTime from = LocalDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(text));
// 直接根据指定的格式解析字符串为具体的日期时间类
final LocalDateTime parse = LocalDateTime.parse(text, DateTimeFormatter.ISO_DATE_TIME);

那么,Java8 的踩坑之路还在继续,不知还有多少人在用 Java7- 呢?\(@ ̄ ∇  ̄@)/


Java8 时间格式化 DateTimeFormatter
https://blog.rxliuli.com/p/4550b527cf7347e4b63ec081299ec120/
作者
rxliuli
发布于
2020年4月18日
许可协议