Java对日期,日历及时间的处理一直以来都饱受诟病,尤为是它决定将java.util.Date
定义为可修改的以及将SimpleDateFormat
实现成非线程安全的。java
关于这个新的时间日期库的最大的优势就在于它定义清楚了时间日期相关的一些概念,比方说,瞬时时间(Instant),持续时间(duration),日期(date),时间(time),时区(time-zone)以及时间段(Period)。同时它也借鉴了Joda
库的一些优势,好比将人和机器对时间日期的理解区分开的。Java 8仍然延用了ISO的日历体系,而且与它的前辈们不一样,java.time
包中的类是不可变且线程安全的。web
首先认识下Java8新日期和时间的一些关键类:安全
类名 | 说明 |
---|---|
Instant | 表明时间戳 |
LocalDate | 不包含具体时间的日期 |
LocalTime | 不包含日期的时间 |
LocalDateTime | 包含了日期及时间,没有时区信息 |
ZonedDateTime | 包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的 |
DateTimeFormatter | 日期解析和格式化工具类 |
接下来从几个示例中认识Java8新日期和时间的特别之处,很强大多线程
在Java8中获取当前日期编辑器
Java 8中有一个叫LocalDate的类,它能用来表示今天的日期。这个类与java.util.Date略有不一样,由于它只包含日期,没有时间。所以,若是你只须要表示日期而不包含时间,就可使用它。工具
LocalDate today = LocalDate.now();
System.out.println("Today is : " + today);
输出结果:学习
Today is : 2020-12-13
从输出结果中能够看到,日期是格式化完了后再输出来的,不像以前的Date类那样,打印出来的数据都是未经格式化的,不便于阅读。url
在Java8中获取当前的年月日spa
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);
输出结果:
Year : 2020 , Month : 12 , day : 13
在Java8中获取某个特定的日期
使用工厂方法LocalDate.of(),则能够建立出任意一个日期,它接受年月日的参数,而后返回一个等价的LocalDate实例。关于这个方法还有一个好消息就是它没有再犯以前API中的错,比方说,年只能从1900年开始,月必须从0开始,等等。这里的日期你写什么就是什么,好比说,下面这个例子中它表明的就是1月14日,没有什么隐藏逻辑
LocalDate today = LocalDate.of(2020, 12, 13);
System.out.println("Today is : " + today);
输出结果:
Today is : 2020-12-13
在Java8中检查两个日期是否相等
LocalDate重写了equals方法来进行日期的比较
LocalDate today = LocalDate.now();
LocalDate date = LocalDate.of(2014, 01, 14);
if(date.equals(today)){
System.out.printf("Today %s and date %s are same date %n", today, date);
}
输出结果:
Today 2020-12-13 and date 2020-12-13 are same date
在Java8中检查重复事件
使用MonthDay类。这个类由月日组合,不包含年信息,也就是说你能够用它来表明每一年重复出现的一些日子。固然也有一些别的组合,好比说YearMonth类。它和新的时间日期库中的其它类同样也都是不可变且线程安全的,而且它仍是一个值类(value class)
LocalDate today = LocalDate.now();
LocalDate dateOfBirth = LocalDate.of(2020, 12, 13);
MonthDay birthday = MonthDay.of(dateOfBirth.getMonth(), dateOfBirth.getDayOfMonth());
MonthDay currentMonthDay = MonthDay.from(today);
if (currentMonthDay.equals(birthday)) {
System.out.println("Oh, today is your birthday");
} else {
System.out.println("Sorry, today is not your birthday");
}
输出结果:
Oh, today is your birthday
在Java8中获取当前时间
使用LocalTime的类,它是没有日期的时间,与LocalDate是近亲。这里你也能够用静态工厂方法now()来获取当前时间。默认的格式是hh:mm:ss:nnn,这里的nnn是纳秒
LocalTime time = LocalTime.now();
System.out.println("local time now : " + time);
输出结果:
local time now : 13:44:48.255
在Java8中增长小时数
Java 8不只提供了不可变且线程安全的类,它还提供了一些更方便的方法譬如plusHours()来替换原来的add()方法。顺便说一下,这些方法返回的是一个新的LocalTime实例的引用,由于LocalTime是不可变的,可别忘了存储好这个新的引用。
LocalTime time = LocalTime.now();
LocalTime newTime = time.plusHours(2);
System.out.println("Time after 2 hours : " + newTime);
输出结果:
Time after 2 hours : 15:47:00.787
在Java8中获取1周后的日期
LocalDate是用来表示无时间的日期的,它有一个plus()方法能够用来增长日,星期,或者月,ChronoUnit则用来表示这个时间单位。因为LocalDate也是不可变的,所以任何修改操做都会返回一个新的实例,所以别忘了保存起来。
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
System.out.println("Today is : " + today);
System.out.println("Date after 1 week : " + nextWeek);
输出结果:
Today is : 2020-12-13
Date after 1 week : 2020-12-20
在Java8中获取一年先后的日期
使用LocalDate的plus()方法来给日期增长日,周或者月,如今咱们来学习下如何用minus()方法来找出一年前的那天。
LocalDate today = LocalDate.now();
LocalDate previousYear = today.minus(1, ChronoUnit.YEARS);
System.out.println("Date before 1 year : " + previousYear);
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("Date after 1 year : " + nextYear);
输出结果:
Date before 1 year : 2019-12-13
Date after 1 year : 2021-12-13
在Java8中使用时钟
Java 8中自带了一个Clock类,你能够用它来获取某个时区下当前的瞬时时间,日期或者时间。能够用Clock来替代System.currentTimeInMillis()与 TimeZone.getDefault()方法,若是你须要对不一样时区的日期进行处理的话这是至关方便的。
// Returns the current time based on your system clock and set to UTC.
Clock clock1 = Clock.systemUTC();
System.out.println("Clock : " + LocalDate.now(clock1));
// Returns time based on system clock zone Clock defaultClock =
Clock clock2 = Clock.systemDefaultZone();
System.out.println("Clock : " + LocalDate.now(clock2));
输出结果:
Clock : 2020-12-13
Clock : 2020-12-13
在Java8中判断一个日期在某个日期的先后
在Java 8中,LocalDate类有一个isBefore()和isAfter()方法能够用来比较两个日期。若是调用方法的那个日期比给定的日期要早的话,isBefore()方法会返回true。
LocalDate today = LocalDate.now();
LocalDate tomorrow = LocalDate.of(2020, 12, 14);
if (tomorrow.isAfter(today)) {
System.out.println("Tomorrow comes after today");
}
LocalDate yesterday = today.minus(1, ChronoUnit.DAYS);
if (yesterday.isBefore(today)) {
System.out.println("Yesterday is day before today");
}
输出结果:
Tomorrow comes after today
Yesterday is day before today
在Java8中处理不一样的时区
Java 8不只将日期和时间进行了分离,同时还有时区。如今已经有好几组与时区相关的类了,好比ZonId表明的是某个特定的时区,而ZonedDateTime表明的是带时区的时间。
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime dateAndTimeInNewYork = ZonedDateTime.of(localDateTime, ZoneId.of("America/New_York"));
System.out.println("Current date and time in a particular timezone : " + dateAndTimeInNewYork);
输出结果:
Current date and time in a particular timezone : 2020-12-13T14:40:44.664-05:00[America/New_York]
在Java8中表示固定的日期
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(2020, Month.FEBRUARY);
System.out.printf("Your credit card expires on %s %n", creditCardExpiry);
输出结果:
Days in month year 2020-12: 31
Your credit card expires on 2020-02
在Java8中检查闰年
LocalDate类有一个isLeapYear()的方法可以返回当前LocalDate对应的那年是不是闰年。
LocalDate today = LocalDate.now();
if (today.isLeapYear()) {
System.out.println("This year is Leap year");
} else {
System.out.println("2020 is not a Leap year");
}
输出结果:
This year is Leap year
在Java8中判断两个日期之间包含多少天/月
一个常见的任务就是计算两个给定的日期之间包含多少天,多少周或者多少年。你能够用java.time.Period类来完成这个功能。
LocalDate today = LocalDate.now();
LocalDate java8Release = LocalDate.of(2021, Month.JANUARY, 13);
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 : 1
在Java8中获取当前时间戳
Instant类有一个静态的工厂方法now()能够返回当前时间戳
Instant timestamp = Instant.now();
System.out.println("instant : " + timestamp);
输出结果:
instant : 2020-12-13T07:30:55.877Z
在Java8中使用预约义的格式器来对日期进行解析/格式化
在Java 8以前,时间日期的格式化但是个技术活,咱们的好伙伴SimpleDateFormat并非线程安全的,而若是用做本地变量来格式化的话又显得有些笨重。多亏了线程本地变量,这使得它在多线程环境下也算有了用武之地。此次它引入了一个全新的线程安全的日期与时间格式器。它还自带了一些预约义好的格式器,包含了经常使用的日期格式。
String date = "20201213";
LocalDate formatted = LocalDate.parse(date, DateTimeFormatter.BASIC_ISO_DATE);
System.out.printf("Date generated from String %s is %s %n", date, formatted);
输出结果:
Date generated from String 20201213 is 2020-12-13
在Java8中使用自定义的格式器来解析日期
在DateTimeFormatter的ofPattern静态方法()传入任何的模式,它会返回一个实例,这个模式的字面量与前例中是相同的。好比说M仍是表明月,而m还是分。无效的模式会抛出DateTimeParseException异常
String goodFriday = "12 13 2020";
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM dd yyyy");
LocalDate holiday = LocalDate.parse(goodFriday, formatter);
System.out.printf("Successfully parsed String %s, date is %s%n", goodFriday, holiday);
} catch (DateTimeParseException ex) {
ex.printStackTrace();
}
输出结果:
Successfully parsed String 12 13 2020, date is 2020-12-13
在Java8中对日期进行格式化,转换成字符串
使用DateTimeFormatter类的实例,调用它的format()方法。这个方法会返回一个表明当前日期的字符串,对应的模式就是传入的DateTimeFormatter实例中所定义好的。
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm a");
String landing = localDateTime.format(format);
System.out.printf("Arriving at : %s %n", landing);
输出结果:
Arriving at : 十二月 13 2020 04:13 下午
Date
与
Calendar API
中的刚好相反,那里面像
java.util.Date
以及
SimpleDateFormat
这些关键的类都不是线程安全的。