1.8 以前 Java 关于日期的类是 Date
与 Calendar
, 与之对应的格式化的类是 SimpleDateFormat
。 上一篇介绍了在1.8 中提供的 java.time
包, Java 提供了更加丰富的时间类型来供我们使用,与之对应的格式化类型也需要使用新提供的 DateTimeFormatter
。
很多文章写过 SimpleDateFormat
类,每当提到它,都会让开发人员注意这个类并不是线程安全的,也就是每次使用都必须新生成,而不能当作全局静态变量来使用。
就像 Date
是可变类型是一个设计失误一样,SimpleDateFormat
设计为非线程安全也是一个设计失误,这样的设计失误却需要由程序员来承担。DateTimeFormatter
纠正了这个设计失误,变为了线程安全的。
DateTimeFormatter
在职责分离上也更清晰,由于 Date
没有时区 的信息,所以在格式化时这个任务就只能由 SimpleDateFormat
承担,导致很难理解 SimpleDateFormat
如何处理时区。 新增加的 ZonedDateTime
等时间类增加了时区及与时区无关的 LocalDateTime
类,DateTimeFormatter
就可以专注于解析与格式化的工作。
DateTimeFormatter
提供的方法命名上遵循 java.time
包的一贯原则,提供了很多静态方法来生成一个 DateTimeFormatter
。
格式化当地时间:
// 作为静态字段,线程安全,各个方法都可以使用
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String format() {
LocalDateTime localDateTime = LocalDateTime.now();
return formatter.format(localDateTime);
」
DateTimeFormatter
的 pattern 与 SimpleDateFormat
中的完全一致,因此没有额外学习成本。
上例使用的是当地时间,如果一个跨国公司要处理时间,就必须考虑到不同国家所在的时区不同,使用服务器所配置的时区就不行了,必须显式地将时区加到处理代码中。
假设我们从美国买了一件东西,美国的物流传了一个时间戳我们,需要在网站上显示给国内的用户。此时我们显式地指定时区(LocalDateTime
中不保存时区,我们只是用它来生成本地时间):
public static final ZoneId BEIJING = ZoneId.of("+08:00");
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String format(Long timestampMillis) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestampMillis), zoneId);
return formatter.format(localDateTime);
}
解析时间与格式化时间不太一致,由于时区存在于 ZonedDateTime
类中而不存在于 LocalDateTime
,因此解析时间是将 DateTimeFormatter
传入时间类中来实现的,由各个时间类来决定存储的形式。
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
public ZonedDateTime parse(String dateTime) {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTime, formatter);
return zonedDateTime;
}
//调用 parse("2020-11-17 21:52:31 +08:00")