背景
????????報表需求,需要傳遞每組數據中最小的日期,后臺根據傳遞的最小日期,向前取參數傳遞的月份的上個月為結束時間的近五個月數據
例:參數傳:2025/02,則需返回2025/01, 2024/12, 2024/11, 2024/10, 2024/09這五個年月數據。
實現方案
????????先將請求參數按照/進行分割,得到年與月份,然后使用日歷類Calendar,進行設置。獲取到2025年1月31號23點59分59秒999毫秒的結束時間與
2024年9月30號0點0分0秒000毫秒的開始時間,然后按照開始時間與結束時間查詢出該時段的所有數據,并按照年/月進行分組統計。
示例代碼
public static Date getEndTime(String date, int months) {// 分割輸入字符串為年和月String[] parts = date.split("/");int year = Integer.parseInt(parts[0]);int month = Integer.parseInt(parts[1]);// 創建 Calendar 實例(默認系統時區)Calendar calendar = Calendar.getInstance();// 設置年月(注意月份需減1)calendar.set(Calendar.YEAR, year);// 0-11 對應 1-12月calendar.set(Calendar.MONTH, month - 1);// 時間部分清零(時、分、秒、毫秒)calendar.set(Calendar.HOUR_OF_DAY, 23);calendar.set(Calendar.MINUTE, 59);calendar.set(Calendar.SECOND, 59);calendar.set(Calendar.MILLISECOND, 999);calendar.add(Calendar.MONTH, months);calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));return calendar.getTime();
}
上述代碼單元測試結果,輸出2025-01-21 23:59:59.999,符合預期
異常場景?
當執行這段代碼的時間如果在月底,且當前月份的天數大于請求參數所在月份時,則會報錯,如下,如果執行方法的時間為5月30號,請求參數"2025/02"時,則獲取不到預期值:2025-01-21 23:59:59.999,返回數據仍為:2025-02-28?23:59:59.999.
問題原因分析
原始代碼的執行流程
-
Calendar.getInstance()?sun.util.locale.provider.CalendarProviderImpl#getInstance 可以參考此方法
-
默認獲取當前時間:
2025/05/30
(假設調用時的時間)。 -
此時?
DAY_OF_MONTH=30
。
-
-
設置年月
calendar.set(Calendar.YEAR, 2025); // 2025年 calendar.set(Calendar.MONTH, 1); // 2月(Calendar.MONTH 是 0-11)
-
此時日期變為?
2025/02/30
,但?2月沒有30天,所以?Calendar
?會自動調整到?2025/03/02(因為 2025年2月只有28天,30 - 28 = 2,所以變成3月2日)。
-
-
當前是?calendar.add(Calendar.MONTH, -1)
2025/03/02
,減去1個月變成?2025/02/02
(不是1月,因為3月減1是2月)。 -
calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH))
-
2月的最大天數是28(2025年不是閏年),所以最終日期是?
2025/02/28 23:59:59.999
。
-
為什么返回的是2月的最后一天?
-
關鍵問題:
calendar.set(Calendar.MONTH, 1)
?設置2月時,由于當前?DAY_OF_MONTH=30
(來自?Calendar.getInstance()
),而2月沒有30天,所以?Calendar
?自動調整到了?3月2日。 -
然后?
add(Calendar.MONTH, -1)
?變成?2月2日,而不是預期的1月。 -
最后設置月末日期,得到?
2025/02/28 23:59:59.999
。
修復方案
public static Date getEndTime(String date, int months) {String[] parts = date.split("/");int year = Integer.parseInt(parts[0]);int month = Integer.parseInt(parts[1]);Calendar calendar = Calendar.getInstance();// 先清除時間部分,避免當前日期影響calendar.clear(); // 或 calendar.set(Calendar.DAY_OF_MONTH, 1);// 設置年月calendar.set(Calendar.YEAR, year);calendar.set(Calendar.MONTH, month - 1); // 0-11// 加減月份calendar.add(Calendar.MONTH, months);// 設置月末最后一天 + 23:59:59.999calendar.set(Calendar.HOUR_OF_DAY, 23);calendar.set(Calendar.MINUTE, 59);calendar.set(Calendar.SECOND, 59);calendar.set(Calendar.MILLISECOND, 999);calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));return calendar.getTime();
}
?總結
-
原始問題:由于?
Calendar
?初始化時攜帶了當前日期(如30日),而2月沒有30天,導致自動調整到3月2日,再?-1
?個月變成2月2日,最終返回2月末。 -
修復方法:在設置年月前?清除?
DAY_OF_MONTH
(如?calendar.clear()
?或?set(DAY_OF_MONTH, 1)
),避免當前日期影響計算。
這樣,傳入?"2025/02"
?和?-1
?時,就能正確返回?2025年1月31日 23:59:59.999。