基于java8的时间工具类(个人总结)
项目开发中经常使用到时间处理,但是这些时间处理方法有许多值得探讨的方法
引用:
https://zhuanlan.zhihu.com/p/135985813--分析
https://blog.csdn.net/qq_36596145/article/details/85331002--时间工具类
一.SimpleDateFormat线程不安全
阿里手册中是禁止使用static修饰SimpleDateFormat
1.SimpleDateFormat的format方法
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}
其中的calendar是一个共享变量,并且没有做线程安全控制,因此在多并发的情况下,调用到calendar.setTime方法的时候,可能会造成一个线程刚设置好time就被另外一个线程给修改了,导致获取不到正确的值.
protected Calendar calendar;
测试
package src.com.sinosig.sl.sssc.test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatTest {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static Date d1 = null;
private static Date d2 = null;
private static Date d3 = null;
static {
try {
d1 =sdf.parse("2020-12-12 12:12:12");
d2 =sdf.parse("2019-11-11 11:11:11");
d3 =sdf.parse("2018-10-10 10:10:10");
} catch (ParseException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
String parse = sdf.format(d1);
System.out.println("当前日期d1:" + parse);
});
Thread t2 = new Thread(() -> {
String parse = sdf.format(d2);
System.out.println("当前日期d2:" + parse);
});
Thread t3 = new Thread(() -> {
String parse = sdf.format(d3);
System.out.println("当前日期d3:" + parse);
});
t1.start();
t2.start();
t3.start();
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试结果
当前日期d1:2018-11-11 11:11:11
当前日期d3:2018-11-11 11:11:11
当前日期d2:2019-11-11 11:11:11
2.SimpleDateFormat的parse方法
Date parsedDate;
try {
parsedDate = calb.establish(calendar).getTime();
// If the year value is ambiguous,
// then the two-digit year == the default start year
if (ambiguousYear[0]) {
if (parsedDate.before(defaultCenturyStart)) {
parsedDate = calb.addYear(100).establish(calendar).getTime();
}
}
}
// An IllegalArgumentException will be thrown by Calendar.getTime()
// if any fields are out of range, e.g., MONTH == 17.
catch (IllegalArgumentException e) {
pos.errorIndex = start;
pos.index = oldStart;
return null;
}
其中parse方法中有一步是parsedDate = calb.establish(calendar).getTime().
Calendar establish(Calendar cal) {
boolean weekDate = isSet(WEEK_YEAR)
&& field[WEEK_YEAR] > field[YEAR];
if (weekDate && !cal.isWeekDateSupported()) {
// Use YEAR instead
if (!isSet(YEAR)) {
set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
}
weekDate = false;
}
cal.clear();
方法内部的实现有一个clear操作,多并发的情况下存在一个线程执行了clear,另外一个线程来get就没有东西了,就会报空指针.
测试
package com.sinosig.sl.sssc.service.impl;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
import com.sinosig.sl.sssc.base.utils.ThreadLocalDateUtil;
public class DateFormatTest {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static Date parse(String date){
Date parse = null;
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
private static Date parse1(String date){
Date parse = null;
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Date parse=parse("2020-12-12 12:12:12");
System.out.println("当前日期:" + parse);
});
Thread t2 = new Thread(() -> {
Date parse=parse("2020-12-12 12:12:12");
System.out.println("当前日期:" + parse);
});
Thread t3 = new Thread(() -> {
Date parse=parse("2020-12-12 12:12:12");
System.out.println("当前日期:" + parse);
});
t1.start();
t2.start();
t3.start();
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程执行完毕");
}
}
测试结果
Exception in thread "Thread-1" 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:2084)
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.sinosig.sl.sssc.service.impl.DateFormatTest.parse(DateFormatTest.java:17)
at com.sinosig.sl.sssc.service.impl.DateFormatTest.lambda$1(DateFormatTest.java:40)
at java.lang.Thread.run(Thread.java:748)
当前日期:Sat Dec 12 12:12:12 CST 2020
当前日期:Sat Dec 12 12:12:12 CST 2020
线程执行完毕
二.解决方案
1使用局部变量
在方法内部每次用到都new SimpleDateformat();但是这样使用可能会占用大量的内存和jvm空间
2.ThreadLocal
private static ThreadLocal<SimpleDateFormat> sdf= new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
3.java8的新时间处理类(重要)
LocalDate
只会获取年月日
- 创建
LocalDate
//获取当前年月日
LocalDate localDate = LocalDate.now();
//构造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 10); - 获取年、月、日、星期几
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK)
LocalTime
只会获取几点几分几秒
- 创建
LocalTime
LocalTime localTime = LocalTime.of(13, 51, 10);
LocalTime localTime1 = LocalTime.now(); - 获取时分秒
//获取小时
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
//获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
//获取秒
int second = localTime.getSecond();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
LocalDateTime
获取年月日时分秒,等于LocalDate+LocalTime
- 创建
LocalDateTime
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate); - 获取
LocalDate
LocalDate localDate2 = localDateTime.toLocalDate(); - 获取
LocalTime
LocalTime localTime2 = localDateTime.toLocalTime();
Instant
获取秒数
- 创建
Instant
对象
Instant instant = Instant.now(); - 获取秒数
long currentSecond = instant.getEpochSecond(); - 获取毫秒数
long currentMilli = instant.toEpochMilli();
格式化时间
LocalDate localDate = LocalDate.of(2019, 9, 10);
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
//自定义格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s3 = localDate.format(dateTimeFormatter);
DateTimeFormatter
默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过DateTimeFormatter
的ofPattern
方法创建自定义格式化方式
解析时间
LocalDate localDate1 = LocalDate.parse("20190910", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-10", DateTimeFormatter.ISO_LOCAL_DATE);