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.869219400ZlocalDate = 2022-04-15localTime = 11:18:33.880220100localDateTime = 2022-04-15T11:18:33.880220100zonedDateTime = 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 = 2022yearMonth = 2022-04monthDay = --04-15
of 方法
now()
只能按当前时间生成对象,of
可以指定时间
of 方法有很多重载,可以根据不同的参数生成对应数据
案例
public static void main(String[] args) {// 初始化 2018年8月8日 LocalDateLocalDate 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);}}UCTAsia/NicosiaAmerica/Indiana/WinamacSystemV/MST7MDTAmerica/Argentina/ComodRivadaviaAmerica/Boa_VistaAmerica/GrenadaAsia/Atyrau...
获取当前系统默认时区信息
public static void main(String[] args) {ZoneId zoneId = ZoneId.systemDefault();System.out.println("zoneId = " + zoneId);}// zoneId = Asia/Shanghai
时区信息添加
使用
localDateTime
的atZone(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-13plusDays = 2016-02-17plusWeeks = 2016-03-05plusMonths = 2016-07-13plusYears = 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
也是个接口
,LocalDate
、LocalTime
、Year
之类的东西都实现了它,都是它的实现类
Temporal 接口类型转换
- 例如,转换为
LocalDate
,可以使用LocalDate.from()
方法
案例:员工发工资日为每月15号,如果发薪日是周末,则调整为周五
- 传入一个
日期时间对象
,判断是不是 15 号
- 如果不是 15号,修改为 15 号
- 15 号是不是周末,如果是,调整为周五
/*** 员工发工资日为每月15号,如果发薪日是周末,则调整为周五*/public class PayDayAdjuster implements TemporalAdjuster {@Overridepublic 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-15realPayDay = 2018-12-14
TemporalQuery
LocalDate
、LocalTime
等都有一个query
方法,可以针对日期进行查询R query(TemporalQuery query)
是一个泛型方法,R 与 query 参数的实际类型保持一致TemporalQuery
是一个函数式接口,里面有一个R queryFrom(TemporalAccessor t)
方法TemporalAccessor
是 Temporal 的父接口- LocalDate 等都是它的派生实现类
queryFrom
的逻辑就是,传入一个日期时间对象,通过自定义逻辑返回数据
案例:计算下一个劳动节还有多少天
public class UtilDayQueryImpl implements TemporalQuery<Long> {@Overridepublic 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