Java8里提供的全新的日期与时间的处理

Java8之前的日期与时间的处理,最常用的是Data、Calendar这两个类,还有格式化的SimpleDateFormat类。这三个类是经常使用的。则三个类的功能其实是非常薄弱的。我们的痛点列举如下:

1、进行一些日期计算的时候,比如当前日期+60天之后的日期。或30天之前的日期,或5周之后的日期,3周之前的日期,3个月、2年之后的日期等等;

 

Data类是一个可变的类,是很容易出错的。SimpleDateFormat不是线程安全的。

 

针对以上痛点,在Java8出来之前,社区里已经出现了一个专门的项目针对日期和时间处理的。这个项目就叫JodaTime。它提供了非常多的易用而且见名知意的非常非常多的一些方法,这些方法都非常实用,可以解决日常开发中很多时间处理的问题。在Java8出来之前时间处理还有一个非常头疼的问题就是时区的概念,Java8出来之前就没有提供时区处理的方式。

 

时区概念:中国是处在东八区,像日本就是东九区,比如北京上上午十点,东京就是上午十一点,同一个时刻,在不同的国家时间是不一样的。这一点在之前的Java8日期API中根本就没有提供解决方案。

 

JodaTime可以和Java即有的类例如Data进行互相转化。我用JoadTime处理完一些时间之后还可以转换回到Data上。

从Java8开始,在Java里就引入了一套全新的日期与时间的API。如果你之前在项目中大量使用了JodaTime,那么你学习Java8提供的这套日期与时间API将会非常轻松。Java8的这套日期与时间API,包括设计思想,都是极大的得到了JodaTime的启发,很多地方都是沿用了JoadTime里的一些设计方式。新的API里解决了日期线程安全的问题。

 

下面大概过一些JodaTime这个项目里面的一些子项目:

Joda-Time:提供与日期和时间处理的基础类

Joda_money:提供与货币、钱相关的一些基础处理,金融项目里面经常会用到的

Joda-Beans:基础的管理Java Bean的方法

Joda-Convert:将字符串转化成对象,也可以将对象转换程字符串。

Joda-Collect:对于集合的数据库结构提供额外的支撑

Joda-Primitives:提供针对原生数据类型的集合,避免装箱拆箱的操作

 

Java8里新提供的日期和时间的基础设施是在rt.jar里面的java.time包以及这个包下面的子包里,从Java8开始,建议使用Java8里面的内容替换掉项目里面使用的JodaTime。

 

关于日期与时间:

1、世界标准时间:格林威治标准时间,全球时间的基准,其他地方的时间都是在这个基准时间的基础上加或者减时间。

2、UTC时间,不带时区的一个标准时间。例如格式是2018-12-1T11:22:33.567Z【mongodb时间底层存储的就是这种格式的】

3、ISO8601,本身是一个标准,就是针对日期与时间标准的处理形式。JodaTime默认使用的时间格式。

 

下面举些示例:

public boolean isAfterPayDay(DateTime datetime) {

  if (datetime.getMonthOfYear() == 2) {   // February is month 2!!

    return datetime.getDayOfMonth() > 26;

  }

  return datetime.getDayOfMonth() > 28;

}

 

public Days daysToNewYear(LocalDate fromDate) {

  LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);

  return Days.daysBetween(fromDate, newYear);

}

 

public boolean isRentalOverdue(DateTime datetimeRented) {

  Period rentalPeriod = new Period().withDays(2).withHours(12);

  return datetimeRented.plus(rentalPeriod).isBeforeNow();

}

 

public String getBirthMonthText(LocalDate dateOfBirth) {

  return dateOfBirth.monthOfYear().getAsText(Locale.ENGLISH);

}

 

 

下面开始学习JodaTime了:

package com.shensiyuan.java8;

import org.joda.time.DateTime;

import org.joda.time.LocalDate;

public class JodaTest2 {

    public static void main(String[] args) {

        // 今天

        DateTime today = new DateTime();

        // 明天

        DateTime tomorry = today.plusDays(1);

        System.out.println(today.toString("yyyy-MM-dd"));

        System.out.println(tomorry.toString("yyyy-MM-dd"));

        System.out.println("=======================");

        // 返回一个datetime的副本同时更新了天这个字段,就是把天这个字段设置为1

        DateTime d1 = today.withDayOfMonth(1);

        System.out.println(d1.toString("yyyy-MM-dd"));

        System.out.println("=====================");

        // 表示当前的时区

        LocalDate localDate = new LocalDate();

        System.out.println(localDate);

        System.out.println("=====================");

        // 计算距离当前这一天后面三个月的最后一天的日期

        // dayOfMonth表示获取到了那个月份的那一天的这个属性。withMaximumValue就是把它设置成当前这个月份里面最小可以设置的那个天数(就是1)

        // withMaximumValue()表示把它设置成当前这个月份里最大可以设置的那一天,也就是取一个月最后一天(不需要自己考虑那一天到底是有28、30还是31天)。

        localDate = localDate.plusMonths(3).dayOfMonth().withMinimumValue();

        System.out.println(localDate);

        System.out.println("=====================");

        // 计算两年前的第三个月的最后一天的日期

        DateTime dateTime = new DateTime();

        // monthOfYear()获取到月份,setCopy(3)将这个月份设置为3,dayOfMonth()获取到月份的天

        DateTime dateTime2 = dateTime.minusYears(2).monthOfYear().setCopy(3).dayOfMonth().withMinimumValue();

        System.out.println(dateTime2.toString("yyyy-MM-dd"));

    }

}

更有价值的示例:

package com.shensiyuan.java8;

import java.util.Date;

import org.joda.time.DateTime;

import org.joda.time.DateTimeZone;

import org.joda.time.format.DateTimeFormat;

public class JodaTest3 {

    public static void main(String[] args) {

        // 传入的是一个utc标准时间,而北京是位于东八区的,所以打印出来的时间会在这个时间的基础上加八个小时

        System.out.println(JodaTest3.convertUTC2Date("2014-11-04T09:22:54.876Z"));

        // 这个是东八区的时间转换成标准的utc时间,要减去8小时

        System.out.println(JodaTest3.convertDate2UTC(new Date()));

        System.out.println(JodaTest3.convertDate2LocalByDateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));

    }

    // 说明一点:做app的时候,前后端交互,后端给前端返回的时间格式最好是utc的,因为这个是不带时区的,返回到前端之后,前端app肯定知道它所处的时区是哪个,它自己把这个转换成对应时区的时间即可

    // 给定一个UTC标准时间,把它转换成一个日期类型

    // 标准UTC时间:2014-11-04T09:22:54.876Z

    // 常用的由客户端传过来的时间转换成一个服务器存储的时间

    public static Date convertUTC2Date(String utcDate) {

        try {

            DateTime dateTime = DateTime.parse(utcDate, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));

            return dateTime.toDate();

        } catch (Exception e) {

            return null;

        }

    }

    // 给定一个日期类型,返回utc字符串类型

    // 常用的由服务器返回的时间转换成一个utc的标准时间

    public static String convertDate2UTC(Date javaDate) {

        DateTime dateTime = new DateTime(javaDate, DateTimeZone.UTC);

        return dateTime.toString();

    }

    // 格式化给定日期

    public static String convertDate2LocalByDateFormat(Date javaDate, String dateFormat) {

        DateTime dateTime = new DateTime(javaDate);

        return dateTime.toString(dateFormat);

    }

}

 

强调一点:

无论是jodaTime还是java8里面的日期时间的api,他们都是不可变的对象,目的是为了确保线程安全,每一次创建的时候都是返回一个全新的对象去使用。这个里面返回的月份都是从1开始的,不是从0开始,可读性更强。

Java8之前的Date、Calendar、SimpleDateFormat都是线程不安全的,都是可变的对象。这个里面返回的月是从0开始的,比如3月,返回的是2,可读性不强。

 

下面看Java8里面提供的日期和时间api的使用方式(java8提供的日期时间类基本都位于java.time这个包下):

package com.shensiyuan.java8;

import java.time.Clock;

import java.time.Instant;

import java.time.LocalDate;

import java.time.LocalDateTime;

import java.time.LocalTime;

import java.time.MonthDay;

import java.time.Period;

import java.time.YearMonth;

import java.time.ZoneId;

import java.time.ZonedDateTime;

import java.time.temporal.ChronoUnit;

import java.util.Set;

public class Java8TimeTest {

    public static void main(String[] args) {

        // 当前时区里的当前时间

        // LocalDate关注的是年月日

        LocalDate localDate = LocalDate.now();

        System.out.println(localDate);

        System.out.println(localDate.getYear() + "," + localDate.getMonthValue() + "," + localDate.getDayOfMonth());

        System.out.println("===================");

        LocalDate localDate2 = LocalDate.of(2017, 3, 3);

        System.out.println(localDate2);

        System.out.println("===================");

        LocalDate localDate3 = LocalDate.of(2010, 3, 25);

        // MonthDay表示有月份有天,没有年的概念。如果只关注月和日,不关注年则可以使用这个类

        MonthDay monthDay = MonthDay.of(localDate3.getMonth(), localDate3.getDayOfMonth());

        MonthDay monthDay2 = MonthDay.from(LocalDate.of(2011, 3, 25));

        if (monthDay.equals(monthDay2)) {

            System.out.println("equals");

        } else {

            System.out.println("not equals");

        }

        System.out.println("===================");

        // LocalTime关注的是时分秒

        LocalTime time = LocalTime.now();

        System.out.println(time);

        // 增加三小时,增加20分钟

        LocalTime time2 = time.plusHours(3).plusMinutes(20);

        System.out.println(time2);

        System.out.println("===================");

        // 找到当前这一天的下两周的时间

        LocalDate localDate1 = localDate.plus(2, ChronoUnit.WEEKS);

        System.out.println(localDate1);

        System.out.println("===================");

        // 当前这一天的两个月之前的日期

        LocalDate localDate4 = localDate.minus(2, ChronoUnit.MONTHS);

        System.out.println(localDate4);

        System.out.println("===================");

        // Clock表示时钟的意思表示当前的时刻

        // systemDefaultZone表示当前默认的时区

        Clock clock = Clock.systemDefaultZone();

        System.out.println(clock.millis());

        System.out.println("===================");

        LocalDate localDate5 = LocalDate.now();

        LocalDate localDate6 = LocalDate.of(2017, 3, 19);

        System.out.println(localDate5.isAfter(localDate6));

        System.out.println(localDate5.isBefore(localDate6));

        System.out.println(localDate5.equals(localDate6));

        System.out.println("===================");

        // ZoneId表示时区,getAvailableZoneIds()返回的是所有的时区信息

        Set<String> set = ZoneId.getAvailableZoneIds();

        set.stream().forEach(System.out::println);

        System.out.println("===================");

        // 构造一个时区,后面是时区的名字

        ZoneId zoneId = ZoneId.of("Asia/Shanghai");

        // 本地的带有日期和时间两个维度的这样的一个对象

        LocalDateTime localDateTime = LocalDateTime.now();

        System.out.println(localDateTime);

        // 构造一个带有时区的时间

        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId);

        System.out.println(zonedDateTime);

        System.out.println("===================");

        // 带有年也月的时间

        YearMonth yearMonth = YearMonth.now();

        System.out.println(yearMonth);

        // 月份的天数

        System.out.println(yearMonth.lengthOfMonth());

        // 是不是闰年

        System.out.println(yearMonth.isLeapYear());

        System.out.println("===================");

        YearMonth yearMonth1 = YearMonth.of(2016, 2);

        System.out.println(yearMonth1);

        // 这个月的长度

        System.out.println(yearMonth1.lengthOfMonth());

        // 这年的长度

        System.out.println(yearMonth1.lengthOfYear());

        System.out.println(yearMonth1.isLeapYear());

        System.out.println("===================");

        LocalDate localDate7 = LocalDate.now();

        LocalDate localDate8 = LocalDate.of(2017, 8, 25);

        // Period表示周期的意思。between表示包含前边的,不包含后边的一个时间范围

        Period period = Period.between(localDate7, localDate8);

        // 得到间隔的年数

        System.out.println(period.getYears());

        // 得到间隔的月数

        System.out.println(period.getMonths());

        // 得到间隔的天数

        System.out.println(period.getDays());

        System.out.println("===================");

        // Instant表示时间线上的一个时间点。输出的是不带时区的标准的UTC的格式

        System.out.println(Instant.now());

    }

}

 

下面举更多的例子:

Period的between方法含义:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值