传统时间 API
Date 类
-
Date 类在 Java 面世之初就存在于标准库中,但从 Java 1.1 开始,Date 类的所有方法都被弃用
-
使用示例
// 获取当前时间,toString: Wed Jan 01 00:00:00 CST 2020 Date date = new Date(); // 对应的时间戳,long 类型 long stamp = d.getTime();
Calendar 类
-
Java 1.1 推荐使用 Calendar 类处理时间
-
使用示例
// 获取 Calendar 实例,默认当前时间 Calendar calendar = Calendar.getInstance(); // 通过 setTime() 设置 Calendar 对象的时间 calendar.setTime(new Date()); // 通过 getTime() 获取对应的 Date 对象 Date date = calendar.getTime(); // 通过年/月/日/时/分/秒设置时间,month 由 0 开始 calendar.set(2020, 1, 1); calendar.set(2020, 1, 1, 8, 30, 30); // 通过 set(int field, int value) 设置时间字段 calendar.set(Calendar.MONTH, 0); // 通过 get(int field) 获取对应时间字段 int month = calendar.get(Calendar.MONTH) + 1; // 通过 after 和 before 方法比较 Calendar 对象的先后,返回布尔值 Calendar oldCalendar = Calendar.getInstance(); oldCalendar.set(2019, 1, 1, 8, 30, 30); boolean after = calendar.after(oldCalendar);
存在的问题
-
时间计算困难
毫秒值与日期直接转换比较繁琐,通过毫秒值来计算时间差步骤较多
-
线程安全问题
SimpleDateFormat 类是线程不安全的,在多线程的情况下,全局共享一个SimpleDateFormat类中的Calendar对象可能会出现异常
因为 SimpleDateFormat 类 format 方法执行时使用一个 Calendar 类型成员变量报错时间,该变量被多个线程共享时就会出问题
Java 8 新时间 API
time 包常用类
-
Instant 类
Instant类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,之后学习的类型转换中,均可以使用Instant类作为中间类完成转换
-
Duration 类
Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性
-
Period类
Period类表示一段时间的年、月、日
-
LocalDate 类
LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年-月-日
-
LocalTime 类
LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度
-
LocalDateTime 类
LocalDateTime类是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日-时-分-秒
-
ZonedDateTime 类
ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间
now 方法
-
now 方法根据当前日期或时间创建实例
-
使用示例
// 使用 now 方法创建 Instant 的实例对象 Instant instantNow = Instant.now(); // 使用 now 方法创建 LocalDate 的实例对象 LocalDate localDateNow = LocalDate.now(); // 使用 now 方法创建 LocalTime 的实例对象 LocalTime localTimeNow = LocalTime.now(); // 使用 now 方法创建 LocalDateTime 的实例对象 LocalDateTime localDateTimeNow = LocalDateTime.now(); // 使用now方法创建 ZonedDateTime 的实例对象 ZonedDateTime zonedDateTimeNow = ZonedDateTime.now(); // 祖鲁时间(格林尼治时间 GMT /国际标准时间 UTC) System.out.println("Instant:" + instantNow); // 年-月-日 yy-MM-dd System.out.println("LocalDate:" + localDateNow); // 时:分:秒 HH:mm:ss System.out.println("LocalTime:" + localTimeNow); // 年-月-日T时:分:秒 System.out.println("LocalDateTime:" + localDateTimeNow); // 年-月-日T时:分:秒+08:00[Asia/Shanghai] 表示时区 System.out.println("ZonedDateTime:" + zonedDateTimeNow); /* Instant:2020-11-26T08:12:23.426Z LocalDate:2020-11-26 LocalTime:16:12:23.473 LocalDateTime:2020-11-26T16:12:23.473 ZonedDateTime:2020-11-26T16:12:23.473+08:00[Asia/Shanghai] */ -
time 包其他类也可以使用 now 方法更精准的获取某些信息
-
Year类(表示年)
-
YearMonth类(表示年月)
-
MonthDay类(表示月日)
-
of 方法
-
of 方法根据给定的参数生成对应的日期/时间对象
-
使用示例
// 2018 年 8 月 8 日 LocalDate localDate = LocalDate.of(2018, 8, 8); // 8 点 8 分 8 秒,秒为 0 或省略则 toString 不显示 LocalTime localTime = LocalTime.of(8, 8, 8); // 2018 年 8 月 8 日 8 点 8 分 8 秒 LocalDateTime localDateTime = LocalDateTime.of(2018, 8, 8, 8, 8, 8); System.out.println("localTime: " + localTime); System.out.println("localDate: " + localDate); System.out.println("localDateTime:" + localDateTime); /* localDate: 2018-08-08 localTime: 08:08:08 localDateTime:2018-08-08T08:08:08 */
ZoneId 时区类
-
ZoneId 用于表示时区
-
ZoneId.getAvailableZoneIds 返回一个 set,里面包含了约 600 个时区,如 Asia/Shanghai
-
ZoneId.of 传入表示时区的字符串返回时区对象
-
ZonedDateTime 设置时区
// 获取 LocalDateTime LocalDateTime localDate = LocalDateTime.now(); // LocalDateTime atZone 返回 ZonedDateTime 对象 // 设置该 LocalDateTime 对象时区为上海 ZonedDateTime zonedDateTime = localDate.atZone(ZoneId.of("Asia/Shanghai")); System.out.println(zonedDateTime); // 获取时区为东京的对应时间 ZonedDateTime tokyoDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo")); System.out.println(tokyoDateTime); /* 2020-11-26T16:54:31.692+08:00[Asia/Shanghai] 2020-11-26T17:54:31.692+09:00[Asia/Tokyo] */
Month 枚举类
-
Month 月份枚举
// 使用 Month 枚举获得月份 LocalDateTime localDate = LocalDateTime.of(2018, Month.AUGUST, 8, 8, 8);注意老版本 Calendar 月份是从 0 开始,LocalDate/LocalDateTime 月份都是是从 1 开始
-
Month.of 方法
Month month = Month.of(12); System.out.println(month); // DECEMBER
plus 和 minus 方法
-
LocalDate 的 plus 方法
- LocalDate plusDay(long days) 增加天数
- LocalDate plusWeeks(long weeks) 增加周数
- LocallDate plusMonths(long months) 增加月数
- LocalDate plusYears(long years) 增加年数
-
LocalTime 的 plus 方法
- LocalTime plusNanos(long nanos) 增加纳秒
- LocalTime plusSeconds(long seconds) 增加秒
- LocalTime plusMinutes(long minutes) 增加分钟
- LocalTime plusHours(long hours) 增加小时
-
plus(TemporalAmount amoutToadd)
TemporalAmount 是一个接口,Period 是其实现类,代表一段时间
LocalDateTime localDateTime = LocalDateTime.now(); // 1 年 2 个月 3 天 Period p = Period.of(1, 2, 3); // 在 localDateTime 基础上加上 p 表示的一段时间 LocalDateTime dateTimeAfterPlus = localDateTime.plus(p); -
plus(long amountToadd, TemporalUnit unit)
TemporalUnit是一个接口,ChronoUnit 是其实现类,封装了许多时间段
LocalDateTime localDateTime = LocalDateTime.now(); // 加 1 天 LocalDateTime localDateTime1 = localDateTime.plus(1, ChronoUnit.DAYS); // 加 2 小时 LocalDateTime localDateTime2 = localDateTime.plus(2, ChronoUnit.HOURS);
with 方法
-
with 方法可以直接对时间的某一项进行修改
- LocalDateTime withNano(int i) 修改纳秒
- LocalDateTime withSecond(int i) 修改秒
- LocalDateTime withMinute(int i) 修改分支
- LocalDateTime withHour(int i) 修改小时
- LocalDateTime withDayOfMonth(int i) 修改日
- LocalDateTime withMonth(int i) 修改月
- LocalDateTime withYear(int i) 修改年
-
with(TemporalField field, long newValue)
temporalField是一个接口,ChronoField 是其实现类,提供了许多日期字段的枚举
LocalDateTime localDateTime = LocalDateTime.of(2018, 8, 8, 8, 8, 8); // 修改到 10 月 LocalDateTime localDateTimeModified = localDateTime.withMonth(10); // 修改到 10 月 LocalDateTime localDateTimeModified = localDateTime.with(ChronoField.MONTH_OF_YEAR, 10); -
with(TemporalAdjuster adjuster)
TemporalAdjuster 是一个函数式接口,可以自定义 TemporalAdjuster 实现类完成想要的修改
TemporalAdjusters 封装了许多方法,返回 TemporalAdjuster 的实现
TemporalAdjusters 的 next、previous 方法配合 DayOfWeek 枚举类,可以方便的获取下周或上周的某天
LocalDateTime localDateTime = LocalDateTime.of(2018, 8, 8, 8, 8, 8); // 改为当月第一天 LocalDateTime localDateTime1 = localDateTime.with(TemporalAdjusters.firstDayOfMonth()); // 改为下个月第一天 LocalDateTime localDateTime2 = localDateTime.with(TemporalAdjusters.firstDayOfNextMonth()); // 自定义 TemporalAdjuster,改为当月倒数第二天 LocalDateTime localDateTime3 = localDateTime.with(new TemporalAdjuster() { @Override public Temporal adjustInto(Temporal temporal) { return temporal.with(ChronoField.DAY_OF_MONTH, temporal.range(ChronoField.DAY_OF_MONTH).getMaximum() -1 ); } }); // next、previous 方法配合 DayOfWeek 枚举类,如:下一个周五 LocalDateTime localDateTime4 = localDateTime.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
query 方法
-
LocalDate、LocalTime 有一个 query 方法,用来进行对日期的查询
-
R query(TemporalQuery query) 是一个泛型方法,返回的数据是传入的泛型类的类型
-
TemporalQuery 是一个泛型接口,通过重写其 queryFrom(TemporalAccessor temporal) 方法,传入时间返回数据
LocalDate localDate = LocalDate.of(2018, 8, 8); // 返回是否过了当年的劳动节 boolean isAfterLabour = localDate.query(new TemporalQuery<Boolean>() { @Override public Boolean queryFrom(TemporalAccessor temporal) { LocalDate today = LocalDate.from(temporal); LocalDate labour = LocalDate.of(today.getYear(), 5 ,1); return today.isAfter(labour); } });
parse 和 format
-
parse 方法是 LocalDate、LocalDateTime 的静态方法,将字符串转为时间或日期对象
-
format 是 LocalDate、LocalDateTime 的实例方法,用于格式化字符串
-
DateTimeFormatter 类封装了常用的格式作为静态变量,也可使用 ofPattern 方法自定义格式
// LocalDate.parse,默认解析 yyyy-MM-dd LocalDate parsedDate1 = LocalDate.parse("2018-08-08"); // LocalDate.parse 指定格式 LocalDate parsedDate2 = LocalDate.parse("2018/11/1", DateTimeFormatter.ofPattern("yyyy/M/d")); LocalDateTime localDateTime = LocalDateTime.of(2018,8,8,8,8,8); // localDateTime 对象可以直接调用 format 方法进行格式化 // 2018-08-08 String s1 = localDateTime.format(DateTimeFormatter.ISO_DATE); // 2018-08-08T08:08:08 String s2 = localDateTime.format(DateTimeFormatter.ISO_DATE_TIME); // DateTimeFormatter.ofPattern 自定义格式 -> 2018.08.08 String s3 = localDateTime.format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));