JDK8 日期 API 使用

JDK8 日期 API 使用

  • 早期日期类的问题
    • 老版本 API 计算困难问题
    • 线程安全问题
    • 其它
  • Date-Time API 中基本类的使用
    • of 方法在日期 /时间类的应用
    • 为 LocalDateTime 添加时区信息
    • Month 枚举类的使用
    • 根据现有实例创建日期与时间对象
    • Period、Duration、ChronoUnit 计算时间差
    • plus 和 minus 方法的应用
    • with 方法在日期/时间类的应用
  • 调节器 TemporalAdjuster 与查询 TemporalQuery
    • 自定义 TemporalAdjusters 调节器
    • TemporalQuery 的使用
  • java.util.Date 与 java.time.LocalDate 转换
    • 使用 Instant 类将 java.util.Date 转换为 java.time.LocalDate
    • java.sql.Date 类中的转换方法使用
    • java.util.Timestamp 类中的转换方法使用
    • 将 java.util.Date 转换为 java.time.LocalDate 方法二
    • 将java.util.Calendar 转换为 ZonedDateTime
    • 将java.util.Calendar 转换为 LocalDateTime
  • 日期的解析与格式化 DateTimeFormatter
    • 新日期类的 format 和 parse 方法
    • DateTimeFormatter.ofLocalizedDate 指定解析格式
    • 自定义格式化格式

早期日期类的问题

Date 类在 JDK1.1 之前就已经有了,从 API 可以发现,从 JDK1.1开始 Date 类中的好多方法都已经被弃用,Java1.1推荐采用 Calendar 类处理日期和时间,但是这个类同样存在不少问题

Constructor and Description
Date() 分配一个 Date对象,并初始化它,以便它代表它被分配的时间,测量到最近的毫秒。
Date(int year, int month, int date) 已弃用 截至JDK 1.1版,由Calendar.set(year + 1900, month, date)GregorianCalendar(year + 1900, month, date)
Date(int year, int month, int date, int hrs, int min) 已弃用 从JDK 1.1版开始,替换为Calendar.set(year + 1900, month, date, hrs, min)GregorianCalendar(year + 1900, month, date, hrs, min)
Date(int year, int month, int date, int hrs, int min, int sec) 已弃用 截至JDK 1.1版,由Calendar.set(year + 1900, month, date, hrs, min, sec)GregorianCalendar(year + 1900, month, date, hrs, min, sec)
Date(long date) 分配一个 Date对象,并将其初始化为表示自称为“时代”的标准基准时间以后的指定毫秒数,即1970年1月1日00:00:00 GMT。
Date(String s) 已弃用 从JDK 1.1版开始,由DateFormat.parse(String s)

老版本 API 计算困难问题

例子:小明出生于 1995年 12 月20日,计算当前这个时间他已经出生了多少天
步骤思路:
1、初始化 Date 对象,利用无参构造方法
2、获取当前时间距离 1970年1月1日过了多少毫秒
3、初始化 Calendar 对象并设置为 1995年6月20日,并将 Calendar 对象转化为 Date 对象,再转换 1995年6月20日距离 1970年1月1日过了多少毫秒
4、将两个毫秒数做相减操作,然后将毫秒数转换为天数
Date date = new Date();
long nowTime = date.getTime();
Calendar calendar = Calendar.getInstance();
//小心这个坑,calendar 中月份是加一的
calendar.set(1995, 11, 20);
Date birthdayDate = calendar.getTime();
long birthdayTime = birthdayDate.getTime();
//毫秒/1000(秒)/60(分钟)/60(小时)/24(天)
long day = (nowTime-birthdayTime)/1000/60/60/24;
System.out.println("1995年 12 月20日 距离现在 "+ day+"天");//1995年 12 月20日 距离现在 8892天
// 来看看 Java8 新版本 API 如何操作
day = ChronoUnit.DAYS.between(LocalDate.of(1995, 12, 20), LocalDate.now());
System.out.println("1995年 12 月20日 距离现在 "+ day+"天");//1995年 12 月20日 距离现在 8893天

可以看到两种方式计算的结果差了一天,如果自己用计算器算的话,发现后者是对的,说明前者在毫秒转换计算的时候出现了一点误差…

线程安全问题

我们之前对时间的格式化转换一般是用 SimpleDateFormat 类,但是这个类是线程不安全的,在多线程情况下,全局共享一个 SimpleDateFormat 类中的 Calendar 对象有可能出现异常。

/**
 * 创建 10 个线程,将字符串"2018-12-12 12:12:12" 转换为 Date 对象后打印到控制台上
 */
public class Test2 {
    final static SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) {
        //循环,创建 10 个线程
        for(int i=0; i<10; i++){
            new Thread(()->{
                try {
                    Date date = SIMPLE_DATE_FORMAT.parse("2018-12-12 12:12:12");
                    System.out.println(date);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            },"threadName"+i).start();
        }
    }
}

JDK8 日期 API 使用_第1张图片
当然啦,解决的方法无非就是加同步代码块呗

而新版本日期 API 都是线程安全的

其它

另外一个问题就是在 java.util.Date 和 java.util.Calendar 类之前,枚举类型还没有出现,所以在字段中使用整数常量都是可变的,而不是线程安全的,为了处理实际开发中遇到的问题,标准库随后引入了 java.sql.Date 作为 java.util.Date 的子类,但是还是没能彻底解决问题。

最终 Java 8 引入了java.time 包,这种全新的包从根本上解决了长久以来存在的诸多弊端

初始化 Calendar 对象,封装日期 2018年8月8日
Calendar calendar = Calendar.getInstance();
//通过 set 方法设置年月日,-> 开发规范:不允许使用没有定义的魔法数字
//出现提示:Should be one of: Calendar.JANUARY, Calendar.FEBRUARY, Calendar.MARCH, Calendar.APRIL...
calendar.set(2018,8,8);
//应该这样,而 calendar 月份从0开始,很容易就忘记
calendar.set(2018,Calendar.AUGUST,12);

Date-Time API 中基本类的使用

Date-Time API 中的所有类均生成不可变实例,它们是线程安全的,并且这些类不提供公共构造函数,也就是说没办法通过 new 直接创建,需要采用工厂方法加以实例化

System.out.println(Instant.now());//2020-04-18T01:08:23.712Z
System.out.println(LocalDate.now());//2020-04-18
System.out.println(LocalTime.now());//09:08:23.856
System.out.println(LocalDateTime.now());//2020-04-18T09:08:23.857
System.out.println(ZonedDateTime.now());//2020-04-18T09:08:23.857+08:00[Asia/Shanghai]

不仅仅是刚才提供的几个类可以使用 now 方法,Java8 的 Time 包还提供了其它的几个类可以更精确的获取某些信息:

System.out.println(Year.now());//2020
System.out.println(YearMonth.now());//2020-04
System.out.println(MonthDay.now());//--04-18

注意:没有 Month.now(),Month 是个枚举类,有1-12月

of 方法在日期 /时间类的应用

of 方法可以根据给定的参数生成对应的日期/时间对象,基本上每个类都有对应的 of 方法用于生成指定的日期/时间对象,而且重载形式多变,可以根据不同的参数生成对应的数据。

System.out.println(LocalDate.of(2018,8,8));//2018-08-08
System.out.println(LocalTime.of(2,38,20));//02:38:20
System.out.println(LocalDateTime.of(2019,9,9,9,0));//2019-09-09T09:00
System.out.println(YearMonth.of(2020,5));
System.out.println(MonthDay.of(4,22));

对于 LocalTime,如果是初始化晚上的时间,需要加12;如果秒和纳秒是0的话,那么默认不会封装这些数据,只显示小时和钟

System.out.println(LocalTime.of(18,30));//晚上 18:30 
System.out.println(LocalTime.of(19,30,0,0));//晚上 19:30 

注意 LocalDateTime 有个特殊重载方法:of(LocalDate date, LocalTime time)

为 LocalDateTime 添加时区信息

在打印 ZonedDateTime 的时候,发现这个对象封装不仅有时间日期,还有时区,那么时区在 Java 如何获取呢?通过提供的一个类 ZoneId 的 getAvailableZoneIds() 方法可以获取到一个 Set 集合,集合中封装了 600 个时区

System.out.println(ZoneId.systemDefault());//Asia/Shanghai
System.out.println(ZoneId.getAvailableZoneIds());//所有的时区,结果是 set 集合

我们可以通过给 LocalDateTime 添加时区来查看到不同时区的时间,比如 LocalDateTime 中封装的时间是上海时间,想知道此时此刻纽约的时间是什么,就可以将纽约的时区添加进去即可查看到:

  • 封装时间 LocalDateTime 并添加时区信息
  • 更改时区查看对应时间
//1、此时 localDateTime 只是封装了一个时间,并没有时区的概念
LocalDateTime localDateTime = LocalDateTime.of(2018, 11, 28, 8, 54, 38);
//2、添加时区,
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("Asia/Shanghai 时间:"+zonedDateTime);
//3、更改时区
zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("同一时刻 Asia/Tokyo 时间:"+zonedDateTime);

Month 枚举类的使用

java.time 包引入了 Month 枚举类,Month 中包含标准日历中的12个月份的常量,也提供了一些方法供我们使用,推荐在初始化 LocalDate 和 LocalDateTime 对象的时候,月份的参数使用 Month 枚举类方式传入,这样简单易懂更不容易出错,因为如果旧的思维, Calendar 月份从0开始,在新 API 传0会出现异常

//月份建议传 Month 枚举
System.out.println(LocalDate.of(2020, Month.OCTOBER, 23));//2020-10-23
//Month.of() 可以根据传入的数字返回对应的枚举
System.out.println(Month.of(12));//DECEMBER

根据现有实例创建日期与时间对象

想要修改某个日期/时间对象的现有实例时,我们可以使用 plus 和 minus 方法来完成操作。
Java 8中时间相关API 的实例都是不可变的,一旦创建,LocalDate,LocalDateTime,LocalTime 就无法修改(类似 String),这对于线程安全非常有利。

//封装一个日期对象,对象生成后,localDate 是无法修改的
LocalDate localDate = LocalDate.of(2020, 3, 20);//2020-03-20
//计算 4 天后的时间, 注意返回的是一个新对象,
LocalDate plusDays = localDate.plusDays(4);//2020-03-24
//计算 3 周后的时间
LocalDate plusWeeks = localDate.plusWeeks(3);//2020-04-10
//计算 5 个月后的时间
LocalDate plusMonths = localDate.plusMonths(5);//2020-08-20
//计算 6 年后的时间
LocalDate plusYears = localDate.plusYears(6);//2026-03-20

上面是加时间,减同理,minusXXX(),查看源码发现,底层调用的还是 plusXX方法

Period、Duration、ChronoUnit 计算时间差

Period类主要是方法getYears(),getMonths()和getDays()来计算,Period 更多的是计算一段时期,比如Period.between(LocalDate.of(2019,5,1),LocalDate.of(2020,5,1)).getDays() 是0,通过getYears 可以得出1 年

LocalDate today = LocalDate.now();
LocalDate birthDate = LocalDate.of(1995, Month.JUNE, 20);
Period p = Period.between(birthDate, today);
System.out.printf("年龄 : %d 年 %d 月 %d 日",p.getYears(),p.getMonths(),p.getDays());

==========sout==========
年龄 : 241011

Duration 提供了使用基于时间的值(如秒,纳秒)测量时间量的方法。

Instant inst1 = Instant.now();
Instant inst2 = inst1.plus(Duration.ofSeconds(10));
System.out.println("相隔毫秒 : " + Duration.between(inst1, inst2).toMillis());
System.out.println("相隔秒 : " + Duration.between(inst1, inst2).getSeconds());
System.out.println("相隔纳秒 : " + Duration.between(inst1, inst2).getNano());

==========sout============
相隔毫秒 : 10000
相隔秒 : 10
相隔纳秒 : 0

ChronoUnit 类可用于在单个时间单位内测量一段时间,例如天数或秒。

LocalDate startDate = LocalDate.of(1993, Month.OCTOBER, 19);
LocalDate endDate = LocalDate.now();
long days = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("相隔天数   : " + days);

============sout===============
相隔天数   : 9691

当然要计算相隔多少小时得用 LocalDateTime 了,ChronoUnit.HOURS.between(LocalDateTime.of(2019, 5, 1,12,1,2), LocalDateTime.of(2020, 5, 1,2,4,1)),非常简单。

plus 和 minus 方法的应用

上面我们都是对 日期/时间的 某一项进行加减,其实还有两个单独的 plus 方法,Temporal plus(TemporalAmount amount)Temporal plus(long amountToAdd, TemporalUnit unit)

问题:今天小张查看自己车辆保险记录的时候发现还有 2年3个月8天就到期了,计算到期的时间是什么时候

方法一:plusXXX()方法

LocalDate now = LocalDate.now();
//在当前时间基础上加操作 2年3个月8天
LocalDate endTime = now.plusYears(2).plusMonths(3).plusDays(8);
//当前时间:2020-04-25 保险到期时间:2022-08-02
System.out.println("当前时间:"+now+" 保险到期时间:"+endTime);

方法二:plus(TemporalAmount amountToAdd)
TemporalAmount 是一个接口,当接口作为方法参数的时候,实际上传入的是接口的实现类,查看这个接口的实现体系,可以看到有一个类 Period,这个类表示一段时间

LocalDate now = LocalDate.now();
Period period = Period.of(2, 3, 8);
LocalDate endTime = now.plus(period);
//当前时间:2020-04-25 保险到期时间:2022-08-02
System.out.println("当前时间:"+now+" 保险到期时间:"+endTime);

在实际的开发过程中,可能会更精确的去操作日期或者说增加一些特殊的时间,比如说一个实际,一个半天,1千年,Java8 提供了这些日期的表示方式而不需要单独进行计算了。

TemporaUnit 是一个接口,通过接口继承体现可以发现,可以使用子类 ChronoUnit 来表示,ChronoUnit 封装了很多时间段供我们使用。

JDK8 日期 API 使用_第2张图片

//在这天结婚
LocalDateTime marryTime = LocalDateTime.of(2020, 2, 2, 11, 11, 11);
//十年后称为 锡婚
LocalDateTime time = marryTime.plus(1, ChronoUnit.DECADES);//2030-02-02T11:11:11
//半天后(12小时)再请朋友们吃饭
LocalDateTime eatTime = time.plus(1, ChronoUnit.HALF_DAYS);//2030-02-02T23:11:11

with 方法在日期/时间类的应用

以 LocalDateTime 为例,如果不需要对日期进行加减而是要直接修改日期的话可以使用 with 方法,with 方法提供了很多修改时间的方式

//在实际业务中可能调用的是别人的方法获得时间
LocalDateTime time = getTime();
//经过使用发现这个 time 的时间有误,日期应该是 1号
//那么在不知道原有时间的基础上,无法进行增减操作,可以直接使用 with 方法进行修改
LocalDateTime resultTime1 = time.withDayOfMonth(1);
//这种方法也是可以的
LocalDateTime resultTime2 = time.with(ChronoField.DAY_OF_MONTH,1);
System.out.println("原来的时间:"+time);
System.out.println("修正后的时间:"+resultTime1);
System.out.println("修正后的时间:"+resultTime2);

======输出===========
原来的时间:2018-12-12T08:28:44
修正后的时间:2018-12-01T08:28:44
修正后的时间:2018-12-01T08:28:44

当然还有 withHour()、withYear()、withDayOfYear()…等方法

调节器 TemporalAdjuster 与查询 TemporalQuery

之前我们通过 with 方法修改日期中封装的 数据,但是有些时候可能会做些复杂的操作,比如将时间调整到下个周的周日,下一个工作日,或者本月的某一天,这个时候可以使用 调节器TemporalAdjuster 来更方便的处理日期

LocalDate now = LocalDate.now();
//修改为本月的第一天
LocalDate firstDayOfMonth = now.with(TemporalAdjusters.firstDayOfMonth());
//修改为当月的最后一天
LocalDate lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());
//修改为当年的最后一天
LocalDate lastDayOfYear = now.with(TemporalAdjusters.lastDayOfYear());
//修改为下一年的第一天
LocalDate firstDayOfNextYear = now.with(TemporalAdjusters.firstDayOfNextYear());
//修改为下个月的第一天
LocalDate firstDayOfNextMonth = now.with(TemporalAdjusters.firstDayOfNextMonth());

//修改为下一个周日
LocalDate nextSunday = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
//修改为上一个周三
LocalDate previousWEDNESDAY = now.with(TemporalAdjusters.previous(DayOfWeek.WEDNESDAY));

自定义 TemporalAdjusters 调节器

通过 Java8 本身提供的 TemporalAdjusters 中方法可以完成一些常用的操作,如果要自定义日期的时间的更改逻辑,可以通过实现 TemporalAdjuster 接口的方式完成
思考:发薪日是每月 15号,如果发薪日是周末,则调整为周五

自定义调节器

public class DateAdjuest implements TemporalAdjuster {
    @Override
    public Temporal adjustInto(Temporal temporal) {
        LocalDate payDate = LocalDate.from(temporal);
        //把发薪日修改为 15
        LocalDate realPayDay = payDate.withDayOfMonth(15);
        //接着判断这个日子是不是周末,如果是则提前改为上一个周五 发工资
        if (payDate.getDayOfWeek()== DayOfWeek.SUNDAY || payDate.getDayOfWeek()==DayOfWeek.SATURDAY)
            realPayDay = realPayDay.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
        return realPayDay;
    }
}
LocalDate date = LocalDate.of(2019, 12, 15);
LocalDate realtime = LocalDate.from(new DateAdjuest().adjustInto(date));
System.out.println("原来的日期是:"+date+"真实发薪日:"+realtime);
========输出=============
原来的日期是:2019-12-15真实发薪日:2019-12-13

TemporalQuery 的使用

LocalDate、LocalTime 有一个方法叫做 query,可以针对日期进行查询,query(TemporalQuery query) 是一个泛型方法,返回的数据就是传入的泛型类型,而 TemporalQuery 是一个泛型接口,里面有一个抽象方法 queryFrom(TemporalAccessor temporal) ,TemporalAccessor 是 Temporal 的父接口,实际上也就是 LocalDate、LocalTime 相关类的顶级父接口,这个 queryFrom 方法的实现逻辑就是 传入一个日期/时间对象,通过自定义逻辑返回数据。如果要计划日期距离某一特定天数差多少天,可以自定义实现类 TemporalQuery 并作为参数传递到 query 方法中。

思考:计算当前时间距离下一个劳动节还有多少天?

public class TemporalQueryImpl implements TemporalQuery<Long> {
    @Override
    public Long queryFrom(TemporalAccessor temporal) {
        //TemporalAccessor 是 LocalDate、LocalTime 的父接口,
        //相当于 LocalDate就是这个接口的实现类,将 temporal 转换为 LocalDate 进行使用
        LocalDate now = LocalDate.from(temporal);
        //封装当年的劳动节时间
        LocalDate laborDay = LocalDate.of(now.getYear(), Month.MAY, 1);
        //判断当前时间是否已经超过当年的劳动年,如果超过,laborDay 加一年
        if (now.isAfter(laborDay)){
            laborDay = laborDay.plusYears(1);
        }
        //通过 ChronoUnit 类的 between 方法来计算两个时间点的差额
        long days = ChronoUnit.DAYS.between(now, laborDay);
        //返回的就是相隔的天数
        return days;
    }
}
Long days = now.query(new TemporalQueryImpl());
System.out.println("当前时间是:"+now+" 距离下个劳动节还有:"+days);

思考:计算当前时间距离下一个圣诞节/儿童节/劳动节还有多少天?

public class TemporalQueryImpl implements TemporalQuery<Long[]> {
    /**
     * @param temporal
     * @return 表示距离三个节日的差额,0索引表示圣诞节,1->儿童节,2->劳动节
     */
    @Override
    public Long[] queryFrom(TemporalAccessor temporal) {
        //TemporalAccessor 是 LocalDate、LocalTime 的父接口,
        //相当于 LocalDate就是这个接口的实现类,将 temporal 转换为 LocalDate 进行使用
        LocalDate now = LocalDate.from(temporal);
        //封装圣诞节
        LocalDate d1 = LocalDate.of(now.getYear(), Month.DECEMBER, 25);
        //封装儿童节
        LocalDate d2 = LocalDate.of(now.getYear(), Month.JUNE, 1);
        //封装当年的劳动节时间
        LocalDate d3 = LocalDate.of(now.getYear(), Month.MAY, 1);
        //判断当前时间是否已经超过当年的劳圣诞节/儿童节/劳动节,如果超过,加一年
        if (now.isAfter(d1)) d1 = d1.plusYears(1);
        if (now.isAfter(d2)) d2 = d2.plusYears(1);
        if (now.isAfter(d3)) d3 = d3.plusYears(1);

        //通过 ChronoUnit 类的 between 方法来计算两个时间点的差额
        Long[] days = {ChronoUnit.DAYS.between(now, d1),ChronoUnit.DAYS.between(now, d2),ChronoUnit.DAYS.between(now, d3)};
        //返回的就是相隔的天数
        return days;
    }
}
Long[] days = now.query(new TemporalQueryImpl());
System.out.println("当前时间是:"+now+" \n距离下个圣诞节还有:"+days[0]+" \n距离下个儿童节还有:"+days[1]+" \n距离下个劳动节还有:"+days[2]);
==============输出==============
当前时间是:2020-04-25 
距离下个圣诞节还有:244 
距离下个儿童节还有:37 
距离下个劳动节还有:6

java.util.Date 与 java.time.LocalDate 转换

对于老项目的改造,需要将 Date 或者 Calendar 转换为 java.time包中相应的类,Java8 中 java.time 包并没有提供太多的内置方式来转换 java.util 包中预处理标准日期和时间的类,我们可以使用 Instant 类作为中介,也可以使用 java.sql.Date 和 java.sql.Timestamp 类提供的方法进行转换

使用 Instant 类将 java.util.Date 转换为 java.time.LocalDate

java.time 包并没有提供很多的方式进行直接转换,但是之前的 Date类、Calendar类在 Java8 都提供了一个新的方法叫 toInstant,可以将当前对象转换为 Instant 对象,通过给 Instant 添加时区信息后就可以转换为 LocalDate 对象。

// 初始化 java.util.Date 对象
Date date = new Date();
//转换为 Instant
Instant instant = date.toInstant();
//添加时区
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
// zonedDateTime 通过 toLocalDate()方法转换
LocalDate localDate = zonedDateTime.toLocalDate();
System.out.println("原生 date:"+date);
System.out.println("转换后 localDate: "+localDate);

========sout==========
原生 date:Sat Apr 25 13:00:24 CST 2020
转换后 localDate: 2020-04-25

java.sql.Date 类中的转换方法使用

看清楚 这个包,java.sql.Date

//初始化 java.sql.Date 对象
Date date = new Date(System.currentTimeMillis());
// java.sql.Date 自带 toLocalDate() 方法进行转换
LocalDate localDate = date.toLocalDate();
System.out.println("java.sql.date:"+date);
System.out.println("java.time.localDate: "+localDate);
=========sout=========
java.sql.date:2020-04-25
java.time.localDate: 2020-04-25

java.util.Timestamp 类中的转换方法使用

Timestamp timestamp = new Timestamp(System.currentTimeMillis());
LocalDateTime localDateTime = timestamp.toLocalDateTime();

将 java.util.Date 转换为 java.time.LocalDate 方法二

这种方法就是使用 java.sql.Date 作为中间桥梁,因为 java.sql.Date 提供了 toLocalDate() 方法…

java.util.Date date = new Date();
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
LocalDate localDate = sqlDate.toLocalDate();

将java.util.Calendar 转换为 ZonedDateTime

//初始化 Calendar 对象
Calendar calendar = Calendar.getInstance();
//Java1.1 可以通过 calendar 获取时区对象( ZonedDateTime 必须要有时区)
TimeZone timeZone = calendar.getTimeZone();
//Java1.8可以通过 timeZone 获取到 zoneId->有了 zoneId 就可以构建 ZonedDateTime 对象了
ZoneId zoneId = timeZone.toZoneId();
//通过 ZonedDateTime 的 ofInstant() 方法封装成 ZonedDateTime 对象
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(calendar.toInstant(), zoneId);
//有了 zonedDateTime 也就有了 localDate、localDateTime
zonedDateTime.toLocalDate();
zonedDateTime.toLocalDateTime();
zonedDateTime.toLocalTime();

将java.util.Calendar 转换为 LocalDateTime

当然我们可以先得到 ZonedDateTime 再通过 toLocalDateTime 获取,就像上面一样。
但是 Calendar 本身就可以获取到年月日时分秒信息,这些信息可以作为 LocalDateTime 参数进行构造。

Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR);
int minite = calendar.get(Calendar.MINUTE);
int seconds = calendar.get(Calendar.SECOND);
//注意因为 Calendar 中的月份是从 0 开始的,所以这里获取到的月份要加 1
LocalDateTime time = LocalDateTime.of(year, month + 1, day, hour, minite, seconds);
System.out.println(time);//2020-04-25T01:30:06

日期的解析与格式化 DateTimeFormatter

SimpleDateFormate 之前说过是线程不安全的,所以 Java8 提供了新的格式化类 DateTimeFormatter。DateTimeFormatter 提供了大量的预定义格式化器,包括常量如 ISO_LOCAL_DATE,模式字母(yyyy-MM-dd)及本地化样式

新日期类的 format 和 parse 方法

LocalDateTime localDateTime = LocalDateTime.now();
//通过 format 进行格式化
String s1 = localDateTime.format(DateTimeFormatter.ISO_DATE);
String s2 = localDateTime.format(DateTimeFormatter.ISO_DATE_TIME);
System.out.println(localDateTime);
System.out.println("ISO_DATE 格式:"+s1);
System.out.println("ISO_DATE_TIME 格式:"+s2);

//通过 parse 解析
LocalDateTime time = LocalDateTime.parse(s2);
LocalDate date = LocalDate.parse(s1);
System.out.println(time);
System.out.println(date);

========sout==========
2020-04-25T13:41:16.639
ISO_DATE 格式:2020-04-25
ISO_DATE_TIME 格式:2020-04-25T13:41:16.639
2020-04-25T13:41:16.639
2020-04-25

DateTimeFormatter.ofLocalizedDate 指定解析格式

LocalDateTime localDateTime = LocalDateTime.now();
// 通过 DateTimeFormatter.ofLocalizedDate 指定解析格式
String s1 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL));
String s2 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG));
String s3 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
String s4 = localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT));
System.out.println("FULL: "+s1);
System.out.println("LONG: "+s2);
System.out.println("MEDIUM: "+s3);
System.out.println("SHORT: "+s4);

============sout=============
FULL: 2020425日 星期六
LONG: 2020425日
MEDIUM: 2020-4-25
SHORT: 20-4-25

这种方式在不同地区显示方式会不一样,在其它地区不会显示中文,会根据当前默认时区来进行区别显示

自定义格式化格式

LocalDateTime localDateTime = LocalDateTime.now();
String format = localDateTime.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));

你可能感兴趣的:(java基础)