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);
- 以下是日期-时间类中表示时间间隔的通用方法
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);
- 除此之外,还有很多通用方法
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类中的工厂方法
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之间的差异。
//通过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);