JavaSE原理-日期类API
学习目标
掌握Java8中的提供的java.time包中的常用日期类与相关方法
可以从java.util包的下的日期类相关类过渡到java.time包下的日期类
掌握Java8中的日期与字符串之间的相互转换
1.为什么会出现新的日期类API
将java.util.Date类束之高阁才是正确之道 -> Tim Yates
在Java面世之初,标准库就引入了两种用于处理日期和时间的类,它们是
java.util.Date和java.util.Calendar,而前者堪称类糟糕设计的典范,浏览
API可以发现,从Java1.1开始,Date类中的所有方法就已经被弃用,Java1.1推
荐采用Calendar类处理日期和时间,但是这个类同样存在不少问题.
对于日期的计算困难问题.
毫秒值与日期直接转换比较繁琐,其次通过毫秒值来计算时间的差额步骤较多.
package com.itheima.time;
import java.util.Calendar;
import java.util.Date;
/**
* 计算当前时间距离2000年6月1日相差了多少天.
*
* 通过距离1970年1月1日的毫秒差值,可以计算出两个日期之间相隔的天数.
*/
public class JavaUtilTimeDemo01 {
public static void main(String[] args) {
//1.初始化Date对象,无参构造(无参构造默认代表的就是当前时间)
Date dateNow = new Date();
//2.获取当前时间的距离1970年1月1日过了多少毫秒.
long dateTimeNow = dateNow.getTime();
//3.初始化Calendar对象并设时间为2006年6月1日并且将Calendar对象转换
为Date对象.
Calendar paramterTime = Calendar.getInstance();
paramterTime.set(2000, Calendar.JUNE, 1);
Date paramterDateTime = paramterTime.getTime();
//4.计算paramterDateTime与dateTimeNow之间的毫秒差额.
Long intervalTime = dateTimeNow -
paramterDateTime.getTime();
//5.对intervalTime进行计算获取差额,毫秒值/1000->/60->/60->/24
long intervalDay = intervalTime / 1000 / 60 / 60 / 24;
System.out.println("当前时间距离2000年6月1日已经过了" +
intervalDay+"天.");
}
}
线程安全问题
SimpleDateFormat类是线程不安全的,在多线程的情况下,全局共享一个
SimpleDateFormat类中的Calendar对象有可能会出现异常.
package com.itheima.time;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class JavaUtilTimeDemo02 {
//创建SimpleDateFormat的对象(单例)
static SimpleDateFormat SIMPLEDATEFORMAT = 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 {
System.out.println(SIMPLEDATEFORMAT.parse("2018-12-12 12:12:12"));
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
}
另外的一个问题就是在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包的开
发.
2.Date-Time API中的基本类使用
常用类的概述与功能介绍
Instant类
Instant类对时间轴上的单一瞬时点建模,可以用于记录应用程序中的事件时间戳,在之
后学习的类型转换中,均可以使用Instant类作为中间类完成转换.
Duration类
Duration类表示秒或纳秒时间间隔,适合处理较短的时间,需要更高的精确性.
Period类
Period类表示一段时间的年、月、日.
LocalDate类
LocalDate是一个不可变的日期时间对象,表示日期,通常被视为年月日.
LocalTime类
LocalTime是一个不可变的日期时间对象,代表一个时间,通常被看作是小时-秒,时间表
示为纳秒精度.
LocalDateTime类
LocalDateTime是一个不可变的日期时间对象,代表日期时间,通常被视为年-月-日-
时-分-秒.
ZonedDateTime类
ZonedDateTime是具有时区的日期时间的不可变表示,此类存储所有日期和时间字段,精
度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。
now方法在日期/时间类的使用
Date-Time API中的所有类均生成不可变实例,它们是线程安全的,并且这些类
不提供公共构造函数,也就是说没办法通过new的方式直接创建,需要采用工厂方
法加以实例化.
now方法可以根据当前日期或时间创建实例.
package com.itheima.time;
import java.time.*;
public class Java8TimeClassMethodDemo1 {
public static void main(String[] args) {
//使用now方法创建Instant的实例对象.
Instant instantNow = Instant.now();
//使用now方法创建LocalDate的实例对象.
LocalDate localDateNow = LocalDate.now();
//使用now方法创建LocalTime的实例对象.
LocalTime localTimeNow = LocalTime.now();
//使用now方法创建LocalDateTime的实例对象.
LocalDateTime localDateTimeNow = LocalDateTime.now();
//使用now方法创建ZonedDateTime的实例对象.
ZonedDateTime zonedDateTimeNow = ZonedDateTime.now();
//将实例对象打印到控制台.
System.out.println("Instant:"+instantNow);
System.out.println("LocalDate:"+localDateNow);
System.out.println("LocalTime:"+localTimeNow);
System.out.println("LocalDateTime:"+localDateTimeNow);
System.out.println("ZonedDateTime:"+zonedDateTimeNow);
}
}
各个类封装时间所表示的特点
Instant封装的时间为祖鲁时间并非当前时间.
祖鲁时间也是格林尼治时间,也就是国际标准时间.
LocalDate封装的只有年月日,没有时分秒,格式为yyyy-MM-dd.
LocalTime封装的只有时分秒,没有年月日,格式为hh:mm:ss.sss,最
后的sss是纳秒.
LocalDateTime将LocalDate和LocalTime合二为一,在年月日与时分
秒中间使用T作为分隔.
ZonedDateTime中封装了年月日时分秒,以及UTC(祖鲁时间)偏移量,并
且还有一个地区名.
+8:00代表中国是东八区,时间比国际标准时间快八小时.
不仅仅是刚才提供的几个类可以使用now方法,Java8的Time包中还提供了其他的几个类可以更精
准的获取某些信息.
Year类(表示年)
YearMonth类(表示年月)
MonthDay类(表示月日)
package com.itheima.time;
import java.time.*;
public class Java8TimeClassMethodDemo2 {
public static void main(String[] args) {
//初始化Year的实例化对象.
Year year = Year.now();
//初始化YearMonth的实例化对象
YearMonth month = YearMonth.now();
//初始化MonthDay的实例化对象.
MonthDay day = MonthDay.now();
}
}
of方法在日期/时间类的应用
of方法可以根据给定的参数生成对应的日期/时间对象,基本上每个基本类都有
of方法用于生成的对应的对象,而且重载形式多变,可以根据不同的参数生成对
应的数据.
package com.itheima.time;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Java8TimeClassMethodDemo3 {
public static void main(String[] args) {
//初始化2018年8月8日的LocalDate对象.
LocalDate date = LocalDate.of(2018, 8, 8);
System.out.println("LocalDate:" + date);
/*
初始化晚上7点0分0秒的LocalTime对象.
LocalTime.of方法的重载形式有以下几种,可以根据实际情况自行使用.
LocalTime of(int hour, int minute) -> 根据小时/分钟生成对象.
LocalTime of(int hour, int minute, int second) -> 根据小时/分钟/秒生成
对象.
LocalTime of(int hour, int minute, int second, int nanoOfSecond) ->
根据小时/分钟/毫秒/纳秒生成对象.
注意:如果秒和纳秒为0的话,那么默认不会封装这些数据,只显示小时和分钟.
*/
LocalTime time = LocalTime.of(19, 0, 0, 0);
System.out.println("LocalTime:" + time);
/*
初始化2018年8月8日下午7点0分的LocalDateTime对象.
LocalDateTime.of方法的重载形式有以下几种,可以根据事情自行使用.
LocalDateTime of(int year, int month, int dayOfMonth, int hour, int
minute, int second, int nanoOfSecond) -> 根据年/月/日/时/分/秒生成对象.
LocalDateTime of(int year, int month, int dayOfMonth, int hour, int
minute) -> 根据年/月/日/时/分生成对象.
注意:LocalDateTime of(LocalDate date, LocalTime time)方法可以将一个
LocalDate对象和一个LocalTime对象合并封装为一个LocalDateTime对象.
*/
LocalDateTime.of(2018, 8, 8, 19, 0, 0, 0);
LocalDateTime localDateTime = LocalDateTime.of(date, time);
System.out.println("LocalDateTime:" + localDateTime);
}
}
为LocalDateTime添加时区信息(拓展)
在学习ZonedDateTime的时候,发现了这个对象里面封装的不仅有时间日期,并且还有偏移量+时
区,那么时区如何在Java中获取呢,通过提供的一个类ZoneId的getAvailableZoneIds方法可以
获取到一个Set集合,集合中封装了600个时区.
//获取所有的时区信息
Set availableZoneIds = ZoneId.getAvailableZoneIds();
for (String zoneId : availableZoneIds) {
System.out.println(zoneId);
}
同样也提供了获取当前系统默认的时区的方式systemDefault()方法.
//获取当前系统默认的时区信息
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);
我们可以通过给LocalDateTime添加时区信息来查看到不同时区的时间,比如说LocalDateTime中
当前封装的是上海时间,那么想知道在此时此刻,纽约的时间是什么,就可以将纽约的时区Id添加进
去,就可以查看到了,方式如下.
封装时间LocalDateTime并添加时区信息.
更改时区信息查看对应时间.
package com.itheima.time;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
/**
* 为LocalDateTime添加时区信息.
*/
public class Java8TimeClassMethodDemo5 {
public static void main(String[] args) {
//1.封装LocalDateTime对象,参数自定义 -> 2018年11月11日 8点54分38秒
LocalDateTime time = LocalDateTime.of(2018, 11, 11, 8, 54, 38);
//2.封装完成后的time对象只是封装的是一个时间,并没有时区相关的数据,所以添加时区到
对象中,使用atZone方法.
ZonedDateTime zonedDateTime =
time.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("Asia/Shanghai的时间是:" + zonedDateTime);
//3.更改时区查看其它时区的当前时间,通过withZoneSameInstant方法即可更改.
ZonedDateTime otherZonedTime =
zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("在同一时刻,Asia/Tokyo的时间是:" + otherZonedTime);
}
}
Month枚举类的使用
java.time包中引入了Month的枚举,Month中包含标准日历中的12个月份的常
量(从JANURAY到DECEMEBER)也提供了一些方便的方法供我们使用.
推荐在初始化LocalDate和LocalDateTime对象的时候,月份的参数使用枚举的
方式传入,这样更简单易懂而且不易出错,因为如果是老的思维,Calendar传入0
的话,那么会出现异常.
/**
* Month枚举类的使用.