Java8---新的日期和时间API
Java8以前时间和日期api存在的问题:
在Java 1.0 中,对日期和时间的支持只能依赖java.util.Date 类,这个类无法表示日期,只能以毫秒的精度表示时间。
比如:年份的起始选择是1900 年,月份的起始从0 开始。这意味着,如果你想要用Date 表示Java 9 的发布日期,即2017 年9 月21 日,
需要创建下面这样的Date 实例:
Date date = new Date(117, 8, 21);
它的打印输出效果为:
Thu Sep 21 00:00:00 CET 2017
在Java 1.1 中,Date 类中的很多方法被废弃了,取而代之的是java.util.Calendar 类,Calendar 类也有类似的问题和设计缺陷月份依旧是从0 开始计算
(不过,至少Calendar 类去掉了由1900 年开始计算年份这一设计)
有的特性只在某一个类中提供,比如用于以语言无关方式格式化和解析日期或时间的DateFormat 方法就只在Date 类里有。
使用LocalDate 和LocalTime
LocalDate 类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不附带任何与时区相关的信息。
LocalDate date = LocalDate.of(2017, 9, 21);//2017-09-21 int year = date.getYear(); //2017 Month month = date.getMonth(); //SEPTEMBER int day = date.getDayOfMonth(); //21 DayOfWeek dow = date.getDayOfWeek(); //THURSDAY int len = date.lengthOfMonth(); //30 boolean leap = date.isLeapYear(); //false
可以使用工厂方法now 从系统时钟中获取当前的日期:
LocalDate today = LocalDate.now();
TemporalField 是一个接口,它定义了如何访问temporal 对象某个字段的值。ChronoField 枚举实现了这一接口,
可以通过传递一个TemporalField 参数给get 方法访问同样的信息
int year = date.get(ChronoField.YEAR); int month = date.get(ChronoField.MONTH_OF_YEAR); int day = date.get(ChronoField.DAY_OF_MONTH);
使用Java 内建的getYear()、getMonthValue()和getDayOfMonth()方法,以更具可读性的方式访问这些信息,
int year = date.getYear(); int month = date.getMonthValue(); int day = date.getDayOfMonth();
而LocalTime是一个不可变的对象,表示一个没有日期信息的时间。
LocalTime time = LocalTime.of(13, 45, 20); int hour = time.getHour(); int minute = time.getMinute(); int second = time.getSecond();
LocalDate 和LocalTime 都可以通过解析代表它们的字符串创建。使用静态方法parse:
LocalDate date = LocalDate.parse("2017-09-21");
LocalTime time = LocalTime.parse("13:45:20");
一旦传递的字符串参数无法被解析为合法的LocalDate 或LocalTime 对象,这两个parse 方法都会抛出一个继承自RuntimeException 的DateTimeParseException 异常。
使用复合类LocalDateTime
LocalDateTime,是LocalDate 和LocalTime 的合体。它同时表示了日期和时间,但不带有时区信息,可以直接创建,也可以通过合并日期和时间对象创建
LocalDateTime dt1 = LocalDateTime.of(2014, Month.SEPTEMBER, 21, 13, 45, 20); LocalDateTime dt2 = LocalDateTime.of(date, time); LocalDateTime dt3 = date.atTime(13, 45, 20); LocalDateTime dt4 = date.atTime(time); LocalDateTime dt5 = time.atDate(date);
从LocalDateTime 中提取LocalDate 或者LocalTime 组件:
LocalDate date1 = dt1.toLocalDate(); LocalTime time1 = dt1.toLocalTime();
操纵、解析和格式化日期
withAttribute 方法会创建对象的一个副本,并按照需要修改它的属性,它们都不会修改原来的对象!
LocalDate date1 = LocalDate.of(2017, 9, 21); LocalDate date2 = date1.withYear(2011); LocalDate date3 = date2.withDayOfMonth(25); LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 2);
以相对方式修改LocalDate 对象的属性
LocalDate date1 = LocalDate.of(2017, 9, 21); LocalDate date2 = date1.plusWeeks(1); LocalDate date3 = date2.minusYears(6); LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
表示时间点的日期–时间类的通用方法
方 法 名 是否是静态方法 描 述 from 是 依据传入的Temporal 对象创建对象实例 now 是 依据系统时钟创建Temporal 对象 of 是 由Temporal 对象的某个部分创建该对象的实例 parse 是 由字符串创建Temporal 对象的实例 atOffset 否 将Temporal 对象和某个时区偏移相结合 atZone 否 将Temporal 对象和某个时区相结合 format 否 使用某个指定的格式器将Temporal 对象转换为字符串(Instant 类不提供该方法) get 否 读取Temporal 对象的某一部分的值 minus 否 创建Temporal 对象的一个副本,通过将当前Temporal 对象的值减去一定的时长创建该副本 plus 否 创建Temporal 对象的一个副本,通过将当前Temporal 对象的值加上一定的时长创建该副本 with 否 以该Temporal 对象为模板,对某些状态进行修改创建该对象的副本
使用TemporalAdjuster
使用重载版本的with 方法,向其传递一个提供了更多定制化选择的TemporalAdjuster 对象,更加灵活地处理日期
import static java.time.temporal.TemporalAdjusters.*; LocalDate date1 = LocalDate.of(2014, 3, 18); LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); LocalDate date3 = date2.with(lastDayOfMonth());
TemporalAdjusters 类中的工厂方法
方 法 名 描 述 dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天(负数表示从月末往月初计数) firstDayOfMonth 创建一个新的日期,它的值为当月的第一天 firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天 firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天 firstDayOfYear 创建一个新的日期,它的值为当年的第一天 firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值 lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天 lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天 lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天 lastDayOfYear 创建一个新的日期,它的值为当年的最后一天 lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值 next/previous 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期 nextOrSame/previousOrSame 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,则直接返回该对象
TemporalAdjuster 接口
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
这意味着TemporalAdjuster 接口的实现需要定义如何将一个Temporal 对象转换为另一个Temporal 对象
例如:
设计一个NextWorkingDay 类,该类实现了TemporalAdjuster 接口,能够计算明
天的日期,同时过滤掉周六和周日这些节假日。格式如下所示:
date = date.with(new NextWorkingDay());
如果当天的星期数介于周一至周五之间,就将日期向后移动一天;如果当天是周六或者周
日,则返回下一个周一。
date = date.with(temporal -> {
DayOfWeek dow =
DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
使用
TemporalAdjuster 类的静态工厂方法ofDateAdjuster将它的逻辑封装到一个类中实现代码复用
TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(
temporal -> {
DayOfWeek dow =
DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
date = date.with(nextWorkingDay);
打印输出及解析日期–时间对象
所有的DateTime-Formatter 实例都能用于以一定的格式创建代表特定日期或时间的字符串
ASIC_ISO_DATE和ISO_LOCAL_DATE 这样的常量是DateTimeFormatter 类的预定义实例
LocalDate date = LocalDate.of(2014, 3, 18); String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);//20140318 String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2014-03-18
可以通过解析代表日期或时间的字符串重新创建该日期对象,使用工厂方法parse 达到重创该日期对象的目的:
LocalDate date1 = LocalDate.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);
和老的java.util.DateFormat 相比较,所有的DateTimeFormatter 实例都是线程安全的。
DateTimeFormatter 类还支持一个静态工厂方法,它可以按照某个特定的模式创建格式器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
处理不同的时区和历法
时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules 这个类中包含了40 个这样的实例。你可以简单地通过调用ZoneId 的getRules()得到指定时区的规则。
每个特定的ZoneId 对象都由一个地区ID 标识,比如:
ZoneId romeZone = ZoneId.of("Europe/Rome");
地区ID 都为“{区域}/{城市}”的格式,这些地区集合的设定都由因特网编号分配机构(IANA)的时区数据库提供。
你可以通过Java 8 的新方法toZoneId 将一个老的时区对象转换为ZoneId:
ZoneId zoneId = TimeZone.getDefault().toZoneId();
为时间点添加时区信息 LocalDate date = LocalDate.of(2014, Month.MARCH, 18); ZonedDateTime zdt1 = date.atStartOfDay(romeZone); LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); ZonedDateTime zdt2 = dateTime.atZone(romeZone); Instant instant = Instant.now(); ZonedDateTime zdt3 = instant.atZone(romeZone);

通过ZoneId,你还可以将LocalDateTime 转换为Instant: LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); Instant instantFromDateTime = dateTime.toInstant(romeZone); 你也可以通过反向的方式得到LocalDateTime 对象: Instant instant = Instant.now(); LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone);

浙公网安备 33010602011771号