大家好,我是小码同学,一个大龄程序员, 在这里我将分享一些知识干货以及互联网一些热点话题,感兴趣的话就关注我吧,希望对你有所帮助。

把码小辫设为“星标⭐”咱们就自己人了!有酒一起喝!有肉一起吃!

前言

今年是2024年,刚好是闰年。大家都知道,闰年是有366天的,其中二月份有29天。最近有个项目组出了一个生产问题,跟闰年相关的。所以写篇文章跟大家讲讲这个bug,顺便讲讲Java日期处理的一些坑,让大家避坑~

1. 闰年处理的坑

这个bug是如何产生的呢?我给大家举个例子:

比如产品要求支持查询一年之间内的交易流水,并且对这个时间区间的检验。用户选了开始时间和结束时间后,前端先检验,检验通过后,传到后端。后端也检验,然后开始查询。

后端伪代码是这样实现的:

public class Test {

    public static void main(String[] args) {
        // 从前端获取的日期字符串
        String startDateString = "20230228"; // 替换为从前端获取的startDate
        String endDateString = "20240229";   // 替换为从前端获取的endDate

        // 将字符串转换为LocalDate对象
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        LocalDate startDate = LocalDate.parse(startDateString, formatter);
        LocalDate endDate = LocalDate.parse(endDateString, formatter);

        // 将startDate增加12个月
        LocalDate startDatePlus12Months = startDate.plusMonths(12);

        // 检查是否在时间区间范围内
        if (startDatePlus12Months.isBefore(endDate)) {
            System.out.println("时间区间超过一年");
            throw new RuntimeException();
        } else {
            System.out.println("时间区间小于一年");
        }

    }
}

运行结果抛出了一场,输出时间区间超过一年

其实这个例子,就是我们是否把闰年的02.29日这一天算进去,怎么理解都算合理,但是就是要避免前后端不一致的坑。

关于闰年处理的坑,我总结为主要是这几个方面:

小伙伴们在开发代码的时候,注意就好啦~

2. 日期格式yyyy-MM-dd HH:mm:ss 大小写含义区别YYYY 和 yyyy 区别

比如看这个例子:

大写日期转换_大写转换器日期_大写日期转换器在线查询

从输出结果,大家可以发现,同一个日期,只是转化了一下格式,日期就变了。就是因为年份YYYY大写的问题。

HH 和 hh 区别

大写日期转换_大写转换器日期_大写日期转换器在线查询

3.SimleDateFormat的format初始化问题

大写转换器日期_大写日期转换器在线查询_大写日期转换

4. DateTimeFormatter 日期本地化问题

反例:

String dateStr = "Wed Mar 18 10:00:00 2020";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy");
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime);

运行结果:

Exception in thread "main" java.time.format.DateTimeParseException: Text 'Wed Mar 18 10:00:00 2020' could not be parsed at index 0
 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
 at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
 at java.time.LocalDateTime.parse(LocalDateTime.java:492)
 at com.example.demo.SynchronizedTest.main(SynchronizedTest.java:19)

解析:

DateTimeFormatter 这个类默认进行本地化设置,如果默认是中文,解析英文字符串就会报异常。可以传入一个本地化参数(Locale.US)解决这个问题

正例:

String dateStr = "Wed Mar 18 10:00:00 2020";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy",Locale.US);
LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);
System.out.println(dateTime);

5. Calendar设置时间的坑

反例:

Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR, 10);
System.out.println(c.getTime());

运行结果:

Thu Mar 26 22:28:05 GMT+08:00 2020

解析:

我们设置了10小时,但运行结果是22点,而不是10点。因为Calendar.HOUR默认是按12小时制处理的,需要使用Calendar.HOUR_OF_DAY,因为它才是按24小时处理的。

Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 10);

6.日期计算的坑

日期计算,比如new Date()加一个30天,有些小伙伴可能会这么写:

大写日期转换器在线查询_大写日期转换_大写转换器日期

得到的日期居然比当前日期还要早,根本不是晚 30 天的时间。这是因为发生了int发生了溢出。

可以把30修改为 30L,或者直接用Java 8的加减API,如下:

大写转换器日期_大写日期转换器在线查询_大写日期转换

7. SimpleDateFormat 线性安全问题

比如这个例子:

public class SimpleDateFormatTest {

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue(1000));

        while (true) {
            threadPoolExecutor.execute(() -> {
                String dateString = sdf.format(new Date());
                try {
                    Date parseDate = sdf.parse(dateString);
                    String dateString2 = sdf.format(parseDate);
                    System.out.println(dateString.equals(dateString2));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            });
        }
    }

SimpleDateFormat 是个共享变量,多线程跑的时候,就会有问题:

Exception in thread "pool-1-thread-49" java.lang.NumberFormatException: For input string: "5151."
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
 at java.lang.Long.parseLong(Long.java:589)
 at java.lang.Long.parseLong(Long.java:631)
 at java.text.DigitList.getLong(DigitList.java:195)
 at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
 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.example.demo.SimpleDateFormatTest.lambda$main$0(SimpleDateFormatTest.java:19)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-47" java.lang.NumberFormatException: For input string: "5151."
 at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
 at java.lang.Long.parseLong(Long.java:589)
 at java.lang.Long.parseLong(Long.java:631)
 at java.text.DigitList.getLong(DigitList.java:195)
 at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
 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.example.demo.SimpleDateFormatTest.lambda$main$0(SimpleDateFormatTest.java:19)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 at java.lang.Thread.run(Thread.java:748)

大写日期转换_大写日期转换器在线查询_大写转换器日期

或者直接使用DateTimeFromatter

大写转换器日期_大写日期转换_大写日期转换器在线查询

8. Java日期的夏令时问题

反例:

TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.parse("1986-05-04 00:30:00"));
运行结果:
Sun May 04 01:30:00 CDT 1986

解析:

先了解一下夏令时

结合demo代码,中国在1986-05-04当天还在使用夏令时,时间被拨快了1个小时。所以0点30分打印成了1点30分。如果要打印正确的时间,可以考虑修改时区为东8区。

正例:

TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.parse("1986-05-04 00:30:00"));

往期推荐

这里有最新前沿技术资讯、技术干货等内容

点这里 ↓↓↓ 记得关注✔标星⭐ 哦


创业/副业必备:

本站已持续更新1W+创业副业顶尖课程,涵盖多个领域。

点击查看详情

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。