Java8新时间与日期类
1. 旧的时间日期类
1.1 JDK1.0
public class Date implements java.io.Serializable, Cloneable, Comparable<Date>
1.2 JDK1.1
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>
abstract public class TimeZone implements Serializable, Cloneable
1.3 缺点
1.4 线程不安全例子
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.*;
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> call = () -> sdf.parse("20161218");
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(call));
}
for(Future<Date> item: results){
System.out.println(item.get());
}
}
}
Thu Jun 18 00:00:00 CST 2201
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 se.newdate.TestSimpleDateFormat.main(TestSimpleDateFormat.java:18)
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:2089)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at se.newdate.TestSimpleDateFormat.lambda$main$0(TestSimpleDateFormat.java:11)
1.5 使用ThreadLocal解决线程不安全
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Date> call = () -> DateFormatThreadLocal.convert("20161218");
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<Date>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(call));
}
for(Future<Date> item: results){
System.out.println(item.get());
}
pool.shutdown();
}
}
class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
public static Date convert(String source) throws ParseException {
return df.get().parse(source);
}
}
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Process finished with exit code 0
1.6 使用线程安全的LocalDate
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.concurrent.*;
public class TestSimpleDateFormat {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<LocalDate> call = () -> LocalDate.parse("20161218", DateTimeFormatter.ofPattern("yyyyMMdd"));
ExecutorService pool = Executors.newFixedThreadPool(10);
ArrayList<Future<LocalDate>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(pool.submit(call));
}
for(Future<LocalDate> item: results){
System.out.println(item.get());
}
pool.shutdown();
}
}
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
2016-12-18
Process finished with exit code 0
2. 新时间日期类
2.1 概述
LocalDate、LocalTime、LocalDateTime
类的实例是不可变的对象,分别表示使用ISO-8601
日历系统的日期、时间、日期和时间。
- 提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
ISO-8601
日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
2.2 本地时间
import java.time.LocalDateTime;
public class TestLocalDateTime {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("获取当前系统时间:LocalDateTime.now() ==> " + now);
LocalDateTime ldt = LocalDateTime.of(2015, 10, 19, 13, 22, 33);
System.out.println("指定时间:LocalDateTime.of(2015, 10, 19, 13, 22, 33) ==> " + ldt);
LocalDateTime localDateTime = now.plusYears(2);
System.out.println("当前时间+2年:now.plusYears(2) ==> " + localDateTime);
LocalDateTime localDateTime1 = now.minusMinutes(2);
System.out.println("当前时间-2分钟:now.minusMinutes(2) ==> " + localDateTime1);
System.out.println(now.getYear());
System.out.println(now.getMonthValue());
System.out.println(now.getDayOfMonth());
System.out.println(now.getHour());
System.out.println(now.getMinute());
System.out.println(now.getSecond());
}
}
获取当前系统时间:LocalDateTime.now() ==> 2021-08-04T07:15:45.764
指定时间:LocalDateTime.of(2015, 10, 19, 13, 22, 33) ==> 2015-10-19T13:22:33
当前时间+2年:now.plusYears(2) ==> 2023-08-04T07:15:45.764
当前时间-2分钟:now.minusMinutes(2) ==> 2021-08-04T07:13:45.764
2021
8
4
7
15
45
2.3 时间戳
- 以 Unix 元年:1970年1月1日0时0分0秒至今的毫秒值
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
public class TestLocalDateTime {
public static void main(String[] args) {
Instant now = Instant.now();
System.out.println("默认获取UTC时区:Instant.now() ==> " + now);
OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
System.out.println("带偏移量的时间:now.atOffset(ZoneOffset.ofHours(8)) ==> " + offsetDateTime);
long l = now.toEpochMilli();
System.out.println("转时间戳:now.toEpochMilli() ==> " + l);
Instant instant = Instant.ofEpochSecond(60);
System.out.println("对Unix元年+60秒:Instant.ofEpochSecond(60) ==> " + instant);
}
}
默认获取UTC时区:Instant.now() ==> 2021-08-04T02:25:29.498Z
带偏移量的时间:now.atOffset(ZoneOffset.ofHours(8)) ==> 2021-08-04T10:25:29.498+08:00
转时间戳:now.toEpochMilli() ==> 1628043929498
对Unix元年+60秒:Instant.ofEpochSecond(60) ==> 1970-01-01T00:01:00Z
2.4 时间间隔
import java.time.*;
public class TestLocalDateTime {
public static void main(String[] args) {
Instant before = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant last = Instant.now();
Duration between = Duration.between(before, last);
System.out.println("计算时间戳之间的间隔:Duration.between(before, last) ==> " + between.toMillis());
LocalTime lt1 = LocalTime.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
LocalTime lt2 = LocalTime.now();
Duration between1 = Duration.between(lt1, lt2);
System.out.println("计算LocalTime之间的间隔:Duration.between(lt1, lt2) ==> " + between1.toMillis());
}
}
计算时间戳之间的间隔:Duration.between(before, last) ==> 1013
计算LocalTime之间的间隔:Duration.between(lt1, lt2) ==> 1011
import java.time.*;
public class TestLocalDateTime {
public static void main(String[] args) {
LocalDate ld1 = LocalDate.of(2015,1,1);
LocalDate ld2 = LocalDate.now();
Period between1 = Period.between(ld1, ld2);
System.out.println("计算LocalDate之间的间隔:Period.between(ld1, ld2) ==> " +
between1.getYears() + "年" +
between1.getMonths() + "月" +
between1.getDays() + "天");
}
}
计算LocalDate之间的间隔:Period.between(ld1, ld2) ==> 6年7月3天
2.5 时间校正器
- TemporalAdjuster:时间校正器。有时我们需要获取如 将日期调整到下一个周日等操作
- TemporalAdjusters:该类通过静态方法提供大量常用TemporalAdjuster的实现
import java.time.*;
import java.time.temporal.TemporalAdjusters;
public class TestLocalDateTime {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("校正之前:" + now);
LocalDateTime with = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("下一个周日:now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)) ==> " + with);
LocalDateTime with1 = now.with(l -> {
LocalDateTime tmp = (LocalDateTime) l;
DayOfWeek dayOfWeek = tmp.getDayOfWeek();
if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
tmp.plusDays(3);
} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
tmp.plusDays(2);
} else {
tmp.plusDays(1);
}
return tmp;
});
System.out.println("自定义计算下一个工作日:" + with1);
}
}
校正之前:2021-08-04T10:51:48.043
下一个周日:now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)) ==> 2021-08-08T10:51:48.043
自定义计算下一个工作日:2021-08-04T10:51:48.043
2.6 时间日期格式化
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class TestLocalDateTime {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
LocalDateTime now = LocalDateTime.now();
String format = now.format(formatter);
System.out.println("DateTimeFormatter.ISO_DATE 格式化方式:" + format);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String format1 = now.format(dateTimeFormatter);
System.out.println("自定义格式化:DateTimeFormatter.ofPattern(\"yyyy年MM月dd日 HH:mm:ss\") ==> " + format1);
LocalDateTime parse = now.parse(format1, dateTimeFormatter);
System.out.println("自定义格式转回LocalDateTime:now.parse(format1, dateTimeFormatter) ==> " + parse);
}
}
DateTimeFormatter.ISO_DATE 格式化方式:2021-08-04
自定义格式化:DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss") ==> 2021年08月04日 11:02:12
自定义格式转回LocalDateTime:now.parse(format1, dateTimeFormatter) ==> 2021-08-04T11:02:12
2.7 时区处理
- Java8加入对时区的支持,带时区的时间:ZonedDate ZonedTime ZonedDateTime
- 每个时区对应有一个ID,地区ID都为
{区域}/{城市}
的格式
- 例如:Asia/Shanghai 等
- ZonedId:该类中包含了所有的时区信息
- getAvailableZoneIds():可以获取所有时区的信息
- of(id):用指定的时区信息获取ZoneId对象
2.7.1 查看所有时区
import java.time.ZoneId;
import java.util.Set;
public class TestLocalDateTime {
public static void main(String[] args) {
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
for (String str: availableZoneIds){
System.out.println(str);
}
}
}
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El_Salvador
Asia/Pontianak
...
2.7.2 获取指定时区当前时间
import java.time.LocalDateTime;
import java.time.ZoneId;
public class TestLocalDateTime {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now(ZoneId.of("America/Marigot"));
System.out.println("获取时区\"America/Marigot\"的当前时间:LocalDateTime.now(ZoneId.of(\"America/Marigot\")) ==> " + now);
}
}
获取时区"America/Marigot"的当前时间:LocalDateTime.now(ZoneId.of("America/Marigot")) ==> 2021-08-03T23:15:20.208
2.7.3 获取指定时区时间偏移
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class TestLocalDateTime {
public static void main(String[] args) {
LocalDateTime now1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zonedDateTime = now1.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(zonedDateTime);
}
}
2021-08-04T11:21:01.579+08:00[Asia/Shanghai]