前言:java8之前我们创建时间new Date或者使用Calendar,很不幸这些类设计的有缺陷,导致使用这些方法写出的代码很容易出错。DateFormat是一个线程不安全的类,在多线程使用会有意想不到的结果出现。在java8中整合了很多Joda-Time的特性。

一、LocalDate、LocalTime、Instant、Duration、Period

如果创建简单的时间和日期。java.time包提供了很多新类帮你解决问题,分别是LocalDate、LocalTime、Instant、Duration、Period实现的接口都是TemporalAccessor,这些类都是final类型的,都不能修改。

1、LocalDate和LocalTime

可以通过of的静态方法创建LocalDate和LocalTime实例。

a、LocalDate

LocalDate date = LocalDate.of(2019, 9, 1); int year = date.getYear(); Month month = date.getMonth(); int day = date.getDayOfMonth(); DayOfWeek dow = date.getDayOfWeek(); int len = date.lengthOfMonth(); boolean leap = date.isLeapYear();

还可以通过工厂方法系统日期获取当前日期:

LocalDate today = LocalDate.now();

还可以传递一个TemporalField参数给get方法也可以拿到同样的信息。TemporalField是一个接口,ChronoField实现了这一接口。

int year = date.get(ChronoField.YEAR); int month = date.get(ChronoField.MONTH_OF_YEAR); int day = date.get(ChronoField.DAY_OF_MONTH);

b、LocalTime

创建LocalTime也可以使用of静态方法

LocalTime time = LocalTime.of(15, 30, 20); int hour = time.getHour(); int minute = time.getMinute(); int second = time.getSecond();

LocalDate和LocalTime都可以使用parse方法将字符串解析为LocalDate或LocalTime

LocalDate date = LocalDate.parse("2019-09-01"); LocalTime time = LocalTime.parse("15:30:20");

2、合并时间和日期

LocalDateTime是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息。
可以使用atTime或atDate创建LocalDateTime

 LocalDateTime dt1 = LocalDateTime.of(2019, 9, 1, 15, 45, 20);  LocalDate date = LocalDate.now();  LocalTime date1 = LocalTime.now();  LocalDateTime dt3 = date.atTime(15, 45, 20);  LocalDateTime dt5 = date1.atDate(date);

也可以使用toLocalDate或toLocalTime,从LocalDateTime提取LocalDate或LocalTime。

LocalDate date1 = dt1.toLocalDate(); LocalTime time1 = dt1.toLocalTime();

3、机器日期和时间格式

今天是某年,某月,某日等等作为人我们可以看懂,但是作为机器是看不懂的。从机器角度看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是java.time.Instant类对时间建模的方式。基本上是以Unix元年时间(1970年1月1日)开始所经历秒数计算的。
可以使用静态工厂方式创建该实例

  Instant instant = Instant.ofEpochSecond(3);   Instant.ofEpochSecond(3,-1000000);//三秒之前的100w纳秒(1秒)     Instant.ofEpochSecond(3,1000000);//三秒之后的100w纳秒(1秒)

4、定义Duration 和Period

需要创建Temporal对象之间的Duration (间隔),Duration 类的静态工厂方法between就是为了这个目的设计的。

Duration d1 = Duration.between(time1, time2); Duration d1 = Duration.between(dateTime1, dateTime2); Duration d2 = Duration.between(instant1, instant2)

如果是需要以年,月,日的方式对多个时间建模,可以使用Period类。该类也有between方法。

Period tenDays = Period.between(LocalDate.of(2019, 3, 8), LocalDate.of(2019, 9, 1));

Duration 和Period提供了很多静态方法,可以直接创建对应的实例。

Duration threeMinutes = Duration.ofMinutes(3); Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); Period tenDays = Period.ofDays(10); Period threeWeeks = Period.ofWeeks(3); Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

JAVA8 日期和时间(LocalDate、LocalDateTime、ZoneId、Duration、Period、Instant)API 用法详解
JAVA8 日期和时间(LocalDate、LocalDateTime、ZoneId、Duration、Period、Instant)API 用法详解

二、操纵、解析和格式化日期

如果已经有了一个LocalDate对象,想要创建它的修改版本,最直接的方法使用withxxx()方法。withxxx()会创建一个对象的副本。

LocalDate date1 = LocalDate.of(2019, 1, 1); LocalDate date2 = date1.withYear(2011); LocalDate date3 = date2.withDayOfMonth(25); LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);

还可以加上或者减去一段时间

LocalDate date1 = LocalDate.of(2019, 9, 1); LocalDate date2 = date1.plusWeeks(1); LocalDate date3 = date2.minusYears(3); LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);

LocalDate、LocalTime、LocalDateTIme、Instant都有大量的通用方法
JAVA8 日期和时间(LocalDate、LocalDateTime、ZoneId、Duration、Period、Instant)API 用法详解

1、使用TemporalAdjuster

截止目前看到的日期比较都是相对比较直接的,有时候需要更加复杂的操作。比如,将日期调整到下个周日、下个工作日等。可以使用with的重载版本,向其传递一个更多定制化的TemporalAdjuster对象。

LocalDate date1 = LocalDate.of(2019, 9, 1); LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); LocalDate date3 = date2.with(lastDayOfMonth());

TemporalAdjuster的工厂方法列表

JAVA8 日期和时间(LocalDate、LocalDateTime、ZoneId、Duration、Period、Instant)API 用法详解
如果没有符合你要求的预定义TemporalAdjuster,你可以创建自己的TemporalAdjuster

//函数式接口 @FunctionalInterface public interface TemporalAdjuster { 	Temporal adjustInto(Temporal temporal); }

例:计算明天的日期,同时过滤掉周六日

public class NextWorkingDay implements TemporalAdjuster {     @Override     public Temporal adjustInto(Temporal 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是一个函数式接口你能以lambda方式想该adjuster接口传递行为

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

如果你使用lambda方式定义TemporalAdjuster对象,推荐使用TemporalAdjusters的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; 	if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; 	return temporal.plus(dayToAdd, ChronoUnit.DAYS); }); date = date.with(nextWorkingDay);

2、打印输出及解析日期

a、格式化和解析日期java8提供了线程安全DateTimeFormatter类,创建格式化器最简单的方法时通过它的静态工厂方法及常量。

下面例子,生成不同格式的字符串

LocalDate date = LocalDate.of(2019, 9, 1); String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);//20190901 String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2019-09-01

可以用prase方法解析字符串

LocalDate date1 = LocalDate.parse("20190901", DateTimeFormatter.BASIC_ISO_DATE); LocalDate date2 = LocalDate.parse("2019-09-01", DateTimeFormatter.ISO_LOCAL_DATE);

b、按照某个模式创建DateTimeFormatter

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); LocalDate date1 = LocalDate.of(2019, 9, 1); String formattedDate = date1.format(formatter); LocalDate date2 = LocalDate.parse(formattedDate, formatter);

c、创建本地化的DateTimeFormatter

DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); LocalDate date1 = LocalDate.of(2019, 3, 1); String formattedDate = date.format(italianFormatter); // 1. marzo 2019 LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);

三、处理不同时区

时区的处理是新版日期和时间API新增的功能。新的java.time.ZoneId替代了老版的java.util.TimeZone。
例:

ZoneId romeZone = ZoneId.of("Europe/Rome");//"区域/城市"

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

下面这张图可以更好的让我我们理解LocalDate、LocalTime、LocalDateTime、ZonedDateTime
JAVA8 日期和时间(LocalDate、LocalDateTime、ZoneId、Duration、Period、Instant)API 用法详解
通过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);

LocalDateTime和Date转换实例

  public static void dateToCoverLocadate() {         Date date = new Date();         Instant instant = date.toInstant();         ZoneId zoneId = ZoneId.systemDefault();         LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);         System.out.println(localDateTime);     }       public static void LocaldateToCoverDate() {         LocalDateTime now = LocalDateTime.now();         ZoneId zoneId = ZoneId.systemDefault();         Instant instant = now.atZone(zoneId).toInstant();         Date from = Date.from(instant);         System.out.println(from);     }