DateTimeFormatter ✨
在使用旧的Date
对象时,我们使用SimpleDateFromat
进行格式化显示。使用新的LocalDateTime
或ZonedDateTime
时,我们要进行格式化显示,就要用DateTimeFormatter
。
和SimpleDateFormat
不同的是,DateTimeFormatter
不但是不变对象,它还是线程安全的。SimpleDateFormat
不是线程安全的,使用的时候,只能在方法内部创建新的局部变量。而DateTimeFormatter
可以只创建一个实例,到处引用。
创建DateTimeFormatter
时,通过传入格式化字符串来实现:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
另一种创建方法是,传入格式化字符串时,同时执行Locale
:
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.US);
System.out.println(formatter1.format(date)); // Mon, 2025-January-20 14:02
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.CHINESE);
System.out.println(formatter2.format(date)); // 周一, 2025-一月-20 14:02
这种方式可以按照Locale
默认习惯格式化:
ZonedDateTime zonedDateTime = ZonedDateTime.now();
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm ZZZZ");
System.out.println(formatter.format(zonedDateTime)); // 2023-08-26T21:50 GMT+08:00
var zhFormatter = DateTimeFormatter.ofPattern("yyyy MMM dd EE HH:mm", Locale.CHINA);
System.out.println(zhFormatter.format(zonedDateTime)); // 2023 8月 26 周六 21:50
var usFormatter = DateTimeFormatter.ofPattern("E, MMMM/dd/yyyy HH:mm", Locale.US);
System.out.println(usFormatter.format(zonedDateTime)); // Sat, August/26/2023 21:50
如果要自定义输出的格式,或者要把一个非ISO 8601格式的字符串解析成LocalDateTime
,可以使用新的DateTimeFormatter
:
LocalDateTime dateTime = LocalDateTime.now();
// 自定义日期时间格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
System.out.println(dateTime.format(formatter)); // 20/01/2025 14:05:53
// 自定义格式解析
LocalDateTime dateTime2 = LocalDateTime.parse("20/09/2021 10:00:00", formatter);
System.out.println(dateTime2); // 2021-09-20T10:00
LocalDateTime
提供了对日期和时间进行加减的非常简单的链式调用:
LocalDateTime now = LocalDateTime.of(2020, 12, 31, 10, 10, 10);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(now); // 2020-12-31T10:10:10
// 加一天3小时5分钟
LocalDateTime after = now.plusDays(1).plusHours(3).plusMinutes(5);
System.out.println(after.format(formatter)); // 2021-01-01 13:15:10
// 减一天3小时5分钟
LocalDateTime before = now.minusDays(1).minusHours(3).minusMinutes(5);
System.out.println(before.format(formatter)); // 2020-12-30 07:05:10
// 减去1个月
LocalDateTime beforeMonth = now.minusMonths(1);
System.out.println(beforeMonth.format(formatter)); // 2020-11-30 10:10:10
注意到月份加减会自动调整日期,例如从2020-12-31
减去1个月得到的结果是2020-11-30
,因为11月没有31日。
对日期和时间进行调整则使用withXxx()
方法,例如:withHour(15)
会把10:11:12
变为15:11:12
:
调整年:
withYear()
调整月:
withMonth()
调整日:
withDayOfMonth()
调整时:
withHour()
调整分:
withMinute()
调整秒:
withSecond()
LocalDateTime now = LocalDateTime.of(2020, 12, 31, 10, 10, 10);
System.out.println(now); // 2020-12-31 10:10:10
// 日期变为12
System.out.println(now.withDayOfMonth(12)); // 2020-12-12T10:10:10
// 月份变为9
System.out.println(now.withMonth(9)); // 2020-09-30T10:10:10
同样注意到调整月份时,会相应地调整日期,即把2020-12-31
的月份调整为9
时,日期也自动变为30
。
实际上,LocalDateTime
还有一个通用的with()
方法允许我们做更复杂的运算。例如:
// 本月第一天0:00时刻:
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
System.out.println(firstDay); // 2023-08-01T00:00
// 本月最后1天:
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDay); // 2023-08-31
// 下月第1天:
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(nextMonthFirstDay); // 2023-09-01
// 本月第1个周一:
LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstWeekday); // 2023-08-07
对于计算某个月第1个周日这样的问题,新的API可以轻松完成。
要判断两个LocalDateTime
的先后,可以使用isBefore()
、isAfter()
方法,对于LocalDate
和LocalTime
类似:
LocalDateTime now = LocalDateTime.now();
LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
System.out.println(now.isBefore(target)); // false
System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19))); // true
System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00"))); // true
注意到LocalDateTime
无法与时间戳进行转换,因为LocalDateTime
没有时区,无法确定某一时刻。后面我们要介绍的ZonedDateTime
相当于LocalDateTime
加时区的组合,它具有时区,可以与long
表示的时间戳进行转换。
最后更新于
这有帮助吗?