LocalDateTime
约 1346 字大约 4 分钟
2024-08-10
从 Java 8 开始,java.time 包提供了新的日期和时间 API,主要涉及的类型有
本地日期和时间:
LocalDateTime,LocalDate,LocalTime带时区的日期和时间:
ZonedDateTime时刻:
Instant时区:
ZoneId,ZoneOffset时间间隔:
Duration
以及一套新的用于取代 SimpleDateFormat 的格式化类型 DateTimeFormatter
和旧的 API 相比,新 API 严格区分了时刻、本地日期、本地时间和带时区的日期时间,并且,对日期和时间进行运算更加方便
此外,新 API 修正了旧 API 不合理的常量设计
Month的范围用1~12表示1月到12月Week的范围用1~7表示周一到周日
最后,新 API 的类型几乎全部是不变类型(和 String 类似),可以放心使用不必担心被修改
LocalDateTime
LocalDateTime,它表示一个本地日期和时间,通过 now() 获取到的总是以当前默认时区返回的,和旧 API 不同,LocalDateTime、LocalDate 和 LocalTime 默认严格按照 ISO 8601 规定的日期和时间格式进行打印
public static void main(String[] args) {
LocalDate d = LocalDate.now(); // 当前日期
LocalTime t = LocalTime.now(); // 当前时间
LocalDateTime dt = LocalDateTime.now(); // 当前日期时间
System.out.println(d); // 2021-02-25
System.out.println(t); // 23:02:41.820
System.out.println(dt); // 2021-02-25T23:02:41.820
}在获取 3 个类型的时候,由于执行一行代码总会消耗一点时间,因此,3 个类型的日期和时间很可能对不上(时间的毫秒数基本上不同)。为了保证获取到同一时刻的日期和时间,可以改写如下
LocalDateTime dt = LocalDateTime.now(); // 当前日期时间
LocalDate d = dt.toLocalDate(); // 当前日期
LocalTime t = dt.toLocalTime(); // 当前时间LocalDateTime 可以通过 of() 方法创建指定的日期和时间
LocalDate d = LocalDate.of(2021, 02, 25); // 指定日期
LocalTime t = LocalTime.of(23, 07, 22); // 指定时间
LocalDateTime dt1 = LocalDateTime.of(2021, 02, 25, 23, 07, 22);
LocalDateTime dt2 = LocalDateTime.of(d, t);因为严格按照 ISO 8601 的格式,因此,将字符串转换为 LocalDateTime 就可以传入标准格式
LocalDate d = LocalDate.parse("2021-02-25");
LocalTime t = LocalTime.parse("23:07:22");
LocalDateTime dt = LocalDateTime.parse("2021-02-25T23:07:22");ISO 8601 规定的日期和时间分隔符是 T。标准格式如下
日期:
yyyy-MM-dd时间:
HH:mm:ss带毫秒的时间:
HH:mm:ss.SSS日期和时间:
yyyy-MM-dd'T'HH:mm:ss带毫秒的日期和时间:
yyyy-MM-dd'T'HH:mm:ss.SSS
LocalDateTime 提供了对日期和时间进行加减的非常简单的链式调用
public static void main(String[] args) {
LocalDateTime dt1 = LocalDateTime.of(2021, 03, 26, 23, 07, 22);
System.out.println(dt1); // 2021-03-26T23:07:22
// 加 5天,减 3 小时
LocalDateTime dt2 = dt1.plusDays(5).minusHours(3);
System.out.println(dt2); // 2021-03-31T20:07:22
// 减 1 月
LocalDateTime dt3 = dt2.minusMonths(1);
System.out.println(dt3); // 2021-02-28T20:07:22
}月份加减会自动调整日期,例如从 2021-03-31 减去 1 个月得到的结果是 2021-02-28,因为 2 月没有 31 日
对日期和时间进行调整则使用 withXxx() 方法
调整年:
withYear()调整月:
withMonth()调整日:
withDayOfMonth()调整时:
withHour()调整分:
withMinute()调整秒:
withSecond()
public static void main(String[] args) {
LocalDateTime dt1 = LocalDateTime.of(2021, 03, 26, 23, 07, 22);
System.out.println(dt1); // 2021-03-26T23:07:22
// 日期变为 29 号
LocalDateTime dt2 = dt1.withDayOfMonth(29);
System.out.println(dt2); // 2021-03-29T23:07:22
// 月份变为 2
LocalDateTime dt3 = dt2.withMonth(1);
System.out.println(dt3); // 2021-01-29T23:07:22
}调整月份时,会相应地调整日期,例如把 2021-03-29 的月份调制为 2 时,日期自动变为了 28
LocalDateTime 还有一个通用的 with() 方法允许我们做更复杂的运算
public static void main(String[] args) {
// 本月第一天 0:00 时刻
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
System.out.println(firstDay); // 2021-02-01T00:00
// 本月最后 1 天
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDay); // 2021-02-28
// 下月第 1 天
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(nextMonthFirstDay); // 2021-03-01
// 本月第 1 个周一
LocalDate firstWeekDay = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstWeekDay); // 2021-02-01
}判断时间先后
要判断两个 LocalDateTime 的先后,可以使用 isBefore()、isAfter() 方法,对于 LocalDate和LocalTime 类似
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now(); // 2021-02-25T23:48:20.543
LocalDateTime targetTime = LocalDateTime.parse("2021-02-23T22:22:22");
System.out.println(now.isBefore(targetTime)); // false
}DateTimeFormatter
如果要自定义输出的格式,或者要把一个非 ISO 8601 格式的字符串解析成 LocalDateTime,可以使用新的 DateTimeFormatter
public static void main(String[] args) {
// 自定义格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dtf.format(LocalDateTime.now())); // 2021/02/25 23:18:02
// 自定义格式解析
LocalDateTime dt = LocalDateTime.parse("2021/02/25 23:07:22", dtf);
System.out.println(dt); // 2021-02-25T23:07:22
}Duration 和 Period
Duration 表示两个时刻之间的时间间隔。另一个类似的 Period 表示两个日期之间的天数
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2021, 02, 28, 17, 43, 56);
LocalDateTime end = LocalDateTime.of(2022, 02, 28, 18, 22, 57);
Duration d = Duration.between(start, end);
System.out.println(d); // PT8760H39M1S,8760小时39分1秒
Period p = LocalDate.of(2021, 02, 28).until(LocalDate.of(2022, 03, 29));
System.out.println(p); // P1Y1M1D,1年1月1日
}注意到两个 LocalDateTime 之间的差值使用 Duration 表示,类似 PT1235H10M30S,表示 1235 小时 10 分钟 30 秒。而两个 LocalDate 之间的差值用 Period 表示,类似 P1M21D,表示 1 个月 21 天
Java 8 引入的 java.time API。怎么和一个开源的 Joda Time 很像?难道 JDK 也开始抄袭开源了?其实正是因为开源的 Joda Time 设计很好,应用广泛,所以 JDK 团队邀请 Joda Time 的作者 Stephen Colebourne 共同设计了 java.time API