大家好,我是小码同学,一个大龄程序员, 在这里我将分享一些知识干货以及互联网一些热点话题,感兴趣的话就关注我吧,希望对你有所帮助。
把码小辫设为“星标⭐”咱们就自己人了!有酒一起喝!有肉一起吃!
前言
今年是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+创业副业顶尖课程,涵盖多个领域。
点击查看详情
评论(0)