Java 处理日期格式序列化

文章讨论了SimpleDateFormat在多线程环境中的线程不安全问题,提出通过使用局部变量或ThreadLocal来解决。另外,强调了日期格式大小写的重要性,错误的格式可能导致意外的结果。最后提到了LocalDateTime的ResolverStyle在严格校验模式下的差异,以及如何创建严格模式的日期格式转换器。
摘要由CSDN通过智能技术生成

1、SimpleDateFormat 线程不安全问题

SimpleDateFormat.format方法使用多线程共享的变量Calendar,并修改Calendar,因此当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
SimpleDateFormat.parse方法也是线程不安全的,parse方法实际调用的是CalenderBuilder的establish来进行解析,其方法中主要步骤不是原子操作

一般解决方案有:

  1. 定义成局部变量使用,一个线程一个对象;
  2. 加互斥锁
  3. 使用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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值