Java8新特性——新一套時間API的使用
JDK 1.0中包含了一個java.util.Date類,但是它的大多數方法已經在JDK 1.1引入Calendar類之後被棄用了。而Calendar並不比Date好多少。它們面臨的問題是:
可變性:像日期和時間這樣的類應該是不可變的。
偏移性:Date中的年份是從1900開始的,而月份都從0開始。
格式化:格式化只對Date有用,Calendar則不行。
此外,它們也不是線程安全的;不能處理閏秒等。
關於線程安全問題,下面舉個例子
package com.xnn.time; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 類(接口)描述: * @author xnn * 2018年10月24日下午10:59:06 */ public class TestSimpleDateFormat { public static void main(String[] args) throws Exception { //定義日期格式 SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); Callable<Date> callable = new Callable<Date>() { @Override public Date call() throws Exception { return format.parse("20181024"); } }; //創建一個有10個線程的線程池 ExecutorService pool = Executors.newFixedThreadPool(10); List<Future<Date>> futures = new ArrayList<>(); for (int i = 0; i < 10; i++) { //把每個線程執行的結果放在集合中去 futures.add(pool.submit(callable)); } for (Future<Date> future : futures) { System.out.println(future.get()); } } }
運行結果
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points at java.util.concurrent.FutureTask.report(FutureTask.java:122) at java.util.concurrent.FutureTask.get(FutureTask.java:192) at com.xnn.time.TestSimpleDateFormat.main(TestSimpleDateFormat.java:37) Caused by: java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.lang.Double.parseDouble(Double.java:538) at java.text.DigitList.getDouble(DigitList.java:169) at java.text.DecimalFormat.parse(DecimalFormat.java:2056) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514) at java.text.DateFormat.parse(DateFormat.java:364) at com.xnn.time.TestSimpleDateFormat$1.call(TestSimpleDateFormat.java:28) at com.xnn.time.TestSimpleDateFormat$1.call(TestSimpleDateFormat.java:1) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
報了個並發異常的錯,
在Java8之前,我們都是通過加鎖來處理諸如此類的問題。
/** * */ package com.xnn.time; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * 類(接口)描述:線程池鎖 * @author xnn * 2018年10月25日上午9:39:07 */ public class DateFormatThreadLocal { private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { //重寫ThreadLocal類的initialValue()方法沒返回一個 protected DateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd"); } }; //解析時間 public static Date convert (String source) throws Exception { return df.get().parse(source); } } package com.xnn.time; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 類(接口)描述: * @author xnn * 2018年10月24日下午10:59:06 */ public class TestSimpleDateFormat { public static void main(String[] args) throws Exception { Callable<Date> callable = new Callable<Date>() { @Override public Date call() throws Exception { return DateFormatThreadLocal.convert("20181024"); } }; //創建一個有10個線程的線程池 ExecutorService pool = Executors.newFixedThreadPool(10); List<Future<Date>> futures = new ArrayList<>(); for (int i = 0; i < 10; i++) { //把每個線程執行的結果放在集合中去 futures.add(pool.submit(callable)); } for (Future<Date> future : futures) { System.out.println(future.get()); } pool.shutdown(); } }
運行結果
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
Wed Oct 24 00:00:00 CST 2018
第三次引入的API是成功的,並且java 8中引入的java.time API 已經糾正了過去的缺陷,將來很長一段時間內它都會為我們服務。
Java 8 吸收了 Joda-Time 的精華,以一個新的開始為 Java 創建優秀的 API。新的 java.time 中包含了所有關於本地日期(LocalDate)、本地時間(LocalTime)、本地日期時間(LocalDateTime)、時區(ZonedDateTime)和持續時間(Duration)的類。歷史悠久的 Date 類新增了 toInstant() 方法,用於把 Date 轉換成新的表示形式。這些新增的本地化時間日期 API 大大簡化了日期時間和本地化的管理.
java.time – 包含值對象的基礎包
java.time.chrono – 提供對不同的日歷系統的訪問
java.time.format – 格式化和解析時間和日期
java.time.temporal – 包括底層框架和擴展特性
java.time.zone – 包含時區支持的類
說明:大多數開發者只會用到基礎包和format包,也可能會用到temporal包。因此,盡管有68個新的公開類型,大多數開發者,大概將只會用到其中的三分之一。
現在有了新的時間日期API可以完美解決線程安全問題
package com.xnn.time;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 類(接口)描述:
* @author xnn
* 2018年10月24日下午10:59:06
*/
public class TestSimpleDateFormat {
public static void main(String[] args) throws Exception {
//用LocalDate類
Callable<LocalDate> callable = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
//定義格式用DateTimeFormater類
DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyyMMdd") ;
return LocalDate.parse("20181025",formatter);
}
};
//創建一個有10個線程的線程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
//把每個線程執行的結果放在集合中去
futures.add(pool.submit(callable));
}
for (Future<LocalDate> future : futures) {
System.out.println(future.get());
}
pool.shutdown();
}
}
本地時間與時間戳LocalDate LocalTime LocalDateTime
LocalDate、LocalTime、LocalDateTime 類是其中較重要的幾個類,它們的實例是不可變的對象,分別表示使用 ISO-8601日歷系統的日期、時間、日期和時間。它們提供了簡單的本地日期或時間,並不包含當前的時間信息,也不包含與時區相關的信息
package com.xnn.time;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import org.junit.Test;
/**
* 類(接口)描述:
* @author xnn
* 2018年10月25日上午10:39:10
*/
public class TestLocalDAteTime {
//1、LocalDate LocalTime LocalDateTime
@Test
public void test() {
//now()方法 獲取當前系統時間
LocalDateTime dateTime = LocalDateTime.now();
System.out.println("當前時間:"+dateTime);
LocalDateTime dateTime2 = LocalDateTime.of(2018, 10, 10, 10, 10, 10);
System.out.println("自定義的時間:"+dateTime2);
//對時間的運算
LocalDateTime plusYears = dateTime.plusYears(1);
System.out.println("對當前時間加一年:"+plusYears);
LocalDateTime minusYears = dateTime.minusYears(2);
System.out.println("對當前時間減去兩年:"+minusYears);
//get系列
System.out.println("得到當前時間的年:"+dateTime.getYear());
System.out.println("得到當前時間的年月;"+dateTime.getMonth().getValue());
}
}
運行結果:
當前時間:2018-10-25T12:02:21.250
自定義的時間:2018-10-10T10:10:10
對當前時間加一年:2019-10-25T12:02:21.250
對當前時間減去兩年:2016-10-25T12:02:21.250
得到當前時間的年:2018
得到當前時間的年月;10
Instant:時間戳(以uninx元年:1970年1月1日00:00:00到某個時間的毫秒值)
//Instant:時間戳(以uninx元年:1970年1月1日00:00:00到某個時間的毫秒值)
@Test
public void test1() {
Instant instant = Instant.now();//默認獲取UTC時區,我們是東八區,相差八小時
System.out.println("當前UTC時區的時間:"+instant);
//帶偏移量的
OffsetDateTime offset = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println("偏移了8小時的時區的時間"+offset);
//轉化成毫秒值
System.out.println("毫秒值:"+instant.toEpochMilli());
//從1970年1月1日00:00:00加了1000毫秒
Instant instant2 =Instant.ofEpochMilli(1000);
System.out.println("從1970年一月一日加上一秒:"+instant2);
}
結果:
當前UTC時區的時間:2018-10-25T04:07:31.595Z
偏移了8小時的時區的時間2018-10-25T12:07:31.595+08:00
毫秒值:1540440451595
從1970年一月一日加上一秒:1970-01-01T00:00:01Z
Duration Period
//Duration:計算兩個時間的間隔
//Period:計算兩個日期間的間隔
@Test
public void test2() throws Exception {
Instant instant = Instant.now();
Thread.sleep(1000);
Instant instant1 = Instant.now();
Duration duration = Duration.between(instant, instant1);
System.out.println("間隔是"+duration.toMillis()+"毫秒");
System.out.println("=============");
LocalDateTime time = LocalDateTime.now();
Thread.sleep(1000);
LocalDateTime time1 = LocalDateTime.now();
Duration duration1 = Duration.between(time, time1);
System.out.println(duration1.toMillis()+"毫秒");
//兩個日期的間隔
LocalDate date = LocalDate.of(2018, 1, 1);
LocalDate date1 = LocalDate.now();
Period period = Period.between(date, date1);
System.out.println("兩個日期的間隔是"+period.getYears()+"年"+period.getMonths()+"月"+period.getDays()+"天");
}
運行結果
間隔是1000毫秒
=============
1000毫秒
兩個日期的間隔是0年9月24天
TemporalAdjuster:時間校正器
//TemporalAdjuster:時間校正器
@Test
public void test3() throws Exception {
LocalDateTime dateTime =LocalDateTime.now();
System.out.println("當前時間是:"+dateTime);
//把月中的天指定為自己設定的數值
LocalDateTime dayOfMonth = dateTime.withDayOfMonth(10);
System.out.println("自己設定的值:"+dayOfMonth);
//下個周日
TemporalAdjuster adjuster = TemporalAdjusters.next(DayOfWeek.SUNDAY);
LocalDateTime with = dateTime.with(adjuster );
System.out.println("下個周日是"+with);
//也可以自定義(比如下個周日)
LocalDateTime with2 = dateTime.with((l)->{
LocalDateTime dateTime1 =(LocalDateTime)l;
if(dateTime1.equals(DayOfWeek.FRIDAY)) {
return dateTime1.plusDays(3);
}else if(dateTime1.equals(DayOfWeek.SATURDAY)) {
return dateTime1.plusDays(2);
}
else {
return dateTime1.plusDays(1);
}
});
System.out.println("下個工作日是:"+with2);
}
運行結果
當前時間是:2018-10-25T12:11:46.891
自己設定的值:2018-10-10T12:11:46.891
下個周日是2018-10-28T12:11:46.891
下個工作日是:2018-10-26T12:11:46.891
DateTimeFormater:格式化日期時間
//DateTimeFormater:格式化日期時間
@Test
public void test4() throws Exception {
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE;
LocalDateTime dateTime = LocalDateTime.now();
String format = dateTime.format(isoDateTime);
System.out.println("ISO_DATE格式:"+format);
System.out.println("+++++++++++++++++");
//把時間日期指定為特定格式的字符串形式
DateTimeFormatter isoDateTime1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String format2 = dateTime.format(isoDateTime1);
System.out.println("\"ISO_DATE格式:\"+格式"+format2);
System.out.println("+++++++++++++++++");
//解析日期時間(按照特定的格式)
LocalDateTime parse = LocalDateTime.parse(format2, isoDateTime1);
System.out.println("解析後得到時間:"+parse);
}
ZonedDate ZonedDate ZoneDateTime 帶時區的操作
//ZonedDate ZonedDate ZoneDateTime 帶時區的操作
@Test
public void test5() throws Exception {
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
ZonedDateTime zone = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("上海時區"+zone);
}
運行結果
2018-10-25T12:20:58.861
上海時區2018-10-25T12:20:58.861+08:00[Asia/Shanghai]
Java8新特性——新一套時間API的使用