旧版日期时间API存在的问题
-
设计很差: 在java.util和java.sql的包中都有日期类, java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期。此外用于格式化和解析的类在java.text包中定义.
-
非线程安全: java.util.Date是非线程安全的,所有的日期类都是可变的,这是java日期类最大的问题之一.
-
时区处理麻烦: 日期类并不提供国家化,没有时区支持,因此java引入java.util.Calendar和java.util.TimeZone类,但是他们同样存在上述所有的问题.
/** * 1. 设计不合理 */ Date now = new Date(1985, 9, 23); System.out.println(now); /** * 线程不完全 */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); for (int i = 0; i < 50; i++) { new Thread(() -> { Date date = null; try { date = sdf.parse("2020-09-09"); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date); }); }
输出结果
Connected to the target VM, address: '127.0.0.1:60324', transport: 'socket'
Fri Oct 23 00:00:00 CST 3885
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Thu Sep 09 00:00:00 CST 2202
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
date=Wed Sep 09 00:00:00 CST 2020
Exception in thread "Thread-40" java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.example.jdk.demo07datetime.Test01.lambda$main$0(Test01.java:23)
at java.lang.Thread.run(Thread.java:748)
Disconnected from the target VM, address: '127.0.0.1:60324', transport: 'socket'
Process finished with exit code 0
新的日期时间API介绍
JDK 8中增加了一套全新的日期时间API, 这套API设计合理,是线程安全的。 新的日期及时间API位于java.time包中,下面是一些关键类.
LocalDate: 表示日期,包含年月日, 格式为2019-10-19
LocalTime: 表示时间,包含时分秒,格式为 16:53:33.143423434
LocalDateTime:表示日期时间,包含年月日,时分秒 格式为 2018-10-12T15:12:13.234
DateTimeFormater: 日期时间格式化
Instant: 时间戳,表示一个特定的时间瞬间
Duration: 用于计算两个时间(LocalTime,时分秒)的间隔
Period: 用于计算2个日期(LocalDate,年月日)的间隔
ZoneDateTime: 包含时区的时间
JDK 8的日期和时间类
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间. 他们提供简单的日期和时间,并不包含当前的日期信息,也不包含与时区相关的信息
public static void testLocalDate() {
//当前日期
LocalDate now = LocalDate.now();
System.out.println(now);
//指定日期
LocalDate date = LocalDate.of(2018, 12, 12);
System.out.println(date);
//获取指定日期
System.out.println("year= " + now.getYear());
System.out.println("month= " + now.getMonthValue());
System.out.println("date= " + now.getDayOfMonth());
}
public static void testLocalTime() {
//LocalTime 表示时间,有时分秒
//当前时间
LocalTime nowTime =LocalTime.now();
System.out.println(nowTime);
//指定时间
LocalTime localTime =LocalTime.of(13,22,34);
System.out.println(localTime);
//获取时间
System.out.println(nowTime.getHour());
System.out.println(nowTime.getMinute());
System.out.println(nowTime.getSecond());
System.out.println(nowTime.getNano());
}
public static void testLocalDateTime() {
//LocalDateTime LocalDate+LocalTime,有年月日 时分秒
//当前时间
LocalDateTime nowDateTime =LocalDateTime.now();
System.out.println(nowDateTime);
//指定时间
LocalDateTime localTime =LocalDateTime.of(2018,7,23,13,22,34);
System.out.println(localTime);
//获取时间
System.out.println("year= " + nowDateTime.getYear());
System.out.println("year= " + nowDateTime.getMonthValue());
System.out.println("date= " + nowDateTime.getDayOfMonth());
System.out.println(nowDateTime.getHour());
System.out.println(nowDateTime.getMinute());
System.out.println(nowDateTime.getSecond());
System.out.println(nowDateTime.getNano());
}
/**
* 修改时间
*/
@Test
public void editLocalDateTime(){
LocalDateTime nowDateTime =LocalDateTime.now();
//修改后返回新的时间
LocalDateTime localDateTime = nowDateTime.withYear(9102);
System.out.println("nowDateTime= " + nowDateTime);
System.out.println("localDateTime= " + localDateTime);
//增年份: plus
LocalDateTime plusYears = nowDateTime.plusYears(2);
System.out.println("years+2 ="+plusYears);
//减年份: minus
LocalDateTime minusYears = nowDateTime.minusYears(2);
System.out.println("years-2 ="+minusYears);
}
/**
* 比较时间
*/
@Test
public void compareLocalDateTime(){
LocalDateTime nowDateTime =LocalDateTime.now();
LocalDateTime localTime =LocalDateTime.of(2018,7,23,13,22,34);
System.out.println(nowDateTime.isAfter(localTime));
System.out.println(nowDateTime.isBefore(localTime));
System.out.println(nowDateTime.isEqual(localTime));
}
日期的时间格式化与解析
通过java.time.format.DateTimeFormater类进行日期时间解析与格式化
/**
* 日期格式化
*/
@Test
public void formatLocalDateTime(){
LocalDateTime nowDateTime =LocalDateTime.now();
//格式化,指定时间的格式,jdk 自带的格式
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE_TIME;
String format = nowDateTime.format(isoDateTime);
System.out.println(format);
//指定格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dateTimeFormatter);
//解析
LocalDateTime parse = LocalDateTime.parse("2017-09-09 15:13:23",dateTimeFormatter);
System.out.println(parse);
for (int i = 0; i < 50; i++) {
new Thread(()->{
LocalDateTime parse2 = LocalDateTime.parse("2017-09-09 15:13:23",dateTimeFormatter);
System.out.println("parse2 ="+parse2);
}).start();
}
}
JDK8的Instant类
Instant时间戳/时间线,内部保存了1970年1月1日00:00:00以来的秒和纳秒
/**
* 获取秒/纳秒
*/
@Test
public void testInstant(){
//instant 内部保存了秒和纳秒,一般不是给用户使用的,而是方便我们程序做一些统计
Instant now = Instant.now();
System.out.println(now);
//增加秒
Instant plus = now.plusSeconds(20);
//减少秒
Instant minus = now.minusSeconds(120);
//获取秒/纳秒
System.out.println(now.getEpochSecond());
System.out.println(now.getNano());
}
JDK8计算日期时间差类
Duration/Period类:计算日期时间差
-
Duration: 用于计算2个时间(LocalTime,时分秒)的间隔
-
Period: 用于计算2个日期(LocalDate,年月日)的距离
@Test
public void testDuration(){
//Duration 计算时间的间隔
LocalDateTime now = LocalDateTime.now();
LocalDateTime localTime =LocalDateTime.of(2018,7,23,13,22,34);
Duration duration = Duration.between(now, localTime);
System.out.println("相差的天数:" + duration.toDays());
System.out.println("相差的小时数:" + duration.toHours());
System.out.println("相差的分钟数:" + duration.toMinutes());
System.out.println("相差的秒数:" + duration.toMillis());
//pperiod日期日期的间隔
LocalDate now1 = LocalDate.now();
LocalDate localDate = LocalDate.of(2018, 12, 12);
//between 让后面的时间减去前面的时间
Period period = Period.between(localDate, now1);
System.out.println("相差的年数:" + period.getYears());
System.out.println("相差的月份:" + period.getMonths());
System.out.println("相差的天数:" + period.getDays());
}
jdk8 中的时间校正器
有时我们可能需要获取例如: 将日期调整到"下一个月的第一天"等操作. 可以通过时间校正器来进行
- TemporalAdjuster: 时间校正器
- TemporalAdjusters: 该类通过静态方法提供了大量的常用TemporalAdjuster的实现
@Test
public void testTemporalAdjuster(){
//将日期调整到下一个月的第一天
LocalDateTime now = LocalDateTime.now();
LocalDateTime with = now.with(TemporalAdjusters.firstDayOfMonth());
System.out.println(with);
}
设置日期时间的时区
java8 中加入了对时区的支持,LocalDate、LocalTime、LocalDateTime是不带时区的,带时区的日期时间类分别为: ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着ID. ID的格式为"区域/城市". 例如: Asia/ShangHai 等
ZoneId: 该类包含了所有的时区信息.
@Test
public void testZonedDate(){
// 获取所有的时区id
ZoneId.getAvailableZoneIds().forEach(System.out::println);
//不带时区
LocalDateTime now = LocalDateTime.now();
System.out.println("now: "+now);
//带时区 标准世界时间
ZonedDateTime zonedDateTime = ZonedDateTime.now(Clock.systemUTC());
System.out.println("zonedDateTime: "+zonedDateTime);
//now(): 使用计算机的默认时区
ZonedDateTime zonedDateTime2 = ZonedDateTime.now();
System.out.println("zonedDateTime2: "+zonedDateTime2);
//指定的时区创建时间
ZonedDateTime zonedDateTime3 = ZonedDateTime.now(ZoneId.of("America/Vancouver"));
System.out.println("zonedDateTime3: "+zonedDateTime3);
//修改时区,时间
ZonedDateTime withZoneSameInstant = zonedDateTime2.withZoneSameInstant(ZoneId.of("Asia/ShangHai"));
System.out.println("withZoneSameInstant: "+withZoneSameInstant);
//只改时区 不改时间
ZonedDateTime withZoneSameLocal = zonedDateTime2.withZoneSameLocal(ZoneId.of("Asia/ShangHai"));
System.out.println("withZoneSameLocal: "+withZoneSameLocal);
}