楔子
学习笔记
12 新的日期和时间API
12.1 LocalDate、LocalTime、LocalDateTime、Instant、Duration和Period
12.1.1 使用LocalDate和LocalTime
LocalDate该类的实例是一个不可变对象,它只提供简单的日期,并不包含当天的时间信息。 另外,它也不带任何与时区相关的信息。
可以通过静态工厂方法of创建一个LocalDate实例。
可以通过传递一个TemporalField参数给get方法访问同样的信息。TemporalField是一个接口,它定义了如何访问temporal对象某个字段的值。ChronoField枚举实现这一接口。
//使用TemporalField 读取LocalDate的值
LocalDate date = LocalDate.of(2021, 10, 14);
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
int dayOfMonth = date.get(ChronoField.DAY_OF_MONTH);
int dayOfYear = date.get(ChronoField.DAY_OF_YEAR);
System.out.println(dayOfMonth);
System.out.println(dayOfYear);
12.1.3 机器的日期和时间格式
从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上的某个点的单一大型整数。这也是新的java.time.Instant类时间建模的方式。基本上是以UNIX元年时间开始所经历的秒数进行计算。
可以通过向静态工厂方法ofEpochSecond传递代表秒数的值创建一个该类的实例。此外,Instant类支持纳秒精度。静态工厂还有一个增强的重载版本,它接受第二个以纳秒为单位的参数值。
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
Instant.ofEpochSecond(2, 1_000_000_000);//2秒之后再加10亿纳秒(1秒)
Instant类也支持静态工厂方法now,它能够帮助获取当前时刻的时间戳。 Instant的设计初衷是为了便于机器使用。它包含的是由秒及纳秒所构成的数字。所以,无法,它无法处理那些非常容易理解的时间单位,例如
int day = Instant.now().get(ChronoField.DAY_OF_MONTH);
会抛出异常
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field
12.1.4 定义Duration或Period
Temporal接口定义了如何读取和操作为时间建模的对象的值。
Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
由于LocalDateTime和Instant是为不同的目的而设计的,一个是为了便于人阅读使用,另一个是为了便于机器处理,因此不能将二者混用。如果试图在这两个类之间创建Duration,就会触发一个DateTimeException。此外,因为Duration类主要用于以秒和纳秒衡量时间的长短,所以不能仅向between方法传递一个LocalDate对象作为参数。
如果需要以年,月或者日的方式对多个时间单位建模,那么就可以使用Period类。使用该类的工厂方法between。可以得到两个LocalDate之间的时间长
Period tenDays = Period.between(LocalDate.of(2017, 9, 11), LocalDate.of(2017, 9, 21));
12.2 操作、解析和格式化日期
如果已经有了一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使用withAttrubute方法。该方法会创建对象的一个副本,并按照需要修改它的属性。
LocalDate date1 = LocalDate.of(2021, 10, 14);
LocalDate date2 = date1.withYear(2025);//2025-10-14
LocalDate date3 = date2.withDayOfMonth(25);//2025-10-25
LocalDate with = date3.with(ChronoField.MONTH_OF_YEAR, 2);//2025-02-25
采用更通用的with方法能达到同样的目的,它接受的第一个参数是TemporalField对象。
LocalDate date1 = LocalDate.of(2021, 10, 14);
LocalDate date2 = date1.plusWeeks(1);
LocalDate date3 = date2.minusYears(1);
LocalDate plus = date3.plus(6, ChronoUnit.MONTHS);
12.2.1 使用TemporalAdjuster
之前所有的日期操作都是相对比较直接的。有时候,需要进一步更加复杂的操作,比如,将日期调整到下个周日,下个工作日,或者本月最后一天。可以使用重载版本的with方法。向其传递提供了更多定制化选择的TemporalAdjusterd对象,更加灵活地处理日期。TemporalAdjusters类静态方法提供了预定义的很多方法。
public void temporalAdjuster() {
LocalDate date1 = LocalDate.now();//2021-10-15
LocalDate date2 = date1.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));//2021-10-17
LocalDate date3 = date2.with(TemporalAdjusters.lastDayOfMonth());//2021-10-31
}
TemporalAdjuster类中的工厂方法
方法名 | 描述 |
---|---|
dayOfWeekInMonth | 创建一个新的日期,它的值为同一个月中每一周的第几天(负数表示从月末往月初计数) |
firstDayOfMonth | 创建一个新的日期,它的值为当月的第一天 |
firstDayOfNextMonth | 创建一个新的日期,它的值为下月的第一天 |
firstDayOfNextYear | 创建一个新的日期,它的值为明年的第一天 |
firstDayOfYear | 创建一个新的日期,它的值为当年的第一天 |
firstInMonth | 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值 |
lastDayOfMonthlastDayOf | 创建一个新的日期,它的值为当月的最后一天为下月的最后一天 |
lastDayOfNextYear | 创建一个新的日期,它的值为明年的最后一天 |
lastDayOfYear | 创建一个新的日期,它的值为当年的最后一天 |
lastInMonth | 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值 |
next/previous | 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期 |
nextOrSame/previousOrSame | 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星 期几要求的日期,如果该日期已经符合要求,则直接返回该对象 |
12.2.2 打印输出及解析日期-时间对象
public void dateFormat() {
LocalDate date = LocalDate.of(2014, 3, 18);
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2014-03-18
String s3 = date.format(DateTimeFormatter.BASIC_ISO_DATE);//20140318
LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE);
}