5.新的日期和时间API
第三方的日期和时间库,比如Joda-Time.Java 8在java.time包中整合了很多Joda-Time的特性。
5.1LocalDate、 LocalTime、 Instant、 Duration 以及 Period
public static void main(String[] args) {
/**
* 使用 LocalDate 和 LocalTime
* LocalDate类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。
* 另外,它也不附带任何与时区相关的信息
*/
LocalDate date = LocalDate.of(2018,10,02);
System.out.println(date);//2018-10-02
int year = date.getYear();//2018
System.out.println(year);
Month month = date.getMonth();
System.out.println(month);//OCTOBER
int day = date.getDayOfMonth();
System.out.println(day);//2
DayOfWeek dayOfWeek = date.getDayOfWeek();
System.out.println(dayOfWeek);//TUESDAY
int lengthOfMonth = date.lengthOfMonth();
System.out.println(lengthOfMonth);//31
boolean leapYear = date.isLeapYear();//是否为闰年
System.out.println(leapYear);//false
//从系统时钟中获取当前的日期
LocalDate now = LocalDate.now();
System.out.println(now);//2018-10-02
/**
* 使用TemporalField读取LocalDate的值
*/
int year1 = date.get(ChronoField.YEAR);
int month1 = date.get(ChronoField.MONTH_OF_YEAR);
int day1 = date.get(ChronoField.DAY_OF_MONTH);
System.out.println(year1 + ":" + month1 + ":" + day1);
/**
* 类似地,一天中的时间,比如13:45:20,可以使用LocalTime类表示
*/
LocalTime time = LocalTime.of(13, 45, 20);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
System.out.println(hour + ":" + minute + ":" + second);
/**
* 通过静态方法parse创建
* 使用DateTimeFormatter格式化日期
*/
LocalDate date1 = LocalDate.parse("2014-03-18");
LocalTime time1 = LocalTime.parse("13:45:20");
System.out.println(date1 + ":" + time1);
/**
*合并日期和时间
* LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息,
* 你可以直接创建,也可以通过合并日期和时间对象构造
*/
// 2014-03-18T13:45:20
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 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);
LocalDate date2 = dt1.toLocalDate();
LocalTime time2 = dt1.toLocalTime();
System.out.println(date2 + ":" + time2);
/**
* 机器的日期和时间格式
* 从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。
* 这也是新的java.time.Instant类对时间建模的方式,基本上它是以Unix元年时间
* (传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。
*/
/**
* 定义 Duration 或 Period
* 1.Duration:计算两个LocalTimes对象、两个LocalDateTimes对象,或者两个Instant对象之间的时间长短(s/ns)
* 2.Period:如果你需要以年、月或者日的方式对多个时间单位建模,可以使用Period类。
* 使用该类的工厂方法between,你可以使用得到两个LocalDate之间的时长
*/
Duration d1 = Duration.between(time1, time2);
System.out.println(d1);
Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
LocalDate.of(2014, 3, 18));
System.out.println(tenDays);
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes1 = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays1 = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
}
5.2操纵、解析和格式化日期
如果你已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使用withAttribute方法。 withAttribute方法会创建对象的一个副本,并按照需要修改它的属性。注意,下面的这段代码中所有的方法都返回一个修改了属性的对象。它们都不会修改原来的对象!
package com.h.java8;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
/**
* Created by John on 2018/9/23.
*/
public class TestMain {
public static void main(String[] args) {
/**
* 以比较直观的方式操纵LocalDate的属性
*/
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.withYear(2011);
LocalDate date3 = date2.withDayOfMonth(25);
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);
System.out.println(date1+" " + date2 + " " +date3 + " " + date4);
/**
* 以相对方式修改LocalDate对象的属性
*/
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.plusWeeks(1);
LocalDate date3 = date2.minusYears(3);
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
System.out.println(date1+" " + date2 + " " +date3 + " " + date4);
/**
* 使用 TemporalAdjuster
* 实现一个定制的TemporalAdjuster
*/
LocalDate date = LocalDate.now();
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);
});
System.out.println(date);
/**
* 打印输出及解析日期-时间对象
* 处理日期和时间对象时,格式化以及解析日期-时间对象是另一个非常重要的功能。
* 新的java.time.format包就是特别为这个目的而设计的。
* 和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。
* 所以,你能够以单例模式创建格式器实例,就像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
System.out.println(s1 + " " + s2);
LocalDate date1 = LocalDate.parse("20140318",
DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18",
DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(date1 + " " + date2);
/**
* 按照某个模式创建DateTimeFormatter
*/
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
System.out.println(formattedDate);//18/03/2014
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
System.out.println(date2);//2014-03-18
/**
* 创建一个本地化的DateTimeFormatter
*/
LocalDate date = LocalDate.of(2014, 3, 18);
DateTimeFormatter italianFormatter =
DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
String formattedDate = date.format(italianFormatter); // 18. marzo 2014
System.out.println(formattedDate);
LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);
System.out.println(date2);//2014-03-18
}
}
5.3处理不同的时区和历法
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);
小结:
Java 8之前老版的java.util.Date类以及其他用于建模日期时间的类有很多不一致及设计上的缺陷,包括易变性以及糟糕的偏移值、默认值和命名。
新版的日期和时间API中,日期时间对象是不可变的。
新的API提供了两种不同的时间表示方式,有效地区分了运行时人和机器的不同需求。
你可以用绝对或者相对的方式操纵日期和时间,操作的结果总是返回一个新的实例,老的日期时间对象不会发生变化。
TemporalAdjuster让你能够用更精细的方式操纵日期,不再局限于一次只能改变它的一个值,并且你还可按照需求定义自己的日期转换器。
你现在可以按照特定的格式需求,定义自己的格式器,打印输出或者解析日期时间对象。这些格式器可以通过模板创建,也可以自己编程创建,并且它们都是线程安全的。
你可以用相对于某个地区/位置的方式,或者以与UTC/格林尼治时间的绝对偏差的方式表示时区,并将其应用到日期时间对象上,对其进行本地化。
你现在可以使用不同于ISO-8601标准系统的其他日历系统了。