美文网首页
Java8 新的日期和时间API

Java8 新的日期和时间API

作者: 巴巴11 | 来源:发表于2020-03-29 13:44 被阅读0次

问题:
 为什么在Java 8中需要引入新的日期和时间库
 同时为人和机器表示日期和时间
 定义时间的度量
 操纵、格式化以及解析日期
 处理不同的时区和历法

java.util.Date --> java.util.Calendar --> java.time包

DateFormat方法也有它自己的问题。比如,它不是线程安全的。

java.time包中提供了很多新的类可以帮你解决问题,它们是LocalDate、 LocalTime、 Instant、 Duration和Period。

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(); // 10
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);

类似地,一天中的时间,比如13:45:20,可以使用LocalTime类表示。
你可以使用of重载的两个工厂方法创建LocalTime的实例。
第一个重载函数接收小时和分钟,第二个重载函数同时还接收秒。
同LocalDate一样, LocalTime类也提供了一些getter方法访问这些变量的值:

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
# 可以向parse方法传递一个DateTimeFormatter。该类的实例定义了如何格式化一个日期或者时间对象。
# 是替换老版java.util.DateFormat的推荐替代品。
# 一旦传递的字符串参数无法被解析为合法的LocalDate或LocalTime对象,这两个parse方法都会抛出一个继
# 承自RuntimeException的DateTimeParseException异常
LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13:45:20");

合并日期和时间
这个复合类名叫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 date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();

机器的日期和时间格式
从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。
这也是新的java.time.Instant类对时间建模的方式,
基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。

Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
Instant.ofEpochSecond(2, 1_000_000_000); // 2秒之后再加上100万纳秒( 1秒)
Instant.ofEpochSecond(4, -1_000_000_000);

# Instant类也支持静态工厂方法now,它能够帮你获取当前时刻的时间戳。
# 可以通过Duration和Period类使用Instant

定义 Duration 或 Period

# Temporal接口定义了如何读取和操纵为时间建模的对象的值

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

Period tenDays = Period.between(LocalDate.of(2014, 3, 8),LocalDate.of(2014, 3, 18));

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

这些日期时间对象都是不可修改的,这是为了更好地支持函数式编程,确保线程安全,保持领域模式一致性而做出的重大设计决定。
当然,新的日期和时间API也提供了一些便利的方法来创建这些对象的可变版本。

操纵、解析和格式化日期
如果你已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使
用withAttribute方法。 withAttribute方法会创建对象的一个副本,并按照需要修改它的属
性。方法都返回一个修改了属性的对象。它们都不会修改原来的对象!

LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.withYear(2011); // 2011-03-18
LocalDate date3 = date2.withDayOfMonth(25); // 2011-03-25
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9); // 2011-09-25

# 以相对方式修改LocalDate对象的属性
LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.plusWeeks(1); // 2014-03-25
LocalDate date3 = date2.minusYears(3); // 2011-03-25
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); // 2011-09-25

使用预定义的TemporalAdjuster

import static java.time.temporal.TemporalAdjusters.*;

LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); // 2014-03-23
LocalDate date3 = date2.with(lastDayOfMonth()); // 2014-03-31

TemporalAdjuster类中的工厂方法

dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天
firstDayOfMonth 创建一个新的日期,它的值为当月的第一天
firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天
firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天
firstDayOfYear 创建一个新的日期,它的值为当年的第一天
firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值
lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天
lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天
lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天
lastDayOfYear 创建一个新的日期,它的值为今年的最后一天
lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值
next/previous
创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星
期几要求的日期
nextOrSame/previousOrSame
创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星
期几要求的日期,如果该日期已经符合要求,直接返回该对象

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

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

日期格式化:

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

LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
LocalDate date2 = LocalDate.parse(formattedDate, formatter);

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

DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);


# 处理不同的时区和历法
# 地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构( IANA)的时区数据库提供。
# java.time.ZoneId类是老版java.util.TimeZone

ZoneId romeZone = ZoneId.of("Europe/Rome");
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);

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


# ISO-8601日历系统是世界文明日历系统的事实标准。但是, Java 8中另外还提供了4种其他的
日历系统。这些日历系统中的每一个都有一个对应的日志类,分别是ThaiBuddhistDate、
MinguoDate 、 JapaneseDate 以 及 HijrahDate 。

LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(date);

Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow();

总结:

Java 8之前老版的java.util.Date类以及其他用于建模日期时间的类有很多不一致及
设计上的缺陷,包括易变性以及糟糕的偏移值、默认值和命名。
新版的日期和时间API中,日期时间对象是不可变的。

新的API提供了两种不同的时间表示方式,有效地区分了运行时人和机器的不同需求。

你可以用绝对或者相对的方式操纵日期和时间,操作的结果总是返回一个新的实例,老
的日期时间对象不会发生变化。

TemporalAdjuster让你能够用更精细的方式操纵日期,不再局限于一次只能改变它的
一个值,并且你还可按照需求定义自己的日期转换器。

你现在可以按照特定的格式需求,定义自己的格式器,打印输出或者解析日期时间对象。
这些格式器可以通过模板创建,也可以自己编程创建,并且它们都是线程安全的。

你可以用相对于某个地区/位置的方式,或者以与UTC/格林尼治时间的绝对偏差的方式表
示时区,并将其应用到日期时间对象上,对其进行本地化。

你现在可以使用不同于ISO-8601标准系统的其他日历系统了。
image.png
image.png

像LocalDate、 LocalTime、 LocalDateTime以及Instant这样表示时
间点的日期时间类提供了大量通用的方法


image.png image.png

相关文章

网友评论

      本文标题:Java8 新的日期和时间API

      本文链接:https://www.haomeiwen.com/subject/kvqjuhtx.html