created: 2022-07-24T11:03:29+08:00
updated: 2022-07-24T14:10:52+08:00
tags:
java.time.temporal
包提供了支持日期和时间代码,特别是日期和时间计算的接口、类和枚举的集合。
这些接口旨在用于最低级别。应用程序代码应该根据具体类型声明变量和参数,例如 LocalDate
或 ZonedDateTime
,而不是根据 Temporal
接口。这与声明 String
类型的变量完全一样,而不是 CharSequence
类型的变量。
Temporal
接口提供了用于访问基于时间的对象的框架,并由基于时间的类实现,例如 Instant
、LocalDateTime
和 ZonedDateTime
。此接口提供了添加或减去时间单位的方法,使基于时间的算术在各种日期和时间类中变得容易且一致。 TemporalAccessor
接口提供只读版本的 Temporal
接口。
Temporal
和 TemporalAccessor
对象都是根据字段定义的,由 TemporalField
接口中指定的那样。 ChronoField
枚举是 TemporalField
接口的具体实现,并提供了一组丰富的已定义常量,例如 DAY_OF_WEEK
、MINUTE_OF_HOUR
和 MONTH_OF_YEAR
。
这些字段的单位由 TemporalUnit
接口指定。 ChronoUnit
枚举实现了 TemporalUnit
接口。字段 ChronoField.DAY_OF_WEEK
是 ChronoUnit.DAYS
和 ChronoUnit.WEEKS
的组合。 ChronoField
和 ChronoUnit
枚举将在以下部分中讨论。
Temporal
接口中基于算术的方法需要根据 TemporalAmount
值定义的参数。
实现 TemporalField
接口的 ChronoField
枚举提供了一组丰富的常量来访问日期和时间值。一些示例是 CLOCK_HOUR_OF_DAY
、NANO_OF_DAY
和 DAY_OF_YEAR
。此枚举可用于表示时间的概念方面,例如一年中的第三周、一天中的第 11 个小时或每月的第一个星期一。当遇到未知类型的 Temporal
时,可以使用 TemporalAccessor.isSupported(TemporalField)
方法来确定 Temporal
是否支持特定字段。以下代码行返回 false
,表示 LocalDate
不支持 ChronoField.CLOCK_HOUR_OF_DAY
:
boolean isSupported = LocalDate.now().isSupported(ChronoField.CLOCK_HOUR_OF_DAY);
ISO-8601 特定的 ISO-8601 日历系统的附加字段在 IsoFields
类中定义。以下示例显示如何使用 ChronoField
和 IsoFields
获取字段的值:日历系统的附加字段在 IsoFields
类中定义。以下示例显示如何使用 ChronoField
和 IsoFields
获取字段的值:
time.get(ChronoField.MILLI_OF_SECOND)
int qoy = date.get(IsoFields.QUARTER_OF_YEAR);
另外两个类定义了可能有用的附加字段,WeekFields
和 JulianFields
。
ChronoUnit
枚举实现了 TemporalUnit
接口,并提供了一组基于日期和时间的标准单位,从毫秒到千年。请注意,并非所有类都支持所有 ChronoUnit
对象。例如,Instant
类不支持 ChronoUnit.MONTHS
或 ChronoUnit.YEARS
。 Date-Time API 中的类包含可用于验证类是否支持特定时间单位的 isSupported(TemporalUnit)
方法。以下对 isSupported()
的调用返回 false
,确认 Instant
类不支持 ChronoUnit.DAYS
。
Instant instant = Instant.now();
boolean isSupported = instant.isSupported(ChronoUnit.DAYS);
java.time.temporal
包中的 TemporalAdjuster
接口提供了采用 Temporal
值并返回调整值的方法。调整器可以与任何基于时间的类型一起使用。
如果调整器与 ZonedDateTime
一起使用,则会计算一个新的日期,以保留原始时间和时区值。
TemporalAdjusters
类(注意复数)提供了一组预定义的调整器,用于查找每月的第一天或最后一天、一年的第一天或最后一天、每月的最后一个星期三或特定日期后的第一个星期二,举几个例子。预定义的调整器被定义为静态方法,旨在与静态导入语句一起使用。
下面的示例使用几个 TemporalAdjusters
方法,结合基于时间的类中定义的 with 方法,根据 2000 年 10 月 15 日的原始日期计算新日期:
LocalDate date = LocalDate.of(2000, Month.OCTOBER, 15);
DayOfWeek dotw = date.getDayOfWeek();
System.out.printf("%s is on a %s%n", date, dotw);
System.out.printf("first day of Month: %s%n",
date.with(TemporalAdjusters.firstDayOfMonth()));
System.out.printf("first Monday of Month: %s%n",
date.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)));
System.out.printf("last day of Month: %s%n",
date.with(TemporalAdjusters.lastDayOfMonth()));
System.out.printf("first day of next Month: %s%n",
date.with(TemporalAdjusters.firstDayOfNextMonth()));
System.out.printf("first day of next Year: %s%n",
date.with(TemporalAdjusters.firstDayOfNextYear()));
System.out.printf("first day of Year: %s%n",
date.with(TemporalAdjusters.firstDayOfYear()));
result
2000-10-15 is on a SUNDAY
first day of Month: 2000-10-01
first Monday of Month: 2000-10-02
last day of Month: 2000-10-31
first day of next Month: 2000-11-01
first day of next Year: 2001-01-01
first day of Year: 2000-01-01
您还可以创建自己的自定义调节器。为此,您需要创建一个使用 adjustInto(Temporal)
方法实现 TemporalAdjuster
接口的类。
example
public class PaydayAdjuster implements TemporalAdjuster {
/**
* The adjustInto method accepts a Temporal instance
* and returns an adjusted LocalDate. If the passed in
* parameter is not a LocalDate, then a DateTimeException is thrown.
*/
public Temporal adjustInto(Temporal input) {
LocalDate date = LocalDate.from(input);
int day;
if (date.getDayOfMonth() < 15) {
day = 15;
} else {
day = date.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
}
date = date.withDayOfMonth(day);
if (date.getDayOfWeek() == DayOfWeek.SATURDAY ||
date.getDayOfWeek() == DayOfWeek.SUNDAY) {
date = date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}
return input.with(date);
}
}
example
LocalDate nextPayday = date.with(new PaydayAdjuster());
在 2013 年,6 月 15 日和 6 月 30 日都发生在周末。使用 6 月 3 日和 6 月 18 日(2013 年)的相应日期运行前面的示例,得到以下结果:
Given the date: 2013 Jun 3
the next payday: 2013 Jun 14
Given the date: 2013 Jun 18
the next payday: 2013 Jun 28
TemporalQuery
可用于从基于时间的对象中检索信息。
TemporalQueries
类(注意复数)提供了几个预定义的查询,包括在应用程序无法识别基于时间的对象的类型时有用的方法。与调整器一样,预定义查询被定义为静态方法,并设计为与静态导入语句一起使用。
例如,精度查询返回特定基于时间的对象可以返回的最小 ChronoUnit
。以下示例对几种基于时间的对象使用精度查询:
TemporalQuery<TemporalUnit> query = TemporalQueries.precision();
System.out.printf("LocalDate precision is %s%n",
LocalDate.now().query(query));
System.out.printf("LocalDateTime precision is %s%n",
LocalDateTime.now().query(query));
System.out.printf("Year precision is %s%n",
Year.now().query(query));
System.out.printf("YearMonth precision is %s%n",
YearMonth.now().query(query));
System.out.printf("Instant precision is %s%n",
Instant.now().query(query));
result
LocalDate precision is Days
LocalDateTime precision is Nanos
Year precision is Years
YearMonth precision is Months
Instant precision is Nanos
example1
public class FamilyVacations implements TemporalQuery<Boolean> {
// Returns true if the passed-in date occurs during one of the
// family vacations. Because the query compares the month and day only,
// the check succeeds even if the Temporal types are not the same.
public Boolean queryFrom(TemporalAccessor date) {
int month = date.get(ChronoField.MONTH_OF_YEAR);
int day = date.get(ChronoField.DAY_OF_MONTH);
// Disneyland over Spring Break
if ((month == Month.APRIL.getValue()) && ((day >= 3) && (day <= 8)))
return Boolean.TRUE;
// Smith family reunion on Lake Saugatuck
if ((month == Month.AUGUST.getValue()) && ((day >= 8) && (day <= 14)))
return Boolean.TRUE;
return Boolean.FALSE;
}
}
use
// define a year, a month and a day
LocalDate = date = LocalDate.of(year, month, day);
boolean isFamilyVacation = date.query(new FamilyVacations());
example2
public class FamilyBirthdays {
// Returns true if the passed-in date is the same as one of the
// family birthdays. Because the query compares the month and day only,
// the check succeeds even if the Temporal types are not the same.
public static Boolean isFamilyBirthday(TemporalAccessor date) {
int month = date.get(ChronoField.MONTH_OF_YEAR);
int day = date.get(ChronoField.DAY_OF_MONTH);
// Angie's birthday is on April 3.
if ((month == Month.APRIL.getValue()) && (day == 3))
return Boolean.TRUE;
// Sue's birthday is on June 18.
if ((month == Month.JUNE.getValue()) && (day == 18))
return Boolean.TRUE;
// Joe's birthday is on May 29.
if ((month == Month.MAY.getValue()) && (day == 29))
return Boolean.TRUE;
return Boolean.FALSE;
}
}
use
// Invoking the query without using a lambda expression.
Boolean isFamilyVacation = date.query(new FamilyVacations());
// Invoking the query using a lambda expression.
Boolean isFamilyBirthday = date.query(FamilyBirthdays::isFamilyBirthday);
if (isFamilyVacation.booleanValue() || isFamilyBirthday.booleanValue())
System.out.printf("%s is an important date!%n", date);
else
System.out.printf("%s is not an important date.%n", date);
[[Period和Duration]]