Java 时间类

Java 时间类在 Java 8 发生变化本记录基于 Java 8 时间类

新时间类和 Date 对比

  1. Date 和 SimpleDateFormatter 非线程安全,而 LocalDate 和 LocalTime 和 String 一样,是final类型 - 线程安全且不能被修改

  2. Date 月份从 0 开始,一月是 0,十二月是 11。LocalDate 月份和星期都改成了 enum ,不会再用错。

  3. Date 是一个“万能接口”,它包含日期、时间,还有毫秒数。如果你只需要日期或时间那么有一些数据就没啥用。在新的 Java 8 中,日期和时间被明确划分为 LocalDate 和 LocalTime,LocalDate 无法包含时间,LocalTime 无法包含日期。当然,LocalDateTime 才能同时包含日期和时间。

  4. Date 推算时间(比如往前推几天/ 往后推几天/ 计算某年是否闰年/ 推算某年某月的第一天、最后一天、第一个星期一等等)要结合 Calendar 要写好多代码。

新时间类

Instant 类

Instant 类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,之后学习的类型转换中,均可以使用 Instant 类作为中间类完成转换

演示

// 获取当前时间戳
Instant instant1 = Instant.now();
// 秒
System.out.println(instant1.getEpochSecond());
// 毫秒
System.out.println(instant1.toEpochMilli());

// 以指定时间戳创建Instant
Instant instant2 = Instant.ofEpochSecond(1637406017);

// 构造 ZonedDateTime 类
ZonedDateTime zonedDateTime = instant2.atZone(ZoneId.systemDefault());
System.out.println(zonedDateTime);

Duration 类

Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性

Duration 的内部实现与 Instant 类似,也是包含两部分:seconds 表示秒,nanos 表示纳秒。两者的区别是 Instant 用于表示一个时间戳(或者说是一个时间点),而 Duration 表示一个时间段,所以 Duration 类中 不包含 now() 静态方法。可以通过 Duration.between() 方法创建 Duration 对象

// 构造 ZonedDateTime 类
ZonedDateTime start = Instant.ofEpochSecond(1637406017).atZone(ZoneId.systemDefault());
ZonedDateTime end = Instant.now().atZone(ZoneId.systemDefault());
// 求两个时间戳相差多少时间
Duration between = Duration.between(start, end);
System.out.println(between.getSeconds());

Period 类

Period类表示一段时间的 年、月、日

ZonedDateTime start = Instant.ofEpochSecond(1229105017).atZone(ZoneId.systemDefault());
ZonedDateTime end = Instant.now().atZone(ZoneId.systemDefault());
Period period = Period.between(start.toLocalDate(), end.toLocalDate());
System.out.printf("相差%d年%d月%d日\n", period.getYears(), period.getMonths(), period.getDays());

运行结果

相差12年11月7日

LocalDate 类

LocalDate是一个 不可变 的日期时间对象,表示日期,通常被视为年-月-日

LocalDate localDate = LocalDate.from(Instant.ofEpochSecond(1229105017)
                        .atZone(ZoneId.systemDefault()));
int year = localDate.getYear();                     // 年份:2008
Month month = localDate.getMonth();                 // 月份:DECEMBER
int dayOfMonth = localDate.getDayOfMonth();         // 月份中的第几天:13
DayOfWeek dayOfWeek = localDate.getDayOfWeek();     // 一周的第几天:SATURDAY
int length = localDate.lengthOfMonth();             // 月份的天数:31
boolean leapYear = localDate.isLeapYear();          // 是否为闰年:true

LocalTime 类

LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度

LocalTime localTime = LocalTime.of(17, 23, 52);     // 初始化一个时间:17:23:52
int hour = localTime.getHour();                     // 时:17
int minute = localTime.getMinute();                 // 分:23
int second = localTime.getSecond();                 // 秒:52

LocalDateTime 类

LocalDateTime类是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日-时-分-秒

LocalDateTime dateTime = LocalDateTime.now();
Month month = dateTime.getMonth();             // 返回 Month 对象 NOVEMBER
int monthValue = dateTime.getMonthValue();     // 返回月份数字 11
int dayOfYear = dateTime.getDayOfYear();       // 返回这一天在这一年的第几天 324
long secondOfDay = dateTime.getLong(
    ChronoField.SECOND_OF_DAY);                // 在这一天多少秒 82609

ZonedDateTime 类

ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间

LocalDateTime localDateTime = LocalDateTime.now();

ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("+08:00"));

System.out.println(zonedDateTime);

转换

LocalDateTime LocalTime LocalDate 互转

// LocalDateTime to LocalDate

LocalDate date = localDateTime.toLocalDate();

// LocalDateTime to LocalTime

LocalTime time = localDateTime.toLocalTime();

// LocalDate to LocalDateTime

LocalDateTime dateTime = localDate.atStartOfDay()
      .atZone(ZoneId.systemDefault()).toLocalDateTime();

// LocalTime to LocalDateTime 因为没有提供日期默认是 1970-01-01

Instant.ofEpochSecond(localTime.getSecond()).atZone(ZoneId.systemDefault()).toLocalDateTime()

// LocalDate,LocalTime to LocalDateTime

LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), 					LocalTime.now());

操作

minus 减法操作

方法签名

public LocalDateTime minus(long amountToSubtract, TemporalUnit unit);

default Temporal minus(TemporalAmount amount);

描述

返回减去指定时间的该日期时间的副本

1637422333798image-20211120233200791.png

plus 加法操作

方法签名

Temporal plus(long amountToAdd, TemporalUnit unit);

Temporal plus(TemporalAmount amount);

描述

返回加上指定时间的该日期时间的副本

1637422584794image-20211120233622365.png

with 设置值操作

方法签名

Temporalemporal with(TemporalField field, long newValue);

default Temporal with(TemporalAdjuster adjuster);

描述

指定项(年/月/日/时/分/秒)设置指定值

1637423108766image-20211120234506956.png

演示

// 设置年为 2020 年

LocalDateTime dateTime = localDateTime.withYear(2020);

// 本月第一天

LocalDateTime with = LocalDateTime.now()
   .with(TemporalAdjusters.firstDayOfMonth());

//本月最后一天

LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());

until 两个时间的间隔

方法签名

long until(Temporal endExclusive, TemporalUnit unit);

演示

long seconds = dateTime.until(LocalDateTime.now(), ChronoUnit.SECONDS);

辅助类

ChronoUnit 类

public enum ChronoUnit implements TemporalUnit {
    /**
	* 纳秒概念的单位,最小支持的时间单位。对于ISO日历系统,它等于第二个单位的第1,000,000,000个部分。
	*/
    NANOS("Nanos", Duration.ofNanos(1)),
    /**
	* 微秒概念的单位。对于ISO日历系统,它等于第二个单位的百万分之一。
	*/
    MICROS("Micros", Duration.ofNanos(1000)),
    /**
	* 代表毫秒概念的单位。对于ISO日历系统,它等于第二个单位的千分之一。
	*/
    MILLIS("Millis", Duration.ofNanos(1000_000)),
    /**
	* 代表秒的概念的单位。对于ISO日历系统,除around秒外,它等于SI单位制中的秒。
	*/
    SECONDS("Seconds", Duration.ofSeconds(1)),
    /**
	* 代表分钟概念的单位。对于ISO日历系统,它等于60秒。
	*/
    MINUTES("Minutes", Duration.ofSeconds(60)),
    /**
	* 代表小时概念的单位。对于ISO日历系统,它等于60分钟。
	*/
    HOURS("Hours", Duration.ofSeconds(3600)),
    /*
	* 代表半天概念的单位,用于AM / PM。对于ISO日历系统,它等于12小时。
	*/
    HALF_DAYS("HalfDays", Duration.ofSeconds(43200)),
    /**
	* 代表一天的概念的单位。对于ISO日历系统,这是从午夜到午夜的标准日期。估计一天的持续时间为24小时。
    * 与其他日历系统一起使用时,它必须与地球上太阳升起和落下所定义的日期相对应。不必将日期从午夜开始-在日       * 历系统之间进行转换时,日期应等于午夜。
	*/
    DAYS("Days", Duration.ofSeconds(86400)),
    /**
	* 代表一周概念的单位。对于ISO日历系统,它等于7天。
	* 与其他日历系统一起使用时,它必须对应整数天
	*/
    WEEKS("Weeks", Duration.ofSeconds(7 * 86400L)),
    /**
	* 代表一个月概念的单位。对于ISO日历系统,月份的长度因一年中的月份而异。估计的持续时间为365.2425天的十     * 二分之一。
    * 与其他日历系统一起使用时,它必须对应整数天。
	*/
    MONTHS("Months", Duration.ofSeconds(31556952L / 12)),
    /**
	* 代表一年概念的单位。对于ISO日历系统,它等于12个月。一年的估计持续时间为365.2425天。
    * 当与其他日历系统一起使用时,它必须对应于整数天或月,大约等于地球绕太阳公转所定义的一年。
	*/
    YEARS("Years", Duration.ofSeconds(31556952L)),
    /**
	* 代表十年概念的单位。对于ISO日历系统,它等于10年。
    * 与其他日历系统一起使用时,它必须对应整数天,通常是整数年。
	*/
    DECADES("Decades", Duration.ofSeconds(31556952L * 10L)),
    /**
	* 代表一个世纪概念的单位。对于ISO日历系统,它等于100年。
    * 与其他日历系统一起使用时,它必须对应整数天,通常是整数年。
	*/
    CENTURIES("Centuries", Duration.ofSeconds(31556952L * 100L)),
    /**
	* 代表千年概念的单位。对于ISO日历系统,它等于1000年。
    * 与其他日历系统一起使用时,它必须对应整数天,通常是整数年。
	*/
    MILLENNIA("Millennia", Duration.ofSeconds(31556952L * 1000L)),
    /**
	* 代表时代概念的单位。 ISO日历系统没有纪元,因此无法在日期或日期时间中添加纪元。
	* 人为地将时代的估计持续时间定义为1,000,000,000年。
    * 与其他日历系统一起使用时,该装置没有任何限制。
	*/
    ERAS("Eras", Duration.ofSeconds(31556952L * 1000_000_000L)),
    /**
	* 代表永恒概念的人造单位。这主要与TemporalField一起使用,以表示无限制的字段,
	* 例如年份或时代。人为地将时代的估计持续时间定义为“持续时间”支持的最大持续时间。
	*/
    FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999_999_999));

}

TemporalAdjusters 类

时间调节器: 是修改时间对象的一个关键工具。它们的存在是为了将调整的过程外部化,根据策略设计模式,允许不同的方法。

使用不同方式调节 Temporal 对象而与 Temporal 实现无关

有两种使用TemporalAdjuster的同等方式。

第一种是直接调用接口上的方法。第二种是使用Temporal.with(TemporalAdjuster)。

API 手册

/**
 * 当前月的第一天
 */
public static TemporalAdjuster firstDayOfMonth();
/**
 * 当月的最后一天
 */
public static TemporalAdjuster lastDayOfMonth();
/**
 * 下个月第一天
 */
public static TemporalAdjuster firstDayOfNextMonth();
/**
 * 这年第一天
 */
public static TemporalAdjuster firstDayOfYear();
/**
 * 这年最后一天
 */
public static TemporalAdjuster lastDayOfYear();
/**
 * 明年第一天 明年元旦
 */
public static TemporalAdjuster firstDayOfNextYear();
/**
 * 返回月份调整器中的第一个,它在同一个月中返回第一个匹配的星期几的新日期。
 */
public static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek);
/**
 * 返回“最后一天”调整器,它返回设置为当前月份最后一天的新日期。
 */
public static TemporalAdjuster lastDayOfMonth();