这个bug是如何产生的呢?我给大家举个例子:比如产品要求支持查询一年之间内的交易流水,并且对这个时间区间的检验。用户选了开始时间和结束时间后,前端先检验,检验通过后,传到后端。后端也检验,然后开始查询。
1.假设当天时间2024.02.29日,客户选的开始时间是2023.02.28,结束时间是2024.02.29.
2.前端检验的时候,是用终止时间2024.02.29减12个月,得到开始时间2023.02.28,因此认为区间是合法的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日这一天算进去,怎么理解都算合理,但是就是要避免前后端不一致的坑。关于闰年处理的坑,我总结为主要是这几个方面:
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);五. 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
Calendar c = Calendar.getInstance(); c.set(Calendar.HOUR_OF_DAY, 10);六.日期计算的坑
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)
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"));运行结果:
夏时令这几个时间可以注意一下哈,1986-05-04, 1987-04-12, 1988-04-10, 1989-04-16, 1990-04-15, 1991-04-14。
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"));