Java8 中新的日期和时间


为什么我们需要新的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类

概述:该类表示一段时间的年、月、日

  1. 使用该类中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());
    
  2. 使用该类中的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());
    
  3. 使用该类的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类

概述:表示秒或纳秒的时间间隔,适合处理较短的时间,且需要更高的精确性

  1. 使用该类中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());
    
  2. 使用该类中的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());
    
  3. 使用该类的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);
  1. 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);
    
  2. Instant类中的parse方法
    // 代码示例
    //该方法的入参仅支持UTC格式的字符串,而且date结果的字符串会报出DateTimeParseException异常
    Instant instant = Instant.parse("2020-10-26T12:10:00Z");
    System.out.println(instant);
    
  3. 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);
    
  4. 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);
    
  5. 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格式的时间)。

  1. LocalDateTime获取当前时间(系统自带默认时间)
    // 代码示例
    LocalDateTime localDateTime = LocalDateTime.now();
    System.out.println(localDateTime);
    
  2. 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);
    
  3. 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);
    
  4. 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);
    

文章借鉴处


文章作者: Huowy
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Huowy !
评论
  目录