java时间类LocalDateTime的前世今生

发布时间 2023-09-04 15:10:33作者: 异想天开的carlors

                                                                                                                                        1.日期类API导学

设计初衷:Java原本自带的java.util.Datejava.util.Calendar类,实际上两种类有线程不安全的风险(虽然学习的时候处于单线程环境并不会出现问题),但是之后到了企业中还是可能会增加学习成本,重新学习如何处理时间,所以推出了这个Java8的最新时间类库的讲解,希望降低学生的学习成本,能够更快的融入到企业开发实战中去。

课程目标

*    掌握Java8中提供的java.time包中的常用日期与相关方法

*    可以从java.util包下的日期类相关类过度到java.time包下的日期类

*    掌握Java8中的日期与字符串之间的相互转换

 

学习此课程需要的基础知识

*    java.util.Date与java.util.Calendar类的相关基础知识

*    线程相关的基础知识

开发环境

*    IDEA + JDK1.8

适合人群

*    Java初学者

*    再企业中有从老版本的日期类转换到新版本的日期类需求的人员

感谢B站
手打整理:
java.time笔记(1-18):https://www.jianshu.com/p/20bfae706882
java.time笔记(19-33):https://www.jianshu.com/p/7a1b367ae95f
源码提取在第一个链接开头,手工编码诸位海涵!别忘了点赞哦~

                                                                二、 老版本API计算困难问题

为什么会出现新的日期类API

JAVA面世之初,标准库就引入了两种用于处理日期和时间的类,它们是java.util.Datejava.util.Calendar。而前者堪称类糟糕的设计典范,浏览API可以发现,从Java1.1开始,Date 类中的所有方法就已经被弃用,Java1.1推荐采用Calendar类处理日期和时间,但是这个类同样存在不少问题。

对于日期的计算困难问题

毫秒值与日期直接转换比较繁琐,其次通过毫秒值来计算时间的差额步骤较多

 

                                                                                  三、老版本API线程不安全问题

SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个SimpleDateFormat类中的Calendar对象有可能会出现异常。

 

另外一个问题就是在java.util.Date和java.util.Calendar类之前,枚举类型(ENUM)还没有出现,所以在字段中使用整数常量导致整数常量都是可变的,而不是线程安全的。为了处理 实际开发中遇到的问题,标准库随后引入了java.sql.Date作为java.util.Date的子类,但是还是没能彻底解决问题。最终JavaSE 8中引入了java.time包,这种全新的包从根本上解决了长久以来存在的诸多弊端,java.time包基于Joda-Time库构建,是一种免费的开源解决方案,实际上在Java 8没有出现之前,公司中已经广泛使用Joda-Time来解决Java中的日期与时间问题,Joda-Time的设计团队也参与了java.time包的开发。


                                                                                           四、老版本API使用规范问题

 

                                                                         五、java.time包->常用类的概述和功能介绍

 

Instant类

    Instant类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,之后学习的类型转换中,均可以使用Instant类作为中间类完成转换。

Duration类

    Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性。

Period类

    Period类表示一段时间的年、月、日。

LocalDate类

    LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年月日。

LocalTime类

    LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表示为纳秒精度。

LocalDateTime类

    LocalDateTime类是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日=时-分-秒。

ZonedDateTime类

    ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。

now方法在日期/时间类的使用

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



                                                                         六、now方法在日期时间类中的应用1

now方法可以根据当前日期或时间创建实例。

                                                            

                                                                     七、now方法在日期时间类中的应用2

 

不仅仅是以上提供的及各类可以使用now方法,Java8的Time包种还提供了其它几个类可以更精准的获取某些信息。

Year类(表示年)

YearMonth类(表示年月)

MonthDay类(表示月日)


 

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

指定任意时间节点

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

 

                                      

                                                               九、时区信息的获取(拓展)

在学习ZonedDateTime的时候,发现这个对象里面封装的不仅有时间日期,并且还有偏移量+时区,那么时区如何在Java中获取呢?通过提供的一个类ZonedId的getAvailableZoneIds方法可以获取到一个Set集合,集合中封装了600个时区。


 

                                                       

                                                                             十、添加时区信息与获取其它时区时间

 


我们可以通过给 LocalDateTimem添加时区信息来查看到不同时区的时间,比如说 LocalDateTime中当前封装的是上海时间,那么想知道此时此刻,纽约的时间是多少,就可以将纽约的时区Id添加进去,就可以查看到了,方式如下:

*    封装时间 LocalDateTime 并添加时区信息。

*    更改时区信息查看对应时间。

 

                                                                              十一、Month枚举类的使用

java.time包中引用了Month的枚举类,Month中包含标准日历中的12个月份的常量(从JANUARY到DECEMEBER)也提供了一些方便的方法供我们使用。

推荐在初始化LocalDate和LocalDateTime对象的时候,月份的参数使用枚举的方式传入,这样更简单易懂而且不易出错,因为如果是老的思维,Calendar传入0的话,那么会出现异常。

 

                                                                        十二、章节练习(1)

                                              

                                                         十三、plus方法在LocalDate中的使用

想要修改某个日期/时间对象的现有实例时,我们可以使用 plusminus方法来完成操作。

Java8中日期时间相关的API中的所有实例都是不可改变的,一旦创建LocalDate,LocalTime,LocalDateTime就无法修改他们(类似于String),这对于线程安全时非常有利的。

plus方法在LocalDate与LocalTime中的使用

*    LocalDate中定义了多种对日期进行增减操作的方法。

    LocalDate plusDay(long days) 增加天数

    LocalDate plusWeeks(long weeks) 增加周数

    LocallDate plusMonths(long months) 增加月数    

    LocalDate plusYears(long years) 增加年数



 minus方法与以上类似。

                                                           十四、plus方法在LocalTime中的使用

LocalTime中定义了多种对事件进行增减操作的方法

LocalTime plusNanos(long nanos) 增加纳秒

LocalTime plusSeconds(long seconds) 增加秒

LocalTime plusMinutes(long minutes) 增加分钟

LocalTime plusHours(long hours) 增加小时


                                                                 

                                                                            十五、plus的单独使用方式1

本文中都是使用plusXXX的方法进行演示,实际上也有对应的减少方法,以minus开头的方法对应的即为减少,实际上也有对应的减少方法,以minus开头的方法对应的即为减少,实际上minus方法调用的也是plus方法,只不过传入的参数是负数。

plus 和 minus 方法的应用

刚才学习到的plusXXX相关的方法都是添加了数值到具体的某一项上,根据观察还有两个单独的plus方法,接下来我们来学习这两个单独的plus方法。

plus(long amountToadd, TemporalUnit unit)  LocalTime

plus(TemporalAmount amoutToadd)       LocalTime

TemporalAmount 是一个接口,当接口作为方法的参数的时候,实际上传入的是接口的实现类对象,根据查看这个接口的体系,可以看到这个接口有一个实现类,名字叫做Period,在学习第一节的时候,说明了这个 嘞表示一段时间。


 

如何使用Period来表示一段时间呢?这个类本身提供了of(int year, int month, int day)来表示,例如:Period.of(1,2,3)返回的对象即为1年2个月3天这么一个时间段。

 

 

                                                                    十六、plus的单独使用方式2

plus(long amountToadd, TemporalUnit unit)

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

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


                                                                      

                                                                         十七、with方法的使用方式1

with方法在LocalDateTime类的应用

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

LocalDateTime withNano(int i) 修改纳秒

LocalDateTime withSecond(int i) 修改秒

LocalDateTime withMinute(int i) 修改分支

LocalDateTime withHour(int i) 修改小时

LocalDateTime withDayOfMonth(int i) 修改日

LocalDateTime withMonth(int i) 修改月

LocalDateTime withYear(int i) 修改年


 

                                                                                      

                                                                    十八、with方法的使用方式2

with(TemporalField field, long newValue)

temporalField是一个接口,通过查看体系结构,可以使用它的子类

ChronoField,ChronoField中封装了一些日期时间中的组成成分,可以直接选择之后传入第二个参数进行修改。

例如:with(ChronoField.DAY_OFMONTH,1);将日期中的月份中的天数改为1

例如:with(ChronoField.YEAR,2021);将日期中的年份改为2021。


                                                   

                                                                                           十九、章节练习(2)

 

举例一种方式:

                                                            

                                                            二十、TemporalAdjuster调节器的使用

调节器TemporalAdjuster与查询TemporalQuery

with(TemporalAdjuster adjuster)

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

with方法有一个重载形式,需要传入一个TemporalAdjuster对象,通过查看发现TemporalAdjuster是一个接口,那么实际上传入的是这个接口的实现类对象。

 

 

在以上的描述中,发现了一个叫做TemporalAdjusters的类可以给我们提供一些常用的方法,方法如下:

TemporalAdjusters类中常用静态方法的使用

static TemporalAdjuster firstDayofNextMonth()   下个月的第一天

static TemporalAdjuster firstDayOfNextYear()   下一年的第一天

static TemporalAdjuster firstDayOfYear()    当年的第一天


 

注意:TemporalAdjusters 是一个接口,with方法实际上传入的是这个饥饿口的实现类对象,TemporalAdjusters并不是TemporalAdjuster的实现类,只不过TemporalAdjusters的静态方法实现了
TemporalAdjuster,并且将实现类对象返回了。


                                                                              二十一、DayOfWeek枚举类使用

DayOfWeek是一周中星期几的枚举类,其中封装了从周一到周日

 

                                               二十二、自定义TemporalAdjuster调节器

通过Java8本身提供的TemporalAdjusters中的方法可以完成一些常用的操作,如果要自定义日期时间的更改逻辑,可以通过实现TemporalAdjuster类接口的方式来完成。

1、创建类实现TemporalAdjuster接口

2、实现TemporalAdjuster中的 adjusterInto()方法,传入一个日期时间对象,完成逻辑之后返回日期事件对象。

3、通过with方法传入自定义调节器对象完成更改。

例如:假如员工一个月中领取工资,发薪日是每个月的15日,如果发薪日是周末,则调整为周五。



 

 

                                                                 二十三、TemporalQuery的应用

学习的时态类对象(LocalDate,LocalTime)都有一个方法叫做query,可以针对日期进行查询,R    query(TemporalQuery query)这个方法是一个泛型方法,返回的数据就是传入的泛型类的类型,TemporalQuery是一个泛型接口,里面有一个抽象方法是R    queryFrom(TemporalAccessor temporal)TemporalAccessor是Temporal的父接口,实际上也就是LocalDate,LocalDateTime相关类的顶级父接口,这个queryFrom的方法的实现逻辑就是,传入一个日期/时间对象通过自定义逻辑返回数据。

如果要计划日期距离某一天特定天数差距多少天,可以自定义类实现TemporalQuery接口并且作为参数传到query方法中。

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

 

                                                                    二十四、章节练习(3)

 

 

                                              二十五、java.util.Date转换为java.time.LocalDate(1)

对于老项目的改造,需要将Date或者Calendar转换为java.util包中相应的类的,可以根据本小节中提供的方法进行改造。

Java8中的java.time中并没有提供太多的内置方式来转换java.util包中用预处理标准日期和时间的类,我们可以使用Instant类作为中介,也可以使用java.sql.Date和java.sql.TimeStamp类提供的方法进行转换

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

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


                                                                     

                                                         十六、java.sql.Date与java.sql.Timestamp的转换方式

java.sql.Date类中提供直接转换为LocalDate的方法,toLocalDate()

java.sql.Timestamp类是时间戳对象,通过传入一个毫秒值对象进行初始化

 

                                             二十七、java.util.Date转换为java.time.LocalDate方式(2

将java.util.Date类转换为java.time.LocalDate类的第二种方法

java.sql.Date类提供了转换为LocalDate的方法,那么可以将java.util.Date先转换为java.sql.Date。

通过java.sql.Date的构造方法直接传入一个毫秒值可以构造一个java.sql.Date对象,毫秒值可以通过java.util.Date对象的getTime()方法获取到。

 

 

                                                  二十八、Calendar转换为ZonedDateTime

Calendar对象字Java1.1开始提供了一个方法获取时区对象的方法,getTimeZone(),要将Calendar对象转换为ZonedDateTime需要先获取到时区对象。从Java1.8开始TimeZone类提供了一个方法可以获取到ZonedId。获取到ZonedId之后就可以初始化ZOnedDateTime对象了,ZonedDateTime类有一个ofInstant()方法,可以将一个Instant对象和ZonedId对象作为参数传入构造一个ZonedDateTime对象。

 

                                                              二十九、Calendar转换为LocalDateTime

java.util.Calendar类转换为java.time.LocalDateTime类

Calendar对象可以获取到年月日时分秒的信息,这些信息可以作为LocalDateTime构造方法的参数


 

                                                               三十、新日期实践类的parse和format方法

SimpleDateFormat类在刚开始的时候讲过了是线程不安全的,所以Java8提供了新的格式化类 DateTimeFormatter

dateTimeFormatter类提供了大量预定义格式化器,包括常量(如ISO_LOCAL_DATE),模式字母(如yyyy-MM-dd)以及本地化样式。

与SimpleDateFormat不同的是,新版本的日期/时间API的格式化与解析不需要再创建转换器对象了,通过时间日期对象的

                                                           三十一、ofLocalizedDate方法

对日期进行格式化

通过DateTimeFormatter的ofLocalizedDate的方法也可以调整格式化的方式。

public static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) {

    Objects.requireNonNull(dataStyle, message:"dateStyle");

    return new DateTimeFormatterBuilder().appendLocalized(dateStyle,timeStyle:"null")

            .toFormatter(ResolverStyle.Smart, IsoChronology.Instance);

}


此方法需要传入一个FormatStyle类对象,擦好看后发现FormaStyle对象是一个枚举类,其中有几种方式如下:

Full:全显示(年月日+星期) Long:全显示(年月日) Medium:缩略显示(没有年月日汉字) SHORT:精简显示(精简年+月日)



 

                                                                       三十二、自定义格式化器

除了系统自带的方式之外,也可以通过DateTimeFormatter类提供的ofPattern方式创建自定时格式化器,格式化的写法与之前使用SimpleDateFormat相同。

 

 

 

                                                    三十三、章节练习(4)

 

结    束

COPY自

https://www.jianshu.com/p/20bfae706882

https://www.jianshu.com/p/7a1b367ae95f