在 Java8 之前,在 Java 开发过程中,如果要操作时间,是使用 Date 这个类,在 Java8 中新增了 LocalTime、LocalDate 和 LocalDateTime,日期和时间的处理变得更加方便和容易。用了一段时间,刚开始每次用的时候都要上网查一波才能找到要的答案,后来认真看一下官网的 API,发现也不是那么难理解,现在能够通过自己的理解找到自己想要的表达式,在这里做个小小的学习总结。
为什么要有新的时间 API
既然 Date 类已经存在了那么多年,为什么要花那么大的精力去做这个改动?收益是什么呢?
首先,因为 Date 类真的很难用,有很多过于 Geek 的设计,比如月份是从 0 开始,0 是一月,11 是十二月。好吧,我已经知道程序员是从 0 开始计数了,但是每次用的时候都要做各种转换,特别是跟其他输出端有交互的时候,改错一个地方就凌乱了。在新的 APIjava.time
里,这些都用常量表示了,不会用错,代码看起来也更加清晰。
在业务代码中,往往有很多种需求,获取某一天的 0 点,获取下一天的中午时间,将当前时间往前或者往后移动几天,等等这类的需求,这些功能使用 Date 类配合 Calendar 要写好多代码,代码简洁性也不够好。
另外一个,Date 里面的很多方法都弃用了,如果新的项目还敢用这些类,那就是给自己埋坑了,还是趁早改了为好。
旧的时间类 java.util.Date
和格式化类 SimpleDateFormatter
都是可变类,不是线程安全的,在多线程环境下对共享变量 Date 进行操作时,需要自己保证线程安全。而新的时间 APILocalTime
和 LocalDate
和格式化类 DateTimeFormatter
都是 final 类 (不可变) 且是线程安全的。
基于上面的这些原因,就没有理由不使用新的时间 API 了。
从一个用着习惯了很多年的工具切换到新的工具总是很不习惯,过程是痛苦的,但是结果是美好的。
怎么理解新的时间 API
在新的时间 API 里,有 LocalTime
、LocalDate
、LocalDateTime
三个类,LocalTime
只处理时间,无法包含日期,LocalDate
只处理日期,无法包含时间,只有 LocalDateTime
能同时处理日期和时间。
怎么使用?
如果理解了三个类的区别,那么在使用上,需要根据具体情况来处理,下面看看比较常遇到的场景。
LocalDate
只取日期,年月日。
获取今天的日期
LocalDate today = LocalDate.now();// 2020-05-20
使用年月日构造一个日期
LocalDate valentineDay = LocalDate.of(2020, 5, 20); // 月份和日期是从 1 开始
指定对象,获取年、月、日、星期几
int year = localDate.getYear();
Month month = localDate.getMonth();
int day = localDate.getDayOfMonth();
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
或者
int year = localDate.get(ChronoField.YEAR);
int month = localDate.get(ChronoField.MONTH_OF_YEAR);
int day = localDate.get(ChronoField.DAY_OF_MONTH);
int dayOfWeek = localDate.get(ChronoField.DAY_OF_WEEK);
LocalTime
获取时间,只会取几点几分几秒。
初始化时间对象
LocalTime localTime = LocalTime.now();
使用时分秒构造一个对象
LocalTime localTime = LocalTime.of(12, 0, 0);
指定对象,获取时分秒
LocalTime localTime = LocalTime.now();
int hour = localTime.getHour();
int minute = localTime.getMinute();
int second = localTime.getSecond();
int hour = localTime.get(ChronoField.HOUR_OF_DAY);
int minute = localTime.get(ChronoField.MINUTE_OF_HOUR);
int second = localTime.get(ChronoField.SECOND_OF_MINUTE);
LocalDateTime
获取日期 + 时间,年月日 + 时分秒,含义等于 LocalDate+LocalTime
创建时间对象
LocalDateTime localDateTime = LocalDateTime.now();
使用 LocalDate 结合 LocalTime 构造时间对象
LocalDateTime localDateTime = LocalDateTime.of(localData, localTime);
LocalDateTime localDateTime = localDate.atTime(localTime);
LocalDateTime localDateTime = localTime.atDate(localDate);
通过 LocalDateTime 获取 LocalDate 和 LocalTime
LocalDate localDate = localDateTime.toLocalDate();
LocalTime localDate = localDateTime.toLocalTime();
Instant
这个类是表示时间轴上的某一个时刻的时间点。从 1970-01-01 00:00:00 这个时间开始计算,主要用于获取时间戳,与System.currentTimeMillis()
(精确到毫秒)类似,但是 Instant 类可以精确到纳秒。
创建 Instant 对象
Instant instant = Instant.now();
使用时间戳创建 Instant 对象
Instant instant = Instant.ofEpochSecond(100, 100000);
获取秒数和毫秒
instant.getEpochSecond();
instant.getEpochMill();
举例子加深印象
介绍了这几个类的基本 API 后,再通过实现某些具体的需求来加深印象。
获取当前时间戳
// 获取当前时间对象 -> 设置时 -> 转换成时间戳
public static long getCurrentSeconds() {return Instant.now().atZone(ZoneOffset.of("+8")).toEpochSecond();}
获取当天零点 / 结束时间的时间戳
// 使用日期和时间构造日期时间对象 -> 指定时区转换成时间戳
public static long getTodayStartTime() {return LocalDateTime.of(LocalDate.now(), LocalTime.MIN).toEpochSecond(ZoneOffset.of("+8"));
}
public static long getTodayEndTime() {return LocalDateTime.of(LocalDate.now(), LocalTime.MAX).toEpochSecond(ZoneOffset.of("+8"));
}
获取前一天零点
// 使用日期和时间构造日期时间对象 -> 修改日期 -> 指定时区转换成时间戳
public static long getYesterdayStartTime() {return LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(1).toEpochSecond(ZoneOffset.of("+8"));
}
根据时间戳获取时间戳表示的时间当天 0 点
// 使用时间戳构造日期时间对象 -> 修改日期 -> 指定时区转换成时间戳
public static long getDayStartTime(long) {return LocalDateTime.of(LocalDate.now(), LocalTime.MIN).minusDays(1).toEpochSecond(ZoneOffset.of("+8"));
}
判断是否月初第一天
public static boolean isFirstDayOfMonth(String ymd) {LocalDate localDate = LocalDate.parse(ymd, DateTimeFormatter.ofPattern("yyyyMMdd"));
return localDate.equals(localDate.withDayOfMonth(1));
}
总结
Java 新的时间 API 很强大,这里只能举几个自己遇到比较多的场景给大家介绍,所讲的只是最基础最皮毛的东西,要熟练的掌握所有的细节,还是需要去看看 API 或源码的实现,然后在平时具体的需求场景中慢慢积累。
原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
如果本文对你有帮助,请点个赞吧,谢谢
更多精彩内容,请关注个人公众号。