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);

    

 

posted @ 2021-06-18 20:42  如果可以就好了  阅读(103)  评论(0)    收藏  举报