Java8-日期类型
传统日期类的缺陷
传统的日期类存在着线程安全问题:
@Test
public void test1() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Callable<Date> task = new Callable<Date>() { // 创建一个任务对象
@Override
public Date call() throws Exception {
return sdf.parse("20180322");
}
};
// 创建一个又是个线程的线程池
List<Future<Date>> results = new ArrayList<>();
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
// 遍历输出
for(Future<Date> future : results) {
System.out.println(future.get());
}
}
输出:
java.util.concurrent.ExecutionException:
java.lang.NumberFormatException: multiple points
Caused by: java.lang.NumberFormatException: multiple points
出现并发异常。
解决方法
使用ThreadLocal绑定SimpleDateFormat
创建自定义的ThreadLocal类
class MyThreadLocal {
private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() {
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
static Date convert(String v) throws Exception {
return tl.get().parse(v);
}
}
测试自定义的ThreadLocal:
@Test
public void test2() throws Exception {
Callable<Date> task = new Callable<Date>() { // 创建一个任务对象
@Override
public Date call() throws Exception {
return MyThreadLocal.convert("20180322");
}
};
// 创建一个又是个线程的线程池
List<Future<Date>> results = new ArrayList<>();
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
pool.shutdown(); //关闭线程池
// 遍历输出
for(Future<Date> future : results) {
System.out.println(future.get());
}
}
输出:
Thu Mar 22 00:00:00 CST 2018
Thu Mar 22 00:00:00 CST 2018
Thu Mar 22 00:00:00 CST 2018
Thu Mar 22 00:00:00 CST 2018
Thu Mar 22 00:00:00 CST 2018
Thu Mar 22 00:00:00 CST 2018
使用Java8的新特性
在Java8中,使用java.time.*
包下面的类即可解决多线程造成的并发问题。
使用 DateTimeFormatter
和 LocalDate
。
@Test
public void test3() throws Exception {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
Callable<LocalDate> task = new Callable<LocalDate>() { // 创建一个任务对象
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20180322", dtf);
}
};
// 创建一个又是个线程的线程池
List<Future<LocalDate>> results = new ArrayList<>();
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
pool.shutdown(); //关闭线程池
// 遍历输出
for(Future<LocalDate> future : results) {
System.out.println(future.get());
}
}
Java8的日期类型
LocalDateTime、LocalDate、LocalTime
替代了之前的Date
类
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt.getYear());//2018
System.out.println(ldt.getMonthValue());//3
System.out.println(ldt.getDayOfMonth());//23
System.out.println(ldt.getDayOfYear());//82 一年中的第几天
System.out.println(ldt.getDayOfWeek());//FRIDAY
System.out.println(ldt.getHour());//0
System.out.println(ldt.getMinute());//8
System.out.println(ldt.getSecond());//13
System.out.println("--------- 年、月、日、时、分、秒 的相加 ---------");
System.out.println(ldt.plusYears(1).getYear());//2019
System.out.println(ldt.plusMonths(1).getMonthValue());//4
System.out.println(ldt.plusDays(1).getDayOfMonth());//24
System.out.println(ldt.plusHours(1).getHour());//1
System.out.println(ldt.plusMinutes(1).getMinute());//13
System.out.println(ldt.plusSeconds(1).getSecond());//47
System.out.println("--------- 自定义时间 ---------");
LocalDateTime ldt2 = LocalDateTime.of(2018, 5, 1, 0, 0);
System.out.println(ldt2);// 2018-05-01T00:00
Instant 时间戳
时间戳,以Unix元年,即: 1970年1月1日 00时:00分:00秒
Instant i = Instant.now();
// 获得秒
System.out.println(i.getEpochSecond());//1521736686
// 获得毫秒
System.out.println(i.toEpochMilli());//1521736686 272
// 获得纳秒,即获得的是toEpochMilli()之后的纳秒数
System.out.println(i.getNano());//272000000
//getEpochSecond() = toEpochMilli() + getNano()
// 从1970开始加 60 秒
System.out.println(Instant.ofEpochMilli(60));//1970-01-01T00:00:00.060Z
Duration、Period 求间隔
Instant start = Instant.now();
Thread.sleep(1000);
Instant end = Instant.now();
// 间隔毫秒
System.out.println(Duration.between(start, end).toMillis());//1000
//间隔纳秒
System.out.println(Duration.between(start, end).toNanos());//1000000000
System.out.println("--------------");
LocalTime ldt1 = LocalTime.now();
Thread.sleep(1000);
LocalTime ldt2 = LocalTime.now();
System.out.println(Duration.between(ldt1, ldt2).toMillis());//1002
System.out.println("--------------");
LocalDate ld1 = LocalDate.of(2017, 10, 10);
LocalDate ld2 = LocalDate.now();
System.out.println(Period.between(ld1, ld2).getYears());//0
System.out.println(Period.between(ld1, ld2).getMonths());//5
System.out.println(Period.between(ld1, ld2).getDays());//13
时间矫正器
TemporalAdjuster (函数式接口) TemporalAdjusters(工具类)
LocalDateTime ldt1 = LocalDateTime.now();
//修改日期为30号
LocalDateTime ldt2 = ldt1.withDayOfMonth(30);
System.out.println(ldt1);//2018-03-23T00:53:14.718
System.out.println(ldt2);//2018-03-30T00:53:14.718
// 修改日期为下一个周五
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println(ldt3);//2018-03-30T00:59:19.393
// 自定义日期修改器:获得下一个工作日: 1~5
LocalDateTime ldt4 = ldt1.with((ldt) -> {
LocalDateTime ldt5 = (LocalDateTime) ldt; // 强转为 LocalDateTime 类型
DayOfWeek dow = ldt5.getDayOfWeek(); // 得到当前的日期
if(dow.equals(DayOfWeek.FRIDAY)) {
return ldt5.plusDays(3); //当前为周五加三天
}else if(dow.equals(DayOfWeek.SATURDAY)) {
return ldt5.plusDays(2); //当前为周六加两天
}else {
return ldt5.plusDays(1); //当前为周一到周天的话加一天
}
});
System.out.println(ldt4);//2018-03-26T01:13:40.563
格式化时间
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
System.out.println(dtf.parse("2018/03/23"));//{},ISO resolved to 2018-03-23
System.out.println(LocalDateTime.now().format(dtf));//2018/03/23
时区相关
ZoneId
、 ZoneDate
、 ZoneTime
、 ZoneDateTime
。
ZoneId.getAvailableZoneIds()
.parallelStream()
.limit(5)
.forEach(System.out::println);
//Asia/TehranWETEurope/AstrakhanAfrica/JubaAmerica/Campo_Grande
//创建指定时区的 LocalDateTime 对象
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Vientiane"));
System.out.println(ldt);//2018-03-23T00:28:23.965
更多Java8的日期特性参考官方文档