1. 概述
1.1 简介
Java 8 引入了一套全新的时间日期API,操作起来更简便。简单介绍下,LocalDate
和LocalTime
和LocalDateTime
的使用;
java.util.Date
月份从0开始,java.time.LocalDate
月份从1开始并且提供了枚举。
java.util.Date
和SimpleDateFormatter
都不是线程安全的,而LocalDate
和LocalTime
和最基本的String
一样,是不变类型,不但线程安全,而且不能修改,它们分别使用 ISO-8601 日历系统的日期和时间,它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
注 : ISO-8601 日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
1.3 环境
2. LocalDate、LocalTime、LocalDateTime
LocalDate
、LocalTime
、LocalDateTime
三者的使用方式基本一致,所以我们这里就以 LocalDateTime
为例进行索命
2.1 实例
@Testpublic void t1() { // 获取当前时间 LocalDateTime ldt1 = LocalDateTime.now(); System.out.println(ldt1); // 获取指定的时间 LocalDateTime ldt2 = LocalDateTime.of(2018, 12, 10, 10, 10, 10); System.out.println(ldt2); // 加两年 LocalDateTime ldt3 = ldt1.plusYears(2); System.out.println(ldt3); // 减两个月 LocalDateTime ldt4 = ldt1.minusYears(2); System.out.println(ldt4); // 获取年月日时分秒 System.out.println(ldt1.getYear()); System.out.println(ldt1.getMonthValue()); System.out.println(ldt1.getDayOfMonth()); System.out.println(ldt1.getHour()); System.out.println(ldt1.getMinute()); System.out.println(ldt1.getSecond());}
2.2 Instant : 时间戳
使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值
@Testpublic void t2() { Instant ins = Instant.now(); //默认使用 UTC 时区 System.out.println(ins); // 时间偏移量 OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8)); System.out.println(odt); // 获取毫秒值 System.out.println(ins.getNano()); // 对于元年开始进行运算 Instant ins2 = Instant.ofEpochSecond(60); System.out.println(ins2);}
2.3 Duration : 用于计算两个“时间”间隔
@Testpublic void t3() throws InterruptedException { // 时间戳 Instant ins1 = Instant.now(); Thread.sleep(1000); Instant ins2 = Instant.now(); Duration duration = Duration.between(ins1, ins2); System.out.println("所耗费时间为:" + duration.toMillis()); System.out.println("--------------------------------"); // LocalTime LocalTime lt1 = LocalTime.now(); Thread.sleep(1000); LocalTime lt2 = LocalTime.now(); System.out.println("所耗费时间为:" + Duration.between(lt1, lt2).toMillis());}
2.4 Period : 用于计算两个“日期”间隔
@Testpublic void t5() { LocalDate ld1 = LocalDate.of(2017, 1, 1); LocalDate ld2 = LocalDate.now(); Period period = Period.between(ld1, ld2); System.out.println("相差" + period.getYears() + "年" + period.getMonths() + "月" + period.getDays() + "天");}
2.5 日期的操作
TemporalAdjuster
时间校正器,有时我们可能需要获取例如:将日期调整到下个周日等操作。
TemporalAdjusters
该类通过静态方法提供了大量的常用 TemporalAdjuster
的实现。
@Testpublic void t6() { LocalDateTime ldt = LocalDateTime.now(); // 指定这个月的一个固定日期 LocalDateTime ldt2 = ldt.withDayOfMonth(10); System.out.println(ldt2); // 获取下个周日 LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); System.out.println("获取下个周日 : " + ldt3); // 自定义:获取下个工作日 LocalDateTime ldt5 = ldt.with((l) -> { LocalDateTime ldt6 = (LocalDateTime) l; DayOfWeek dayOfWeek = ldt6.getDayOfWeek(); if (dayOfWeek.equals(DayOfWeek.FRIDAY)) { return ldt6.plusDays(3); } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) { return ldt6.plusDays(2); } else { return ldt6.plusDays(1); } }); System.out.println("获取下个工作日 : " + ldt5);}
3. DateTimeFormatter
3.1 简介
在 JDK 1.8 中可以使用 DateTimeFormatter
来代替 SimpleDateFormat
进行日期的格式化,而且 DateTimeFormatter
是线程安全的
3.2 实例
@Testpublic void t8() { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime ldt = LocalDateTime.now(); // 将日期格式化为字符串,两种方式否可以 String dtfDate = dtf.format(ldt); String ldtDate = ldt.format(dtf); System.out.println("dtfDate : " + dtfDate); System.out.println("ldtDate : " + ldtDate); // 将字符串格式化为 LocalDateTime LocalDateTime ldt2 = LocalDateTime.parse(dtfDate, dtf); System.out.println("时间为 : " + ldt2);}
3.3 线程安全
在多线程使用 SimpleDateFormat 进行格式化日期的时候会有线程安全问题。
@Testpublic void t1() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Callable<Date> task = new Callable<Date>() { @Override public Date call() throws Exception { return sdf.parse("2018-12-10"); } }; ExecutorService executor = Executors.newFixedThreadPool(10); List<Future<Date>> results = new ArrayList<>(); for (int i = 0; i < 10; i++) { results.add(executor.submit(task)); } for (Future<Date> future : results) { System.out.println(future.get()); } executor.shutdown();}
可以使用 ThreadLocal 的锁解决 SimpleDateFormat 的线程安全问题
import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;public class DateFormatThreadLocal { private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; public static final Date convert(String source) throws ParseException { return df.get().parse(source); }}
测试方法
@Testpublic void t2() throws Exception { Callable<Date> task = new Callable<Date>() { @Override public Date call() throws Exception { return DateFormatThreadLocal.convert("2018-12-10"); } }; ExecutorService executor = Executors.newFixedThreadPool(10); List<Future<Date>> results = new ArrayList<>(); for (int i = 0; i < 10; i++) { results.add(executor.submit(task)); } for (Future<Date> future : results) { System.out.println(future.get()); } executor.shutdown();}
使用 DateTimeFormatter 进行格式化,DateTimeFormatter 是线程安全的
@Testpublic void t3() throws Exception { DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); Callable<LocalDate> task = new Callable<LocalDate>() { @Override public LocalDate call() throws Exception { return LocalDate.parse("2018-12-10",dtf); } }; ExecutorService executor = Executors.newFixedThreadPool(10); List<Future<LocalDate>> results = new ArrayList<>(); for (int i = 0; i < 10; i++) { results.add(executor.submit(task)); } for (Future<LocalDate> future : results) { System.out.println(future.get()); } executor.shutdown();}
4. 时区设置
@Testpublic void t2() { LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai")); System.out.println(ldt); // 带时区的时间 ZonedDateTime zdt = ldt.atZone(ZoneId.of("Asia/Shanghai")); System.out.println(zdt);}
本文首发于凌风博客: 作者: