除了 lambda 表达式, stream 以及几个小的改进之外, Java 8 还引入了一套全新的时间日期 API, 在本篇教程中我们将通过几个简单的任务示例来学习如何使用 Java 8 的这套 APIJava 对日期, 日历及时间的处理一直以来都饱受诟病, 尤其是它决定将 java.util.Date 定义为可修改的以及将 SimpleDateFormat 实现成非线程安全的
看来 Java 已经意识到需要为时间及日期功能提供更好的支持了, 这对已经习惯使用 Joda 时间日期库的社区而言也是件好事关于这个新的时间日期库的最大的优点就在于它定义清楚了时间日期相关的一些概念, 比方说, 瞬时时间 (Instant), 持续时间(duration), 日期(date), 时间(time), 时区(time-zone) 以及时间段 (Period) 同时它也借鉴了 Joda 库的一些优点, 比如将人和机器对时间日期的理解区分开的 Java 8 仍然延用了 ISO 的日历体系, 并且与它的前辈们不同, java.time 包中的类是不可变且线程安全的新的时间及日期 API 位于 java.time 包中, 下面是里面的一些关键的类:
Instant 它代表的是时间戳
LocalDate 不包含具体时间的日期, 比如 2014-01-14 它可以用来存储生日, 周年纪念日, 入职日期等
LocalTime 它代表的是不含日期的时间
LocalDateTime 它包含了日期及时间, 不过还是没有偏移信息或者说时区
ZonedDateTime 这是一个包含时区的完整的日期时间, 偏移量是以 UTC / 格林威治时间为基准的
新的库还增加了 ZoneOffset 及 Zoned, 可以为时区提供更好的支持有了新的 DateTimeFormatter 之后日期的格式化也变得焕然一新了随便提一句, 我是在去年这个时候 Java 正要推出这个新功能时写的这篇文章, 所以你会发现示例中的时间都还是去年的你运行下这些例子, 它们返回的值肯定都是正确的
Java 8 是如何处理时间及日期的
有人问我学习一个新库的最佳途径是什么? 我的回答是, 就是在实际项目中那样去使用它在一个真实的项目中会有各种各样的需求, 这会促使开发人员去探索和研究这个新库简言之, 只有任务本身才会真正促使你去探索及学习 java 8 的新的日期及时间 API 也是一样为了学习 Java 8 的这个新库, 这里我创建了 20 个以任务为导向的例子我们先从一个简单的任务开始, 比如说如何用 Java 8 的时间日期库来表示今天, 接着再进一步生成一个带时间及时区的完整日期, 然后再研究下如何完成一些更实际的任务, 比如说开发一个提醒类的应用, 来找出距离一些特定日期比如生日, 周日纪念日, 下一个帐单日, 下一个溢价日或者信用卡过期时间还有多少天
示例 1 如何 在 Java 8 中获取当天的日期
Java 8 中有一个叫 LocalDate 的类, 它能用来表示今天的日期这个类与 java.util.Date 略有不同, 因为它只包含日期, 没有时间因此, 如果你只需要表示日期而不包含时间, 就可以使用它
- LocalDate today = LocalDate.now();
- System.out.println("Today's Local date : " + today);
输出
Today's Local date : 2018-02-11
你可以看到它创建了今天的日期却不包含时间信息它还将日期格式化完了再输出出来, 不像之前的 Date 类那样, 打印出来的数据都是未经格式化的
示例 2 如何在 Java 8 中获取当前的年月日
LocalDate 类中提供了一些很方便的方法可以用于提取出年月日以及其它的日期属性使用这些方法, 你可以获取到任何你所需要的日期属性, 而不再需要使用 java.util.Calendar 这样的类了:
- LocalDate today = LocalDate.now();
- int year = today.getYear();
- int month = today.getMonthValue();
- int day = today.getDayOfMonth();
- System.out.printf("Year : %d Month : %d day : %d \t %n", year, month, day);
输出
- Today's Local date : 2018-02-11
- Year : 2018 Month : 2 day : 11
可以看到, 在 Java 8 中获取年月信息非常简单, 只需使用对应的 getter 方法就好了, 无需记忆, 非常直观你可以拿它和 Java 中老的获取当前年月日的写法进行一下比较
示例 3 在 Java 8 中如何获取某个特定的日期
在第一个例子中, 我们看到通过静态方法 now()来生成当天日期是非常简单的, 不过通过另一个十分有用的工厂方法 LocalDate.of(), 则可以创建出任意一个日期, 它接受年月日的参数, 然后返回一个等价的 LocalDate 实例关于这个方法还有一个好消息就是它没有再犯之前 API 中的错, 比方说, 年只能从 1900 年开始, 月必须从 0 开始, 等等这里的日期你写什么就是什么, 比如说, 下面这个例子中它代表的就是 1 月 14 日, 没有什么隐藏逻辑
- LocalDate dateOfBirth = LocalDate.of(2010, 01, 14);
- System.out.println("Your Date of birth is :" + dateOfBirth);
输出 : Your Date of birth is : 2010-01-14
可以看出, 创建出来的日期就是我们所写的那样, 2014 年 1 月 14 日
示例 4 在 Java 8 中如何检查两个日期是否相等
如果说起现实中实际的处理时间及日期的任务, 有一个常见的就是要检查两个日期是否相等你可能经常会碰到要判断今天是不是某个特殊的日子, 比如生日啊, 周年纪念日啊, 或者假期之类有的时候, 会给你一个日期, 让你检查它是不是某个日子比方说假日下面这个例子将会帮助你在 Java 8 中完成这类任务正如你所想的那样, LocalDate 重写了 equals 方法来进行日期的比较, 如下所示:
- LocalDate date1 = LocalDate.of(2014, 01, 14);
- if(date1.equals(today)){
- System.out.printf("Today %s and date1 %s are same date %n", today, date1);
- }
输出
today 2014-01-14 and date1 2014-01-14 are same date
在本例中我们比较的两个日期是相等的同时, 如果在代码中你拿到了一个格式化好的日期串, 你得先将它解析成日期然后才能比较你可以将这个例子与 Java 之前比较日期的方式进行下比较, 你会发现它真是爽多了
示例 5 在 Java 8 中如何检查重复事件, 比如说生日
在 Java 中还有一个与时间日期相关的实际任务就是检查重复事件, 比如说每月的帐单日, 结婚纪念日, 每月还款日或者是每年交保险费的日子如果你在一家电商公司工作的话, 那么肯定会有这么一个模块, 会去给用户发送生日祝福并且在每一个重要的假日给他们捎去问候, 比如说圣诞节, 感恩节, 在印度则可能是万灯节 (Deepawali) 如何在 Java 中判断是否是某个节日或者重复事件? 使用 MonthDay 类这个类由月日组合, 不包含年信息, 也就是说你可以用它来代表每年重复出现的一些日子当然也有一些别的组合, 比如说 YearMonth 类它和新的时间日期库中的其它类一样也都是不可变且线程安全的, 并且它还是一个值类 (value class) 我们通过一个例子来看下如何使用 MonthDay 来检查某个重复的日期:
- LocalDate dateOfBirth = LocalDate.of(2010, 01, 14);
- MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
- MonthDay currentMonthDay = MonthDay.from(today);
- if(currentMonthDay.equals(birthday)){
- System.out.println("Many Many happy returns of the day !!");
- }else{
- System.out.println("Sorry, today is not your birthday");
- }
输出: Many Many happy returns of the day !!
虽然年不同, 但今天就是生日的那天, 所以在输出那里你会看到一条生日祝福你可以调整下系统的时间再运行下这个程序看看它是否能提醒你下一个生日是什么时候, 你还可以试着用你的下一个生日来编写一个 JUnit 单元测试看看代码能否正确运行
示例 6 如何在 Java 8 中获取当前时间
这与第一个例子中获取当前日期非常相似这次我们用的是一个叫 LocalTime 的类, 它是没有日期的时间, 与 LocalDate 是近亲这里你也可以用静态工厂方法 now()来获取当前时间默认的格式是 hh:mm:ss:nnn, 这里的 nnn 是纳秒可以和 Java 8 以前如何获取当前时间做一下比较
- LocalTime time = LocalTime.now();
- System.out.println("local time now :" + time);
输出
local time now : 16:33:33.369 // in hour, minutes, seconds, nano seconds
可以看到, 当前时间是不包含日期的, 因为 LocalTime 只有时间, 没有日期
示例 7 如何增加时间里面的小时数
很多时候我们需要增加小时, 分或者秒来计算出将来的时间 Java 8 不仅提供了不可变且线程安全的类, 它还提供了一些更方便的方法譬如 plusHours()来替换原来的 add()方法顺便说一下, 这些方法返回的是一个新的 LocalTime 实例的引用, 因为 LocalTime 是不可变的, 可别忘了存储好这个新的引用
- LocalTime time = LocalTime.now();
- LocalTime newTime = time.plusHours(2); // adding two hours
- System.out.println("Time after 2 hours :" + newTime);
输出 :
Time after 2 hours : 18:33:33.369
可以看到当前时间 2 小时后是 16:33:33.369 现在你可以将它和 Java 中增加或者减少小时的老的方式进行下比较一看便知哪种方式更好
示例 8 如何获取 1 周后的日期
这与前一个获取 2 小时后的时间的例子类似, 这里我们将学会如何获取到 1 周后的日期 LocalDate 是用来表示无时间的日期的, 它有一个 plus()方法可以用来增加日, 星期, 或者月, ChronoUnit 则用来表示这个时间单位由于 LocalDate 也是不可变的, 因此任何修改操作都会返回一个新的实例, 因此别忘了保存起来
- LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
- System.out.println("Today is :" + today);
- System.out.println("Date after 1 week :" + nextWeek);
输出:
- Today is : 2018-01-14
- Date after 1 week : 2018-01-21
可以看到 7 天也就是一周后的日期是什么你可以用这个方法来增加一个月, 一年, 一小时, 一分钟, 甚至是十年, 查看下 Java API 中的 ChronoUnit 类来获取更多选项
示例 9 一年前后的日期
这是上个例子的续集上例中, 我们学习了如何使用 LocalDate 的 plus()方法来给日期增加日, 周或者月, 现在我们来学习下如何用 minus()方法来找出一年前的那天
- LocalDate previousYear = today.minus(1, ChronoUnit.YEARS);
- System.out.println("Date before 1 year :" + previousYear);
- LocalDate nextYear = today.plus(1, YEARS);
- System.out.println("Date after 1 year :" + nextYear);
输出:
- Date before 1 year : 2013-01-14
- Date after 1 year : 2015-01-14
可以看到现在一共有两年, 一个是 2013 年, 一个是 2015 年, 分别是 2014 的前后那年
示例 10 在 Java 8 中使用时钟
Java 8 中自带了一个 Clock 类, 你可以用它来获取某个时区下当前的瞬时时间, 日期或者时间可以用 Clock 来替代 System.currentTimeInMillis()与 TimeZone.getDefault() 方法
- // Returns the current time based on your system clock and set to UTC.
- Clock clock = Clock.systemUTC();
- System.out.println("Clock :" + clock);
- // Returns time based on system clock zone Clock defaultClock =
- Clock.systemDefaultZone();
- System.out.println("Clock :" + clock);
输出:
- Clock : SystemClock[Z]
- Clock : SystemClock[Z]
你可以用指定的日期来和这个时钟进行比较, 比如下面这样:
- public class MyClass {
- private Clock clock; // dependency inject ...
- public void process(LocalDate eventDate) {
- if(eventDate.isBefore(LocalDate.now(clock)) {
- ...
- }
- }
- }
如果你需要对不同时区的日期进行处理的话这是相当方便的
示例 11 在 Java 中如何判断某个日期是在另一个日期的前面还是后面
这也是实际项目中常见的一个任务你怎么判断某个日期是在另一个日期的前面还是后面, 或者正好相等呢? 在 Java 8 中, LocalDate 类有一个 isBefore()和 isAfter()方法可以用来比较两个日期如果调用方法的那个日期比给定的日期要早的话, isBefore()方法会返回 true
- LocalDate tomorrow = LocalDate.of(2014, 1, 15); if(tommorow.isAfter(today)){
- System.out.println("Tomorrow comes after today");
- }
- LocalDate yesterday = today.minus(1, DAYS);
- if(yesterday.isBefore(today)){
- System.out.println("Yesterday is day before today");
- }
输出:
- Tomorrow comes after today
- Yesterday is day before today
可以看到在 Java 8 中进行日期比较非常简单不需要再用像 Calendar 这样的另一个类来完成类似的任务了
示例 12 在 Java 8 中处理不同的时区
Java 8 不仅将日期和时间进行了分离, 同时还有时区现在已经有好几组与时区相关的类了, 比如 ZonId 代表的是某个特定的时区, 而 ZonedDateTime 代表的是带时区的时间它等同于 Java 8 以前的 GregorianCalendar 类使用这个类, 你可以将本地时间转换成另一个时区中的对应时间, 比如下面这个例子:
- // Date and time with timezone in Java 8 ZoneId america = ZoneId.of("America/New_York");
- LocalDateTime localtDateAndTime = LocalDateTime.now();
- ZonedDateTime dateAndTimeInNewYork = ZonedDateTime.of(localtDateAndTime, america );
- System.out.println("Current date and time in a particular timezone :" + dateAndTimeInNewYork);
输出 :
Current date and time in a particular timezone : 2014-01-14T16:33:33.373-05:00[America/New_York]
可以拿它跟之前将本地时间转换成 GMT 时间的方式进行下比较顺便说一下, 正如 Java 8 以前那样, 对应时区的那个文本可别弄错了, 否则你会碰到这么一个异常:
- Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: ASIA/Tokyo
- at java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:272)
- at java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:227)
- at java.time.ZoneRegion.ofId(ZoneRegion.java:120)
- at java.time.ZoneId.of(ZoneId.java:403)
- at java.time.ZoneId.of(ZoneId.java:351)
示例 13 如何表示固定的日期, 比如信用卡过期时间
正如 MonthDay 表示的是某个重复出现的日子的, YearMonth 又是另一个组合, 它代表的是像信用卡还款日, 定期存款到期日, options 到期日这类的日期你可以用这个类来找出那个月有多少天, lengthOfMonth()这个方法返回的是这个 YearMonth 实例有多少天, 这对于检查 2 月到底是 28 天还是 29 天可是非常有用的
- YearMonth currentYearMonth = YearMonth.now(); System.out.printf("Days in month year %s: %d%n", currentYearMonth, currentYearMonth.lengthOfMonth());
- YearMonth creditCardExpiry = YearMonth.of(2018, Month.FEBRUARY);
- System.out.printf("Your credit card expires on %s %n", creditCardExpiry);
输出:
- Days in month year 2014-01: 31
- Your credit card expires on 2018-02
示例 14 如何在 Java 8 中检查闰年
这并没什么复杂的, LocalDate 类有一个 isLeapYear()的方法能够返回当前 LocalDate 对应的那年是否是闰年如果你还想重复造轮子的话, 可以看下这段代码, 这是纯用 Java 编写的判断某年是否是闰年的逻辑
- if(today.isLeapYear()){
- System.out.println("This year is Leap year");
- }else {
- System.out.println("2018 is not a Leap year");
- }
输出: 2018 is not a Leap year
你可以多检查几年看看结果是否正确, 最好写一个单元测试来对正常年份和闰年进行下测试
示例 15 两个日期之间包含多少天, 多少个月
还有一个常见的任务就是计算两个给定的日期之间包含多少天, 多少周或者多少年你可以用 java.time.Period 类来完成这个功能在下面这个例子中, 我们将计算当前日期与将来的一个日期之前一共隔着几个月
- LocalDate java8Release = LocalDate.of(2014, Month.MARCH, 14);
- Period periodToNextJavaRelease =
- Period.between(today, java8Release);
- System.out.println("Months left between today and Java 8 release :" + periodToNextJavaRelease.getMonths() );
输出:
Months left between today and Java 8 release : 2
可以看到, 本月是 1 月, 而 Java 8 的发布日期是 3 月, 因此中间隔着 2 个月
示例 16 带时区偏移量的日期与时间
在 Java 8 里面, 你可以用 ZoneOffset 类来代表某个时区, 比如印度是 GMT 或者 UTC5:30, 你可以使用它的静态方法 ZoneOffset.of()方法来获取对应的时区只要获取到了这个偏移量, 你就可以拿 LocalDateTime 和这个偏移量创建出一个 OffsetDateTime
- LocalDateTime datetime = LocalDateTime.of(2014, Month.JANUARY, 14, 19, 30);
- ZoneOffset offset = ZoneOffset.of("+05:30");
- OffsetDateTime date = OffsetDateTime.of(datetime, offset);
- System.out.println("Date and Time with timezone offset in Java :" + date);
输出 :
Date and Time with timezone offset in Java : 2014-01-14T19:30+05:30
可以看到现在时间日期与时区是关联上了还有一点就是, OffSetDateTime 主要是给机器来理解的, 如果是给人看的, 可以使用 ZoneDateTime 类
示例 17 在 Java 8 中如何获取当前时间戳
如果你还记得在 Java 8 前是如何获取当前时间戳的, 那现在这简直就是小菜一碟了 Instant 类有一个静态的工厂方法 now()可以返回当前时间戳, 如下:
- Instant timestamp = Instant.now();
- System.out.println("What is value of this instant" + timestamp);
输出 :
What is value of this instant 2014-01-14T08:33:33.379Z
可以看出, 当前时间戳是包含日期与时间的, 与 java.util.Date 很类似, 事实上 Instant 就是 Java 8 前的 Date, 你可以使用这两个类中的方法来在这两个类型之间进行转换, 比如 Date.from(Instant)是用来将 Instant 转换成 java.util.Date 的, 而 Date.toInstant()是将 Date 转换成 Instant 的
示例 18 如何在 Java 8 中使用预定义的格式器来对日期进行解析 / 格式化
在 Java 8 之前, 时间日期的格式化可是个技术活, 我们的好伙伴 SimpleDateFormat 并不是线程安全的, 而如果用作本地变量来格式化的话又显得有些笨重多亏了线程本地变量, 这使得它在多线程环境下也算有了用武之地, 但 Java 维持这一状态也有很长一段时间了这次它引入了一个全新的线程安全的日期与时间格式器它还自带了一些预定义好的格式器, 包含了常用的日期格式比如说, 本例 中我们就用了预定义的 BASIC_ISO_DATE 格式, 它会将 2014 年 2 月 14 日格式化成 20140114
- String dayAfterTommorrow = "20140116";
- LocalDate formatted = LocalDate.parse(dayAfterTommorrow,
- DateTimeFormatter.BASIC_ISO_DATE);
- System.out.printf("Date generated from String %s is %s %n", dayAfterTommorrow, formatted);
输出 :
Date generated from String 20140116 is 2014-01-16
你可以看到生成的日期与指定字符串的值是匹配的, 就是日期格式上略有不同
示例 19 如何在 Java 中使用自定义的格式器来解析日期
在上例中, 我们使用了内建的时间日期格式器来解析日期字符串当然了, 预定义的格式器的确不错但有时候你可能还是需要使用自定义的日期格式, 这个时候你就得自己去创建一个自定义的日期格式器实例了下面这个例子中的日期格式是 MMM dd yyyy 你可以给 DateTimeFormatter 的 ofPattern 静态方法 () 传入任何的模式, 它会返回一个实例, 这个模式的字面量与前例中是相同的比如说 M 还是代表月, 而 m 仍是分无效的模式会抛出 DateTimeParseException 异常, 但如果是逻辑上的错误比如说该用 M 的时候用成 m, 这样就没办法了
- String goodFriday = "Apr 18 2014";
- try {
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
- LocalDate holiday = LocalDate.parse(goodFriday, formatter);
- System.out.printf("Successfully parsed String %s, date is %s%n", goodFriday, holiday);
- } catch (DateTimeParseException ex) {
- System.out.printf("%s is not parsable!%n", goodFriday);
- ex.printStackTrace();
- }
输出 :
Successfully parsed String Apr 18 2014, date is 2014-04-18
可以看到日期的值与传入的字符串的确是相符的, 只是格式不同
示例 20 如何在 Java 8 中对日期进行格式化, 转换成字符串
在上两个例子中, 尽管我们用到了 DateTimeFormatter 类但我们主要是进行日期字符串的解析在这个例子中我们要做的事情正好相反这里我们有一个 LocalDateTime 类的实例, 我们要将它转换成一个格式化好的日期串这是目前为止 Java 中将日期转换成字符串最简单便捷的方式了下面这个例子将会返回一个格式化好的字符串与前例相同的是, 我们仍需使用指定的模式串去创建一个 DateTimeFormatter 类的实例, 但调用的并不是 LocalDate 类的 parse 方法, 而是它的 format()方法这个方法会返回一个代表当前日期的字符串, 对应的模式就是传入的 DateTimeFormatter 实例中所定义好的
- LocalDateTime arrivalDate = LocalDateTime.now();
- try {
- DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm a");
- String landing = arrivalDate.format(format);
- System.out.printf("Arriving at : %s %n", landing);
- } catch (DateTimeException ex) {
- System.out.printf("%s can't be formatted!%n", arrivalDate);
- ex.printStackTrace();
- }
- Output : Arriving at : Jan 14 2014 04:33 PM
可以看到, 当前时间是用给定的 MMM dd yyyy hh:mm a 模式来表示的, 它包含了三个字母表示的月份以及用 AM 及 PM 来表示的时间
Java 8 中日期与时间 API 的几个关键点
看完了这些例子后, 我相信你已经对 Java 8 这套新的时间日期 API 有了一定的了解了现在我们来回顾下关于这个新的 API 的一些关键的要素
它提供了 javax.time.ZoneId 用来处理时区
它提供了 LocalDate 与 LocalTime 类
Java 8 中新的时间与日期 API 中的所有类都是不可变且线程安全的, 这与之前的 Date 与 Calendar API 中的恰好相反, 那里面像 java.util.Date 以及 SimpleDateFormat 这些关键的类都不是线程安全的
新的时间与日期 API 中很重要的一点是它定义清楚了基本的时间与日期的概念, 比方说, 瞬时时间, 持续时间, 日期, 时间, 时区以及时间段它们都是基于 ISO 日历体系的
每个 Java 开发人员都应该至少了解这套新的 API 中的这五个类:
Instant 它代表的是时间戳, 比如 2014-01-14T02:20:13.592Z, 这可以从 java.time.Clock 类中获取, 像这样: Instant current = Clock.system(ZoneId.of(Asia/Tokyo)).instant();
LocalDate 它表示的是不带时间的日期, 比如 2014-01-14 它可以用来存储生日, 周年纪念日, 入职日期等
LocalTime 它表示的是不带日期的时间
LocalDateTime 它包含了时间与日期, 不过没有带时区的偏移量
ZonedDateTime 这是一个带时区的完整时间, 它根据 UTC / 格林威治时间来进行时区调整
这个库的主包是 java.time, 里面包含了代表日期, 时间, 瞬时以及持续时间的类它有两个子 package, 一个是 java.time.foramt, 这个是什么用途就很明显了, 还有一个是 java.time.temporal, 它能从更低层面对各个字段进行访问
时区指的是地球上共享同一标准时间的地区每个时区都有一个唯一标识符, 同时还有一个地区 / 城市 (Asia/Tokyo) 的格式以及从格林威治时间开始的一个偏移时间比如说, 东京的偏移时间就是 + 09:00
OffsetDateTime 类实际上包含了 LocalDateTime 与 ZoneOffset 它用来表示一个包含格林威治时间偏移量 (+/- 小时: 分, 比如 + 06:00 或者 -08:00) 的完整的日期 (年月日) 及时间(时分秒, 纳秒)
DateTimeFormatter 类用于在 Java 中进行日期的格式化与解析与 SimpleDateFormat 不同, 它是不可变且线程安全的, 如果需要的话, 可以赋值给一个静态变量 DateTimeFormatter 类提供了许多预定义的格式器, 你也可以自定义自己想要的格式当然了, 根据约定, 它还有一个 parse()方法是用于将字符串转换成日期的, 如果转换期间出现任何错误, 它会抛出 DateTimeParseException 异常类似的, DateFormatter 类也有一个用于格式化日期的 format()方法, 它出错的话则会抛出 DateTimeException 异常
再说一句, MMM d yyyy 与 MMm dd yyyy 这两个日期格式也略有不同, 前者能识别出 Jan 2 2014 与 Jan 14 2014 这两个串, 而后者如果传进来的是 Jan 2 2014 则会报错, 因为它期望月份处传进来的是两个字符为了解决这个问题, 在天为个位数的情况下, 你得在前面补 0, 比如 Jan 2 2014 应该改为 Jan 02 2014
关于 Java 8 这个新的时间日期 API 就讲到这了这几个简短的示例 对于理解这套新的 API 中的一些新增类已经足够了由于它是基于实际任务来讲解的, 因此后面再遇到 Java 中要对时间与日期进行处理的工作时, 就不用再四处寻找了我们学习了如何创建与修改日期实例我们还了解了纯日期, 日期加时间, 日期加时区的区别, 知道如何比较两个日期, 如何找到某天到指定日期比如说下一个生日, 周年纪念日或者保险日还有多少天我们还学习了如何在 Java 8 中用线程安全的方式对日期进行解析及格式化, 而无需再使用线程本地变量或者第三方库这种取巧的方式新的 API 能胜任任何与时间日期相关的任务
来源: https://juejin.im/post/5abc9d2bf265da23953111cf