1、SimpleDateFormat 线程不安全问题
SimpleDateFormat.format方法使用多线程共享的变量Calendar,并修改Calendar,因此当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
SimpleDateFormat.parse方法也是线程不安全的,parse方法实际调用的是CalenderBuilder的establish来进行解析,其方法中主要步骤不是原子操作
一般解决方案有:
- 定义成局部变量使用,一个线程一个对象;
- 加互斥锁
- 使用ThreadLocal(用完记得释放,避免内存泄露)
如果是JDK8的应用,可以使用 Instant 代替 Date,
LocalDateTime 代替 Calendar,
DateTimeFormatter 代替 Simpledateformatter,
官方给出的解释:simple beautiful strong immutable thread-safe。
2、日期格式的大小写有很大差别
不能所有日期格式全写成大写的YYYY-MM-DD HH:MM:SS格式
小写的y表示 Year为正经的年份,而大写的Y表示的是 Week year,即当前周所在的年,如果此周跨年,则会出问题!!!
请求示示例如下:
System.out.println("STD: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2019-12-31 23:59:59"));
System.out.println("BIG_YEAR: " + new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").parse("2019-12-31 23:59:59"));
System.out.println("BIG_DD: " + new SimpleDateFormat("yyyy-MM-DD HH:mm:ss").parse("2019-12-31 23:59:59"));
System.out.println("BIG_MM: " + new SimpleDateFormat("yyyy-MM-dd HH:MM:ss").parse("2019-12-31 23:59:59"));
System.out.println("BIG_SS: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:SS").parse("2019-12-31 23:59:59"));
响应为:
STD: Tue Dec 31 23:59:59 CST 2019
BIG_YEAR: Sun Dec 30 23:59:59 CST 2018
BIG_DD: Thu Jan 31 23:59:59 CST 2019
BIG_MM: Fri Dec 01 23:00:59 CST 2023
BIG_SS: Tue Dec 31 23:59:00 CST 2019
可见,正确且标准的日期格式只能是:yyyy-MM-dd HH:mm:ss 不能随便大小写!!!
3、LocalDateTime ResolverStyle 有区别
1、字符转LocalDateTime使用严格校验模式,注意年份为uuuu,即使用欧拉式年份,而非BC/AC年份;
2、默认使用SMART模式,但此模式会自动把不合格的字符串形式的时间转成正常时间,使用STRICT模式能严格校验时间格式
//时间戳格式的严格模式
public static final String timestampStrict = "uuuuMMddHHmmss";
//日其格式的严格模式
public static final String dateTimeStrict = "uuuu-MM-dd HH:mm:ss";
对应的格式转换器为:
//使用格式为:uuuuMMddHHmmss,且严格按ISO年表校验的格式器
public static final DateTimeFormatter timestampStrictFormatter = DateTimeFormatter
.ofPattern(timestampStrict)
.withChronology(IsoChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
//使用格式为:uuuu-MM-dd HH:mm:ss,且严格按ISO年表校验的格式器
public static final DateTimeFormatter dateTimeStrictFormatter = DateTimeFormatter
.ofPattern(dateTimeStrict)
.withChronology(IsoChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
使用示例为:
/**
* 把String按严格模式解析成LocalDateTime格式,支持传指定的格式
*
* @param str
* @param pattern
* @return
*/
public static LocalDateTime str2LocalDateTimeByStrict(String str, String pattern) {
if (StringUtils.isEmpty(str))
return null;
if (StringUtils.isEmpty(pattern)) {
pattern = dateTimeStrict ;
}
DateTimeFormatter dateTimeFormatter;
if (timestampStrict.equals(pattern)) {
dateTimeFormatter = timestampStrictFormatter;
} else if (dateTimeStrict.equals(pattern)) {
dateTimeFormatter = dateTimeStrictFormatter;
} else {
dateTimeFormatter = DateTimeFormatter.ofPattern(pattern)
.withChronology(IsoChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
}
LocalDateTime localDateTime = null;
try {
localDateTime = LocalDateTime.parse(str, dateTimeFormatter);
} catch (Exception e) {
log.warn("string turn to LocalDateTime error:"+ e);
}
return localDateTime;
}