Version: Next

基本类

Instant

  • 对时间轴单一时点建模,用于记录应用程序中事件时间戳,与老日期 API 进行转换时,Instant 可以作为中间类完成类型转换
  • 封装 祖鲁时间(格林威治时间),并非当前时间

Duration

  • 表示 纳秒 级别的 时间间隔,适合处理较短时间,精度高

Period

  • 表示一段时间的 年月日,用于计算 一年三个月十五天之后 这种东西

LocalDate

  • 不可变对象,存储 年月日

LocalTime

  • 不可变对象,存储 时分秒

LocalDateTime

  • 不可变对象,存储 年月日时分秒
  • 年月日时分秒 中间,用 T 分隔

ZonedDateTime

  • 带时区的 年月日时分秒

不可变对象

Date | Time 类 API 中的所有类均生成 不可变实例(final),因此 线程安全,并且 不提供公共构造方法,不能 new,必须采用工厂方法进行实例化

now() 工厂方法

  • 用于 实例化对象

年月日 | 时分秒

now() 方法创建实例,除了 Duration 和 Period 都可以

public static void main(String[] args) {
Instant instant = Instant.now();
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("instant = " + instant);
System.out.println("localDate = " + localDate);
System.out.println("localTime = " + localTime);
System.out.println("localDateTime = " + localDateTime);
System.out.println("zonedDateTime = " + zonedDateTime);
}
  • 可以直接 sout,因为他们都重写了 toString() 方法
instant = 2022-04-15T03:18:33.869219400Z
localDate = 2022-04-15
localTime = 11:18:33.880220100
localDateTime = 2022-04-15T11:18:33.880220100
zonedDateTime = 2022-04-15T11:18:33.880220100+08:00[Asia/Shanghai]

Year | YearMonth | MonthDay 类

  • 他们都可以用 now() 方法创造实例

案例

public static void main(String[] args) {
Year year = Year.now();
YearMonth yearMonth = YearMonth.now();
MonthDay monthDay = MonthDay.now();
// 打印
System.out.println("year = " + year);
System.out.println("yearMonth = " + yearMonth);
System.out.println("monthDay = " + monthDay);
}
year = 2022
yearMonth = 2022-04
monthDay = --04-15

of 方法

now() 只能按当前时间生成对象,of 可以指定时间

of 方法有很多重载,可以根据不同的参数生成对应数据

案例

public static void main(String[] args) {
// 初始化 2018年8月8日 LocalDate
LocalDate localDate = LocalDate.of(2018, 8, 8);
System.out.println("localDate = " + localDate);
/*
* 初始化晚上 8 点 0 分 0 秒, 24 小时计时法, LocalTime
* of 方法的重载形式 (小时, 分钟)
* of 方法的重载形式 (小时, 分钟, 秒)
* of 方法的重载形式 (小时, 分钟, 秒, 纳秒)
* */
LocalTime localTime = LocalTime.of(8, 0, 0);
System.out.println("localTime = " + localTime);
/*
* 初始化2018年8月8日下午8点0分0秒的 LocalDateTime 对象
* of 方法的重载形式 (year, month, dayOfMonth, hour, minute, sec, nanoOfSecond)
* (year, month, dayOfMonth, hour, minute)
* */
LocalDateTime localDateTime = LocalDateTime.of(
2018, 8, 8, 8, 0, 0
);
System.out.println("localDateTime = " + localDateTime);
/* 特殊形式
* LocalDateTime of(LocalDate date, LocalTime time)
* */
LocalDateTime local_Date_Time = LocalDateTime.of(localDate, localTime);
System.out.println("local_Date_Time = " + local_Date_Time);
}

为 LocalDateTime添加时区信息

时区信息获取

ZoneId 类的 getAvailableZoneIds() 返回封装了 600 个时区的 Set 集合

public static void main(String[] args) {
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
for (String zoneId : availableZoneIds) {
System.out.println(zoneId);
}
}
UCT
Asia/Nicosia
America/Indiana/Winamac
SystemV/MST7MDT
America/Argentina/ComodRivadavia
America/Boa_Vista
America/Grenada
Asia/Atyrau
...

获取当前系统默认时区信息

public static void main(String[] args) {
ZoneId zoneId = ZoneId.systemDefault();
System.out.println("zoneId = " + zoneId);
}
// zoneId = Asia/Shanghai

时区信息添加

使用 localDateTimeatZone(zoneId) 方法即可

  • zoneId 可以使用 ZoneId.of(时区字符串) 获得

  • 返回类型就是 ZonedDateTime

public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.of(2018, 11, 11, 8, 54, 38);
// 添加时区信息
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("zonedDateTime = " + zonedDateTime);
}
// zonedDateTime = 2018-11-11T08:54:38+08:00[Asia/Shanghai]

根据现有时间推算其他时区的时间

  • 封装 LocalDateTime 对象并添加时区信息
  • 更改时区信息查看对应时间
    • zonedDateTime 的 withZoneSameInstant(zoneId) 方法
public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.of(2018, 11, 11, 8, 54, 38);
// 添加时区信息
ZonedDateTime zonedDateTime1 = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("zonedDateTime1 = " + zonedDateTime1);
// 更改时区
ZonedDateTime zonedDateTime2 = zonedDateTime1.withZoneSameInstant(ZoneId.of("America/Indiana/Winamac"));
System.out.println("zonedDateTime2 = " + zonedDateTime2);
}
zonedDateTime1 = 2018-11-11T08:54:38+08:00[Asia/Shanghai]
zonedDateTime2 = 2018-11-10T19:54:38-05:00[America/Indiana/Winamac]

根据现有实例创建日期与时间对象

plus | minus 方法

LocalDate 中

  • plusDays(long days) 增加天数
  • plusWeeks(long weeks) 增加周数
  • plusMonths(long months) 增加月数
  • plusYears() 增加年数
public static void main(String[] args) {
LocalDate localDate = LocalDate.of(2016, 2, 13);
System.out.println("当前时间:" + localDate);
// 4 天后
LocalDate plusDays = localDate.plusDays(4);
System.out.println("plusDays = " + plusDays);
// 3 周后
LocalDate plusWeeks = localDate.plusWeeks(3);
System.out.println("plusWeeks = " + plusWeeks);
// 5 月后
LocalDate plusMonths = localDate.plusMonths(5);
System.out.println("plusMonths = " + plusMonths);
// 2 年后
LocalDate plusYears = localDate.plusYears(2);
System.out.println("plusYears = " + plusYears);
}
当前时间:2016-02-13
plusDays = 2016-02-17
plusWeeks = 2016-03-05
plusMonths = 2016-07-13
plusYears = 2018-02-13

LocalTime 中

  • plusNanos(long nanos) 增加纳秒
  • plusSeconds(long seconds) 增加秒
  • plusMinutes(long minutes) 增加分
  • plusHours(long hours) 增加小时

plus 单独使用

plus(TemporalAmount amountToAdd)

  • TemporalAmount 是个接口,查看它的体系结构,发现 Period 是他的实现类之一
  • 所有传递时间段进去就可以

案例 : 今天,保险还有 2 年 3 月 8 天到期,计算到期日期

public static void main(String[] args) {
LocalDate localDate = LocalDate.now();
Period period = Period.of(2, 3, 8);
LocalDate target_date = localDate.plus(period);
System.out.println("target_date = " + target_date);
}
target_date = 2024-07-23

plus(long amountToAdd, TemporalUnit unit)

  • 1 个实际、1 个半天、 1 千年, 10 年等单位
  • TemporaUnit 是一个接口,子类 ChronoUnit 封装了很多可用时间段

案例:结婚 10 年是锡婚,2020 2 2 11 11 11 结婚,计算锡婚

public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.of(2020, 2, 2, 11, 11, 11);
LocalDateTime time = localDateTime.plus(1, ChronoUnit.DECADES);
System.out.println(time);
}

with 方法

用于直接更改 年月日时分秒 的值

  • 与 plus 类似,它也有一个特殊时间 ChronoField 的用法

调节器 TemporalAdjuster 与查询 TemporalQuery

TemporalAdjusters 工具类

  • 都是 static 方法,返回的都是 TemporalAdjuster
  • firstDayOfNextMonth()
  • firstDayOfNextYear()
  • firstDayOfYear()

场景:将时间调整到 下周周日,下一个工作日,本月中的某一天等,可以用 TemporalAdjuster 调节器

例子:获取当月第一天

public static void main(String[] args) {
LocalDate now = LocalDate.now();
LocalDate with = now.with(TemporalAdjusters.firstDayOfMonth());
System.out.println(with);
}

Month 枚举 | DayOfWeek 枚举

  • 略,推荐填 月 日的地方都用枚举

自定义 TemporalAdjuster 调节器

  • 它是个接口,写实现类重写方法就可以
  • 函数式接口,里面只有方法 Temporal adjustInto(Temporal temporal)
    • 给一个 temporal 对象,返回一个 temporal 对象
    • Temporal 也是个 接口LocalDateLocalTimeYear 之类的东西都实现了它,都是它的实现类

Temporal 接口类型转换

  • 例如,转换为 LocalDate ,可以使用 LocalDate.from() 方法

案例:员工发工资日为每月15号,如果发薪日是周末,则调整为周五

  • 传入一个 日期时间对象,判断是不是 15 号
    • 如果不是 15号,修改为 15 号
  • 15 号是不是周末,如果是,调整为周五
/**
* 员工发工资日为每月15号,如果发薪日是周末,则调整为周五
*/
public class PayDayAdjuster implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
LocalDate localDate = LocalDate.from(temporal);
if (localDate.getDayOfMonth() != 15) {
localDate = localDate.withDayOfMonth(15);
}
if (localDate.getDayOfWeek() == DayOfWeek.SUNDAY || localDate.getDayOfWeek() == DayOfWeek.SATURDAY) {
localDate = localDate.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}
return localDate;
}
}
public static void main(String[] args) {
LocalDate payDay = LocalDate.of(2018, 12, 15);
System.out.println("payDay = " + payDay);
// 计算真实发薪日
LocalDate realPayDay = LocalDate.from(new PayDayAdjuster().adjustInto(payDay));
System.out.println("realPayDay = " + realPayDay);
}
payDay = 2018-12-15
realPayDay = 2018-12-14

TemporalQuery

  • LocalDateLocalTime 等都有一个 query 方法,可以针对日期进行查询
  • R query(TemporalQuery query) 是一个泛型方法,R 与 query 参数的实际类型保持一致
  • TemporalQuery 是一个函数式接口,里面有一个 R queryFrom(TemporalAccessor t) 方法
    • TemporalAccessor 是 Temporal 的父接口
    • LocalDate 等都是它的派生实现类
    • queryFrom 的逻辑就是,传入一个日期时间对象,通过自定义逻辑返回数据

案例:计算下一个劳动节还有多少天

public class UtilDayQueryImpl implements TemporalQuery<Long> {
@Override
public Long queryFrom(TemporalAccessor temporal) {
long days = 0L;
LocalDate now = LocalDate.from(temporal);
// 当年劳动节时间
LocalDate laborDay = LocalDate.of(now.getYear(), Month.MAY, 1);
// 如果已经过了今年劳动节,那就变成明年劳动节
if (now.isAfter(laborDay)) {
laborDay = laborDay.plusYears(1);
}
// 通过 ChronoUnit.between 计算差额
days = ChronoUnit.DAYS.between(now, laborDay);
return days;
}
}
public static void main(String[] args) {
LocalDate now = LocalDate.now();
Long days = new UtilDayQueryImpl().queryFrom(now);
System.out.println(days);
}
16

日期格式化

SimpleDateFormat 线程不安全

  • DateTimeFormatter
  • 提供了大量预定义格式化器,包括常量 ISO_LOCAL_DATE,模拟字母 yyyy-MM-dd 以及本地化样式
  • 不需要创建转换器对象进行转换,通过日期、时间对象的 parse / format 方法可以直接转换

LocalDate parse | format 方法

  • format 把日期、时间对象变成 字符串
  • parse字符串 解析为日期、时间对象
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
String format1 = now.format(DateTimeFormatter.ISO_DATE);
String format2 = now.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println("format1 = " + format1);
System.out.println("format2 = " + format2);
LocalDateTime fromParse = LocalDateTime.parse(format2);
System.out.println("fromParse = " + fromParse);
}

LocalTimeFormatter 之 ofLocalizedDate 方法

  • 会根据当前系统默认时区,进行区别化显示

  • 参数 FormatStyle dateStyle,是一个 枚举

    • FULL:年月日+星期
    • Long:年月日
    • Medium:没有年月日汉字
    • Short:精简年+月日
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
String fullFormatStr = now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL));
String longFormatStr = now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
String mediumFormatStr = now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
String shortFormatStr = now.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));
System.out.println("fullFormatStr = " + fullFormatStr);
System.out.println("longFormatStr = " + longFormatStr);
System.out.println("mediumFormatStr = " + mediumFormatStr);
System.out.println("shortFormatStr = " + shortFormatStr);
}

自定义格式化器

  • DateTimeFormatter.ofPattern()
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
String format = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("format = " + format);
}
format = 2022-04-15 14:23:48