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