Administrator
发布于 2020-12-25 / 1165 阅读 / 0 评论 / 0 点赞

Java8之新的日期和时间API-LocalDateTime等的使用

Java8之新的日期和时间API-LocalDateTime等的使用

第十二章 新的日期和时间API

1、LocalDate、LocalTime、Instant、Duration 以及 Period

1) 使用LocalDate和LocalTime

  • 创建一个LocalDate对象并读取其值
LocalDate date = LocalDate.of(2014, 3, 18);
//2014-03-18
int year = date.getYear();
//2014
Month month = date.getMonth();
//MARCH
int day = date.getDayOfMonth();
//18
DayOfWeek dow = date.getDayOfWeek();
//TUESDAY
int len = date.lengthOfMonth();
//31.查看一个月一共多少天
boolean leap = date.isLeapYear(); 
//false(不是闰年)


LocalDate today = LocalDate.now(); 
//获取当前时间
  • 当然也可以传递一个TemporalField参数给get方法拿到同样的信息。
  • TemporalField是一个接口,它定 义了如何访问temporal对象某个字段的值。ChronoField枚举实现了这一接口,所以你可以很 方便地使用get方法得到枚举元素的值。
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
int day = date.get(ChronoField.DAY_OF_MONTH); 
  • 创建LocalTime并读取其值
LocalTime time = LocalTime.of(13, 45, 20);
//13:45:20
int hour = time.getHour();
//13
int minute = time.getMinute();
//45
int second = time.getSecond();
//20
  • LocalDate和LocalTime都可以通过parse去将字符串解析成日期时间
LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13:45:20"); 

2) 合并日期和时间-LocalDateTime

  • 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); 
  • 当然也可以从LocalDateTime中提取LocalDate或者LocalTime
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();

3) 机器的日期和时间格式 -Instant

  • 这个是方便机器来读时间的,返回的是时间戳

通过向静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例。静 态工厂方法ofEpochSecond还有一个增强的重载版本,它接收第二个以纳秒为单位的参数值,对 传入作为秒数的参数进行调整。重载的版本会调整纳秒参数,确保保存的纳秒分片在0到999 999 999之间。

  • 所以,下面四个实例相同
//3秒
Instant.ofEpochSecond(3);
//3秒+0纳秒
Instant.ofEpochSecond(3, 0);
//2秒+100万纳秒,即1秒
Instant.ofEpochSecond(2, 1_000_000_000);
//4秒-100万纳秒
Instant.ofEpochSecond(4, -1_000_000_000); 

//可以通过Instant.now()返回int型时间戳
int day = Instant.now();

4) 定义Duration或Period-持续时间或期间

上面的所有类都实现了Temporal接口,这个接口定义了如何读取和操纵为时间建模和对象的值

可以创建两个LocalTimes对象、两个LocalDateTimes对象,或者两个Instant对象之间的duration

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
  • 可能会发现上面的三行都是time的,因为Duration主要是用于计算以秒和纳秒衡量时间的长短,所以不能仅向between传递LocalDate对象作为参数
  • 当然,由于Local系列是方便人来读的,而Instant是方便机器读的时间戳,所以不能将两种参数同时传入between方法,否则会报个DateTimeException异常
  • 当然,如果要算两个日期之间的期间,可以用Period
Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
 																LocalDate.of(2014, 3, 18));
  • 而且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); 
  • 以下是日期-时间类中表示时间间隔的通用方法

image-20201223100530453

image-20201223100541513

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

ChronoUnit很重要,请记住这个枚举类

  • 如果已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使 用withAttribute方法。且不会修改对象而只修改属性.
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);
  • 也可以加减
LocalDate date1 = LocalDate.of(2014, 3, 18);
LocalDate date2 = date1.plusWeeks(1);
LocalDate date3 = date2.minusYears(3);
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
  • 除此之外,还有很多通用方法

image-20201224094424094

1) 使用TemporalAdjuster- 下周、下个工作日等

  • 比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。
  • 可以通过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()); 
TemporalAdjuster类中的工厂方法

image-20201224094900711

2) 实现一个定制的TemporalAdjuster

  • 设计一个NextWorkingDay类,该类实现了TemporalAdjuster接口,能够计算明天 的日期,同时过滤掉周六和周日这些节假日。

  • 如果当天的星期介于周一至周五之间,日期向后移动一天;如果当天是周六或者周日,则 返回下一个周一。

  • NextWorkingDay类的实现如下:

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);
}); 
  • 当然,这样写也依然很累赘,可以对它进行封装。
  • 使用TemporalAdjusters类的静态工厂方法ofDateAdjuster,它接受一个UnaryOperator
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); 

3) 打印输出及解析日期-时间对象(格式化)

  • 新的 java.time.format包就是特别为这个目的而设计的。这个包中,最重要的类是DateTimeFormatter。

  • 所有的 DateTimeFormatter实例都能用于以一定的格式创建代表特定日期或时间的字符串。比如,下 面的这个例子中,我们使用了两个不同的格式器生成了字符串:

LocalDate date = LocalDate.of(2014, 3, 18);
//20140318
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE);
//2014-03-18
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
  • 当然,也可以通过解析代表日期或时间的字符串重新创建该日期对象。所有的日期和时间API 都提供了表示时间点或者时间段的工厂方法,你可以使用工厂方法parse达到重创该日期对象 的目的:
LocalDate date1 = LocalDate.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);
  • 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);
  • 当然你也定义自己的DateTimeFormatter
DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date.format(italianFormatter); // 18. marzo 2014
LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter); 
  • 如果还需要更细化的控制,可以用DateTimeFormatterBuilder类
  • 可 以 通 过 DateTimeFormatterBuilder自己编程上个代码片段的italianFormatter
DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
         .appendText(ChronoField.DAY_OF_MONTH)
         .appendLiteral(". ")
         .appendText(ChronoField.MONTH_OF_YEAR)
         .appendLiteral(" ")
         .appendText(ChronoField.YEAR)
         .parseCaseInsensitive()
         .toFormatter(Locale.ITALIAN); 

3、处理不同时区的和历法

  • 新的java.time.ZoneId 类是老版java.util.TimeZone的替代品。
  • 如果之前用老版的java.util.TimeZone创建了时区,可以用toZoneId()方法转换成新的ZoneId类型
ZoneId zoneId = TimeZone.getDefault().toZoneId(); 

1) 为时间点添加时区信息

//日期
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); 
  • 对ZonedDateTime的组成部分进行了说明,理解LocaleDate、 LocalTime、LocalDateTime以及ZoneId之间的差异。

image-20201224122412386

//通过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);

评论