java作业第十二章_《java 8 实战》读书笔记 -第十二章 新的日期和时间 API

一、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

你还可以通过传递一个TemporalField参数给get方法拿到同样的信息。TemporalField是一个接口,它定义了如何访问temporal对象某个字段的值。ChronoField枚举实现了这一接口,所以你可以很方便地使用get方法得到枚举元素的值,如下所示。

使用TemporalField读取LocalDate的值

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类表示。类似地,一天中的时间,比如13:45:20,可以使用LocalTime类表示。

LocalTime time = LocalTime.of(13, 45, 20);

int hour = time.getHour();

int minute = time.getMinute();

int second = time.getSecond();

LocalDate和LocalTime都可以通过解析代表它们的字符串创建。使用静态方法parse,你可以实现这一目的:

LocalDate date = LocalDate.parse("2014-03-18");

LocalTime time = LocalTime.parse("13:45:20");

你可以向parse方法传递一个DateTimeFormatter。它是替换老版java.util.DateFormat的推荐替代品。

2.合并日期和时间

这个复合类名叫LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息,你可以直接创建,也可以通过合并日期和时间对象构造,如下所示。

直接创建LocalDateTime对象,或者通过合并日期和时间的方式创建

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

注意,通过它们各自的atTime或者atDate方法,向LocalDate传递一个时间对象,或者向LocalTime传递一个日期对象的方式,你可以创建一个LocalDateTime对象。你也可以使用toLocalDate或者toLocalTime方法,从LocalDateTime中提取LocalDate或者LocalTime组件:

LocalDate date1 = dt1.toLocalDate();

LocalTime time1 = dt1.toLocalTime();

3.机器的日期和时间格式

新的java.time.Instant类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的

秒数进行计算。你可以通过向静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例。静态工厂方法ofEpochSecond还有一个增强的重载版本,它接收第二个以纳秒为单位的参数值,对传入作为秒数的参数进行调整。重载的版本会调整纳秒参数,确保保存的纳秒分片在0到999 999 999之间。这意味着下面这些对ofEpochSecond工厂方法的调用会返回几乎同样的Instant对象:

Instant.ofEpochSecond(3);

Instant.ofEpochSecond(3, 0);

Instant.ofEpochSecond(2, 1_000_000_000);

Instant.ofEpochSecond(4, -1_000_000_000);

从Java7开始,你就可以在你的Java代码里把长整型数字比如10000000000写成一个更具可读性10_000_000_000。

Instant类也支持静态工厂方法now,它能够帮你获取当前时刻的时间戳。它包含的是由秒及纳秒所构成的数字。所以,它无法处理那些我们非常容易理解的时间单位。比如下面这段语句:

int day = Instant.now().get(ChronoField.DAY_OF_MONTH);

它会抛出下面这样的异常:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field:

DayOfMonth

4.定义 Duration 或 Period

你可以创建两个LocalTimes对象、两个LocalDateTimes对象,或者两个Instant对象之间的duration( 持续期间),如下所示:

Duration d1 = Duration.between(time1, time2);

Duration d1 = Duration.between(dateTime1, dateTime2);

Duration d2 = Duration.between(instant1, instant2);

如果你试图在这两类对象之间创建duration,会触发一个DateTimeException异常。此外,由于Duration类主要用于以秒和纳秒衡量时间的长短,你不能仅向between方法传递一个LocalDate对象做参数。

如果你需要以年、月或者日的方式对多个时间单位建模,可以使用Period类。使用该类的工厂方法between,你可以使用得到两个LocalDate之间的时长,如下所示:

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

LocalDate.of(2014, 3, 18));

Duration和Period类都提供了很多非常方便的工厂类,直接创建对应的实例;不再是只能以两个temporal对象的差值的方式来定义它们的对象。

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

日期/时间类中表示时间间隔的通用方法

449584bc43d52d49c3b9543bfafb9244.png

c3ca4e442adea1d1e4850e19cc65eafa.png

Temporal接口的实现类:

HijrahDate, Instant, JapaneseDate, LocalDate, LocalDateTime, LocalTime, MinguoDate, OffsetDateTime, OffsetTime, ThaiBuddhistDate, Year, YearMonth, ZonedDateTime

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

如果你已经有一个LocalDate对象,想要创建它的一个修改版,最直接也最简单的方法是使用withAttribute方法。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);

采用更通用的with方法能达到同样的目的,它接受的第一个参数是一个TemporalField对象,格式类似上面代码最后一行。更确切地说,使用get和with方法,我们可以将Temporal对象值的读取和修改区分开。如果Temporal对象不支持请求访问的字段,它会抛出一个UnsupportedTemporalTypeException异常。

以相对方式修改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);

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

5b21e4ca3b57de3ff12ad09f63b9a0c3.png

1.使用 TemporalAdjuster

有的时候,你需要进行一些更加复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天。这时,你可以使用重载版本的with方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象,更加灵活地处理日期。对于最常见的用例,日期和时间API已经提供了大量预定义的TemporalAdjuster。你可以通过TemporalAdjuster类的静态工厂方法访问它们。

使用预定义的TemporalAdjuster

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

LocalDate date1 = LocalDate.of(2014, 3, 18);

LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); //2014-03-23

LocalDate date3 = date2.with(lastDayOfMonth());//2014-03-31

TemporalAdjuster类中的工厂方法

c0d5768e33d20891c1710ab25903ca3d.png

TemporalAdjuster接口

@FunctionalInterface

public interface TemporalAdjuster {

Temporal adjustInto(Temporal temporal);

}

如果你想要使用Lambda表达式定义TemporalAdjuster对象,推荐使用TemporalAdjusters类的静态工厂方法ofDateAdjuster,它接受一个UnaryOperator

3.打印输出及解析日期时间对象

新的java.time.format包就是特别为这个目的而设计的。这个包中,最重要的类是DateTimeFormatter。创建格式器最简单的方法是通过它的静态工厂方法以及常量。像BASIC_ISO_DATE和 ISO_LOCAL_DATE 这样的常量是 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

你可以使用工厂方法parse达到重创该日期对象的目的:

LocalDate date1 = LocalDate.parse("20140318",

DateTimeFormatter.BASIC_ISO_DATE);

LocalDate date2 = LocalDate.parse("2014-03-18",

DateTimeFormatter.ISO_LOCAL_DATE);

和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。

按照某个模式创建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);

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

新的java.time.ZoneId类是老版java.util.TimeZone的替代品。跟其他日期和时间类一样,ZoneId类也是无法修改的。时区是按照一定的规则将区域划分成的标准时间相同的区间。在ZoneRules这个类中包含了40个这样的实例。你可以简单地通过调用ZoneId的getRules()得到指定时区的规则。每个特定的ZoneId对象都由一个地区ID标识,比如:

ZoneId romeZone = ZoneId.of("Europe/Rome");

地区ID都为“{区域}/{城市}”的格式,你可以通过Java 8的新方法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);

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

1.利用和 UTC/格林尼治时间的固定偏差计算时区

ZoneOffset类,它是ZoneId的一个子类,表示的是当前时间和伦敦格林尼治子午线时间的差异:

ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");

注意,使用这种方式定义的ZoneOffset并未考虑任何日光时的影响,所以在大多数情况下,不推荐使用。ZoneOffset也是ZoneId。

你甚至还可以创建这样的OffsetDateTime,它使用ISO-8601的历法系统,以相对于UTC/格林尼治时间的偏差方式表示日期时间。

LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);

OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset)

2.使用别的日历系统

Java 8中另外还提供了4种其他的日历系统。这些日历系统中的每一个都有一个对应的日志类,分别是ThaiBuddhistDate、MinguoDate 、 JapaneseDate 以及HijrahDate 。所有这些类以及 LocalDate 都实现了ChronoLocalDate接口。

如下所示:

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

JapaneseDate japaneseDate = JapaneseDate.from(date);

或者,你还可以为某个Locale显式地创建日历系统,接着创建该Locale对应的日期的实例。新的日期和时间API中,Chronology接口建模了一个日历系统,使用它的静态工厂方法ofLocale,可以得到它的一个实例,代码如下:

Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);

ChronoLocalDate now = japaneseChronology.dateNow();

日期及时间API的设计者建议我们使用LocalDate,尽量避免使用ChronoLocalDate,原因是开发者在他们的代码中可能会做一些假设,而这些假设在不同的日历系统中,有可能不成立。比如,有人可能会做这样的假设,即一个月天数不会超过31天,一年包括12个月,或者一年中包

含的月份数目是固定的。由于这些原因,我们建议你尽量在你的应用中使用LocalDate,包括存储、操作、业务规则的解读;不过如果你需要将程序的输入或者输出本地化,这时你应该使用ChronoLocalDate类。

伊斯兰教日历

在Java 8新添加的几种日历类型中,HijrahDate(伊斯兰教日历)是最复杂一个,因为它会发生各种变化。Hijrah日历系统构建于农历月份继承之上。Java 8提供了多种方法判断一个月份,比如新月,在世界的哪些地方可见,或者说它只能首先可见于沙特阿拉伯。withVariant方法可以用于选择期望的变化。为了支持HijrahDate这一标准,Java 8中还包括了乌姆库拉(Umm Al-Qura)变量。

下面这段代码作为一个例子说明了如何在ISO日历中计算当前伊斯兰年中斋月的起始和终止日期:

HijrahDate ramadanDate =

HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)

.with(ChronoField.MONTH_OF_YEAR, 9);

System.out.println("Ramadan starts on " +

IsoChronology.INSTANCE.date(ramadanDate) +

" and ends on " +

IsoChronology.INSTANCE.date(

ramadanDate.with(

TemporalAdjusters.lastDayOfMonth())));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值