为什么我们需要新的Date和Time类!
- Java8之前所有的日期类都是可变的,这就导致了线程不安全问题;
- java的日期和时间类的定义不一致,在java.util和java.sql中都包含日期类;
- java.util.Date同时包含日期和时间,但是java.sql中只包含日期,将其纳入java.sql中的是不合适的,而且更糟糕的是:这两个类中的日期类的名字都是一样的;
- 对于时间、时间戳、格式化及解析,没有一些明确定义的类,而且对于格式化和解析的需求,Java中有java.text.DateFormat抽象类,但是通常我们用的是SimpleDateFormate类进行格式化和解析
- 日期类不支持国际化,没有时区支持,即使Java引入了-java.util.Calendar和java.util.TimeZone类,但是问题依然存在。
Java8新的日期和时间API的设计原则
Java8中新的日期和时间是基于JSR-310实现的,参考了绝大多数的joda-timeApi。
- 不变性:新的日期/时间API中,所有的类都是不可变的,实现了线程安全;
- 关注点分离:新的API中将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类;
- 清晰明了:新的API中,方法都被明确定义用以完成相同的行为,例如,想要拿到当前实例,可以用new()方法,在所有的类方法中都实现了formate()和parse()方法,不再是之前用单独一个类去解决,而且新的API中所有的类都使用了工厂模式和策略模式;
- 实用性:所有新的日期和时间API类都实现了一系列方法用以完成通用的需求,例如:加、减、格式化、解析、从日期或时间中提取单独部分等等;
- 可扩展性:新的日期/时间API是基于国际标准ISO 8601,其实我们也是可以将其用于非IOS的日历上
Java8中的日期和时间API包含以下包
- java.time包:这是新的Java日期/时间API的基础包,所有的主要基础类都是该包的一部分,例如:LocalDate、LocalTime、LocalDateTime、Instant、Period、Duration等类。这些类都是线程安全的
- java.time.chrono包:这个包为非ISO的日历标准定义了一些泛化的API,我们可以拓展AbstractChronology来创建自己的日历标准
- java.time.formate包:这个包包含能够格式化和解析日期时间对象的类,在绝大数情况下,我们不应该直接使用它,因为java.time包中相应的类已经提供了格式化和解析的方法
- java.time.temporal包:这个包包含一些时态对象,可以用其找出关于某个日期/时间对象的某个特定日期或时间,比如说找到某个月的第一天或最后一天,可以根据withXXX的格式进行区分
- java.time.zone包:这个包支持不同的时区以及相关规则的类
Java8中引入的关于日期和时间的新类:Period和Duration类,两个类表示两个日期和时间之间的差,Period基于日期,Duration类基于时间
Period类
概述:该类表示一段时间的年、月、日
- 使用该类中between()方法获取两个日期之间的差作为Period对像返回;
// 代码示例 LocalDate startTime = LocalDate.of(2020,10,01); LocalDate endTime = LocalDate.of(2021,11,20); Period timeSub = Period.between(startTime,endTime); System.out.println("year:"+timeSub.getYears()); System.out.println("month:"+timeSub.getMonths()); System.out.println("day:"+timeSub.getDays());
- 使用该类中的isNegative()方法判断起止日期的大小
(false为endTime大于startTime,否则相反)原理是根据该该方法(不需要参数)会校验Period对象中的天、月、年是否为负,为负则返回true,为正则返回false// 代码示例 LocalDate startTime = LocalDate.of(2020,10,01); LocalDate endTime = LocalDate.of(2021,11,20); Period timeSub = Period.between(startTime,endTime); System.out.println(timeSub.isNegative());
- 使用该类的plusXXX()方法、minusXXX()方法可以进行日期的增加和减少
// 代码示例 Period periodTime = Period.of(2020,10,26); //年 String plusYears = periodTime.plusYears(1).toString(); String minusYears = periodTime.minusYears(1).toString(); //月 String plusMonths = periodTime.plusMonths(1).toString(); String minusMonths = periodTime.minusMonths(1).toString(); //日 String plusDays = periodTime.plusDays(1).toString(); String minusDays = periodTime.minusDays(1).toString();
Duration类
概述:表示秒或纳秒的时间间隔,适合处理较短的时间,且需要更高的精确性
- 使用该类中between()方法比较两个瞬间的差值
// 代码示例 Instant start = Instant.parse("2020-10-12T10:12:12.00Z"); Instant end = Instant.now(); System.out.println(end.toString()); Duration duration = Duration.between(start,end); System.out.println(duration.isNegative());
- 使用该类中的isNegative()方法判断起止时间的大小
(false为endTime大于startTime,否则相反)原理是根据该该方法(不需要参数)会校验Duration对象中的天、月、年是否为负,为负则返回true,为正则返回false// 代码示例(使用LocalTime也可以获取Duration对象) LocalTime startTime = LocalTime.of(1,10,30,234); LocalTime endTime = LocalTime.of(1,20,30,234); Duration duration = Duration.between(startTime,endTime); System.out.println(duration.isNegative());
- 使用该类的plusXXX()方法、minusXXX()方法可以进行日期的增加和减少
```java
// 代码示例(使用该类中的ofDays方法也可以获取Duration对象)
Duration durationDays = Duration.ofDays(1);
String addDays = durationDays.plusDays(1).toString();
System.out.println(durationDays.getSeconds());
Duration durationHours = Duration.ofHours(3);
String minusHours = durationHours.minusHours(1).toString();
System.out.println(durationHours.getSeconds());
Duration durationMinutes = Duration.ofMinutes(1);
System.out.println(durationMinutes.getSeconds());
Duration durationMillis = Duration.ofMillis(1);
System.out.println(durationMillis.getSeconds());
System.out.println(durationMillis.getNano());
---
#### Instant类
概述:表示某个时间,不带时区的即时时间点,精确到纳秒
内部由两个Long字段组成,第一部分保存自标准Java时代到现在的秒数,第二部分保存的是纳秒数(不会超过999,999,999)。**注意:通过这种方式获取的时间戳与北京时间相差8个时区,需要修正为北京时间,通过查看源代码发现Instant.now()使用等是UTC时间Clock.systemUTC().instant()。LocalDate、LocalDateTime 的now()方法使用的是系统默认时区 不存在Instant.now()的时间问题。**
1. Instant相当于Date,以下是相互转换
```java
// 代码示例
//类方法java.time.Instant
Instant instant = Instant.now();
System.out.println(instant);
Instant beijingTime = Instant.now().plus(8, ChronoUnit.HOURS);
System.out.println(beijingTime);
//类方法java.util.Date
Date date = new Date();
System.out.println(date);
date = Date.from(instant);
System.out.println(date);
instant = date.toInstant();
System.out.println(instant);
- Instant类中通过固定时间转换为Instant对象的方法
//根据Date转换为instant(java.util.Instant中方法) Date date = new Date(); Instant instant = Instant.ofEpochMilli(date.getTime()); System.out.println(instant); //根据毫秒转换为instant(java.util.Instant中方法) instant = Instant.ofEpochMilli(1000 * 60 * 60 * 24); System.out.println(instant); //根据秒转换为instant(java.util.Instant中方法) instant = Instant.ofEpochSecond(60 * 60 * 24 * 5); System.out.println(instant); //根据秒和纳秒转换为instant(java.util.Instant中方法),这样得到的instant会包含纳秒的数据 1000000000纳秒(9位)=1秒 instant = Instant.ofEpochSecond(60 * 60 * 24,1000000000*60); System.out.println(instant);
- Instant类中的parse方法
// 代码示例 //该方法的入参仅支持UTC格式的字符串,而且date结果的字符串会报出DateTimeParseException异常 Instant instant = Instant.parse("2020-10-26T12:10:00Z"); System.out.println(instant);
- Instant类中的加减日期方法(加)plus()和(减)minus()方法
// 代码示例 //ChronoUnit位于java.time.temporal,Periodjava位于Java.time //plus()方法会产生一个新的instant对象 //plus()方法给当前日期增加五天 Instant instant = Instant.now(); Instant instantAddDayOne = instant.plus(5, ChronoUnit.DAYS); Instant instantAddDayTwo = instant.plus(Period.ofDays(5)); Instant instantAddDayTwo1 = instant.plus(Duration.ofDays(5)); System.out.println(instantAddDayOne.toString()); System.out.println(instantAddDayTwo.toString()); System.out.println(instantAddDayTwo1.toString()); System.out.println(instant == instantAddDayOne); //minus()方法会产生一个新的instant对象 //minus()方法给当前日期减少五天 Instant instantSubDay = instant.minus(5, ChronoUnit.DAYS); Instant instantSubDayTwo = instant.minus(Period.ofDays(5)); Instant instantSubDayTwo1 = instant.minus(Duration.ofDays(5)); System.out.println(instantSubDay); System.out.println(instantSubDayTwo); System.out.println(instantSubDayTwo1); System.out.println(instant == instantSubDayTwo);
- Instant类中比较两个日期的大小
// 代码示例 Instant instantNow = Instant.now(); Instant instantAddDay = instantNow.plus(Duration.ofDays(5)); //isAfter()方法判断instantAddDay是否在instantNow之后 boolean flgAfter = instantAddDay.isAfter(instantNow); System.out.println(flgAfter); //isBefore()方法判断instantAddDay是否在instantNow之前 boolean flgBefore = instantAddDay.isBefore(instantNow); System.out.println(flgBefore); //compareTo()方法比较,前者时间纳秒值大于后者返回1,小于返回-1,等于返回0 int result1 = instantAddDay.compareTo(instantNow); System.out.println(result1); int result2 = instantNow.compareTo(instantAddDay); System.out.println(result2); int result3 = instantNow.compareTo(instantNow); System.out.println(result3);
- Instant类计算两个日期的差值
// 代码示例 Instant instantNow = Instant.now(); Instant instantAddDay = instantNow.plus(Duration.ofDays(5)); //between()方法后者比前者大,返回正数,比前者小返回负数,相等返回0(ChronoUnit.DAYS位于java.time.temporal) long result1 = ChronoUnit.DAYS.between(instantNow, instantAddDay); System.out.println(result1); long result2 = ChronoUnit.DAYS.between(instantAddDay, instantNow); System.out.println(result2); long result3 = ChronoUnit.DAYS.between(instantNow, instantNow); System.out.println(result3);
LocalDateTime类
概述:表示不带时区的日期及时间,替换之前的Calendar
**注意:**看上去,LocalDateTime和Instant很象,但记得的是“Instant中是不带时区的即时时间点。可能有人说,即时的时间点 不就是日期+时间么?看上去是这样的,但还是有所区别,比如LocalDateTime对于用户来说,可能就只是一个简单的日期和时间的概念,考虑如下的 例子:两个人都在2013年7月2日11点出生,第一个人是在英国出生,而第二个是在加尼福利亚,如果我们问他们是在什么时候出生的话,则他们看上去都是 在同样的时间出生(就是LocalDateTime所表达的),但如果我们根据时间线(如格林威治时间线)去仔细考察,则会发现在出生的人会比在英国出生的人稍微晚几个小时(这就是Instant所表达的概念,并且要将其转换为UTC格式的时间)。
- LocalDateTime获取当前时间(系统自带默认时间)
// 代码示例 LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDateTime);
- LoclaDateTime类中时间加减计算(加)plusXXX()和(减)minusXXX()方法
// 代码示例 // plus()和minus()方法与Instant类中相同 LocalDateTime localDateTime1 = localDateTime.plusHours(5); System.out.println(localDateTime1); System.out.println(localDateTime1 == localDateTime); LocalDateTime localDateTime2 = localDateTime.minusHours(5); System.out.println(localDateTime2); System.out.println(localDateTime2 == localDateTime);
- LocalDateTime显示年月日,不显示时间;显示时间,不显示年月
// 代码示例 LocalDateTime localDateTime = LocalDateTime.now(); //显示年月日,且增加两个月 LocalDate localDate = localDateTime.toLocalDate().plusMonths(2); System.out.println(localDate); //显示时分秒毫秒,且减少两个小时 LocalTime localTime = localDateTime.toLocalTime().minusHours(2); System.out.println(localTime);
- LocalDateTime类中格式化日期的两种方式
// 代码示例 //通过Instant类获取当前系统时间,ZoneId.systemDefault设置为系统默认时区 //ZoneId位于java.time LocalDateTime localDateTime1 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); System.out.println(localDateTime1); //通过DateTimeFormatter类中的ofPattern方法获取自定义格式的系统时间,位于java.time.format String localDateTimeStr = localDateTime1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); System.out.println(localDateTimeStr);
文章借鉴处