LocalDate、LocalTime 和 LocalDateTime
# 52.LocalDate、LocalTime 和 LocalDateTime
由于 Java 旧的时间 API 不好用,市面上出现了不少关于时间的 API,例如开源的 Joda Time (opens new window),应用广泛;JDK 团队邀请 Joda Time 的作者 Stephen Colebourne 共同设计了 java.time
API,并在 Java8 引入。
主要的类如下:
- 本地日期和时间:
LocalDate
,LocalTime
,LocalDateTime
; DateTimeFormatter
:格式化日期,可取代SimpleDateFormat
;- 带时区的日期和时间:
ZonedDateTime
; - 时刻:
Instant
; - 时区:
ZoneId
,ZoneOffset
; - 时间间隔:
Duration
和Period
;
其他改进:
- 严格区分了时刻、本地日期、本地时间和带时区的日期时间
- 提供了方便的日期和时间运算的 API
- Month 的范围用 1~12 表示 1 月到 12 月 ,Week 的范围用 1~7 表示周一到周日(这才合理嘛!😄)
- 新 API 的类型几乎全部是不变类型(和 String 类似),可以放心使用不必担心被修改。
我们首先介绍这 3 个:
LocalDate
,表示本地日期LocalTime
,表示本地时间LocalDateTime
,表示本地日期和时间- 实际上,看
LocalDateTime
的源码可以发现,LocalDateTime
是由LocalDate
和LocalTime
来表示的
# 入门案例
LocalDate d = LocalDate.now();
LocalTime t = LocalTime.now();
LocalDateTime dt = LocalDateTime.now();
System.out.println(d); //2022-12-22
System.out.println(t); //12:53:28.332
System.out.println(dt); //2022-12-22T12:53:28.332
2
3
4
5
6
7
说明:
- 本地日期和时间通过
now()
获取到的总是以当前默认时区返回的 - 打印的格式和旧的不同,默认严格按照 ISO 8601 (opens new window) 规定的日期和时间格式进行打印
获取一个某个时间的属性:
- 获得年份:
getYear()
- 获得当前的月份(枚举值):
getMonth()
- 获得月份的数值:
getMonthValue()
- 获得今天是本月的第几天:
getDayOfMonth()
- 获得当前时间的小时:
getHour()
- 获得当前时间的分钟:
getMinute()
- 获得当前时间的秒:
getSecond()
- 获得今天是本年的第几天:
getDayOfYear()
- 获得今天是本周的第几天:
getDayOfWeek()
还可以用 get 方法获取指定字段的 int 值:
LocalDateTime ldt = LocalDateTime.of(2022, 1, 1, 2, 3, 4, 56789);//2022-1-1 2:3:1
int i1 = ldt.get(ChronoField.MINUTE_OF_DAY); //123 指的是目前已经过了2*小时 + 3分钟= 123分钟了
int i2 = ldt.get(ChronoField.MINUTE_OF_HOUR); //3 指的是目前在当前这个小时内,已经过了3分钟
2
3
关于 ChronoField 类,我们下回分解
# 指定时间
我们可以在创建对象的时候,指定时间:
LocalDate d2 = LocalDate.of(2022, 5, 21);
LocalTime t2 = LocalTime.of(13,14, 15);
LocalDateTime dt2 = LocalDateTime.of(2022,5, 21, 13, 14, 15);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
System.out.println("d2: " + d2); //d2: 2022-05-21
System.out.println("t2: " + t2); //t2: 13:14:15
System.out.println("dt2: " + dt2); //dt2: 2022-05-21T13:14:15
System.out.println("dt3: " + dt3); //dt3: 2022-05-21T13:14:15
2
3
4
5
6
7
8
9
第 4 行我们用了 LocalDate 和 LocalTime 创建 LocalDateTime ,也可以反过来:
LocalDate d3 = dt2.toLocalDate();
LocalTime t3 = dt2.toLocalTime();
System.out.println("d3: " + d3); //d3: 2022-05-21
System.out.println("t3: " + t3); //t3: 13:14:15
2
3
4
还可以通过字符串来创建:
// 通过字符串创建对象
LocalDateTime dt4 = LocalDateTime.parse("2022-5-21T21:13:14");
LocalDate d4 = LocalDate.parse("2022-5-21");
LocalTime t6 = LocalTime.parse("15:16:17");
2
3
4
注意 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
其他注意事项:
构造方法得是合理的参数值,例如小时的范围是 0~23,如果超过(例如设置成 52),运行的时候会抛出异常:
Exception in thread "main" java.time.DateTimeException: Invalid value for HourOfDay (valid values 0 - 23): 52
at java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
at java.time.temporal.ChronoField.checkValidValue(ChronoField.java:703)
at java.time.LocalTime.of(LocalTime.java:317)
at TestLocalDateTime.main(TestLocalDateTime.java:17)
2
3
4
5
# 调整日期和时间
可以用 withXXX()
调整时间和日期
- 调整年:
withYear()
- 调整月:
withMonth()
- 调整日:
withDayOfMonth()
- 调整时:
withHour()
. 例如:withHour(15)
会把10:11:12
变为15:11:12
: - 调整分:
withMinute()
- 调整秒:
withSecond()
LocalDateTime dt = LocalDateTime.of(2022,5, 21, 13, 14, 15);
dt = dt.withYear(2122);
dt = dt.withHour(05); //调整成5点
System.out.println(dt); //2122-05-21T05:14:15
2
3
4
注意:月份的加减会自动调整日期,例如从 10 月 31 日减去一个月,会得到 9 月 30 日,因为 9 月没有 31
LocalDateTime
还有一个通用的 with()
方法允许我们做更复杂的调整,我们后面会提到。
# 比较时间
由于代码执行需要一定的时间,需要注意分开创建的时间对象是不一样的(1 毫秒不同也不行,除非你能保证两者运行的时间差距在 1 毫秒,一些性能好的计算机可能做到,但我们不能把希望寄托于未知 🧐)。
LocalTime t4 = LocalTime.now();
LocalTime t5 = LocalTime.now();
System.out.println(t4.compareTo(t5)); //-1
2
3
我们还可以用 isBefore()
、isAfter()
方法来判断,其返回 Boolean 值
# 日期和时间的加减
LocalDateTime
提供了方便的加减方法:
LocalDateTime dt = LocalDateTime.of(2022,5, 21, 13, 14, 15);
dt = dt.plusDays(5); // sub 5 days
dt = dt.plusDays(1); // add 1 day
dt = dt.plusHours(1); //sub 1 hour
dt = dt.minusHours(3); //add 3 hours
System.out.println(dt); //2022-05-27T11:14:15
2
3
4
5
6
minus
有减法的意思。可以对秒数、分钟、小时、天数、周数、月份、年份进行加减。
可以用链式调用,简化代码:
dt = LocalDateTime.of(2022,5, 21, 13, 14, 15);
dt = dt.plusDays(5).minusDays(1).plusHours(1).minusHours(3);
System.out.println(dt); //2022-05-25T11:14:15
2
3
注意:月份的加减会自动调整日期,例如从 10 月 31 日减去一个月,会得到 9 月 30 日,因为 9 月没有 31
# 转换时区
LocalDateTime
无法与时间戳进行转换,因为 LocalDateTime
没有时区,无法确定某一时刻。
后面我们会介绍 ZonedDateTime
,其相当于 LocalDateTime
加时区的组合,可以与 long
表示的时间戳进行转换。
# 小结
LocalDate
,LocalTime
,LocalDateTime
的常用方法如下:
方法名 | 用处 | 示例 |
---|---|---|
now() | 获取当前默认时区的当前时间 | LocalDate.now(); |
getXXX() | 获取时间的某个字段 | getYear() 获取年份 |
of() | 指定时间 | LocalDate.of(2022, 5, 21); |
parse(String s) | 根据字符串来指定时间 | LocalDate.parse("2022-5-21"); |
withXXX() | 调整时间和日期 | dt.withYear(2122); |
compareTo() isBefore() isAfter() | 比较时间 | t4.compareTo(t5) |
plusXXX(), minusXXX() | 加减时间 | dt.plusDays(5) |