2019 年,小編寫了很多 bug,這不前一段時間 OOM 差點就把服務器搞掛。跨年的時刻,小編默默立下一個 flag,2020 年再見 bug。
可是沒想到還沒過幾個小時,剛立的 falg 就倒下了,太難了 :sob:。。。。
事件回顧
新年第一天,運營反饋商戶收到對賬文件有問題。剛接到電話時,我是一萬個不相信,這個代碼都跑了這么久,怎么就偏偏今天不對了。可能看到我不信,運營小姐姐隨即發了一張商戶給的賬單截圖。
waht???2020 年才剛來,時間怎么變成 20201231 了。。。
哎,只好先讓運營穩住商戶,然后趕緊起來,打開了電腦,首先定位一下問題。
問題排查
生成賬單偽代碼如下:
上面代碼邏輯其實非常簡單,將賬單數據從數據庫取出,然后按照按照相應的格式組裝數組,寫入文件。
查了幾遍代碼,越看越覺得沒問題啊,這么簡單的問題沒可能出現問題呀。沒辦法,只好請教一下好朋友兼同事小黑。
很快小黑就指出 YYYY/MM/dd HH:mm 格式不對,需要使用 yyyy/MM/dd HH:mm 。修改之后,重新生成對賬單,解決這個問題。
原因分析
雖然解決了問題,但是其實還是一知半解,所幸元旦也沒什么事,就深入研究一下 YYYY 格式。原來 Java 中 YYYY 與 yyyy 分別代表兩種不同格式。
Y 代表 Week Year ,表示當天周所在的年份。這種方式將會把一年劃分成 52 周/53 周(類似于閏年的概念,每隔幾年將會增加一周)。 Week Year 下每周僅屬于某一年,如果某年的第一周或最后一周跨年,就會導致部分日期年份與實際不符。
Week Year 存在兩種標準:
ISO 8601:國際標準,每周從?周一?開始,每年的第一周至少包含 4 天
Common:通用標準,每周從?周日?開始,每年的第一周至少包含 1 天
Calendar 對象可以通過 setFirstDayOfWeek 與 setMinimalDaysInFirstWeek 改變上面默認標準
Java 將會根據系統環境變量決定使用哪種標準,可以通過設置?Locale?改變方式。代碼如下:
// 選擇 20191229 這一天Datedate20191229 = DateUtils.parseDate("20191229","yyyyMMdd");// 將會輸出 2020,使用 Common 。當前系統,Locale 默認值為 Locale.CHINASystem.out.println(DateFormatUtils.format(date20191229,"YYYY"));// 將會輸出 2019,使用 ISO 8601System.out.println(DateFormatUtils.format(date20191229,"YYYY", Locale.FRANCE));
下面例子我們使用?ISO 8601?標準,分別看一下最后一周跨年以及第一周跨年的例子。
2015 年最后一周跨年,2016 前三天使用 YYYY 最后結果為 2015,時間看起來被回退了。
2020 年第一周跨年,2019 年最后兩天使用 YYYY 結果為 2020。
其他分析
終于弄明白 YYYY ,順便也學習一下常用的日期格式。
下面以 2019-12-31 06:06:06:666 時間為例
技術總結
下面開始本篇文章的技術總結:
第一,切記 YYYY 與 yyyy 區別,年份最好統一使用 yyyy
第二,怕忘記的小伙伴可以安裝一下阿里的?Alibaba Java Coding Guidelines插件,這個插件可以檢測出使用 YYYY 的代碼
第三,閱讀完整文檔,消除 Bug 最好的的辦法就是閱讀完整的文檔,奧利給
第四,測試環節增加邊界測試,早發現,早消滅
隨便聊聊
寫這篇的文章時候,發現社區有些小伙伴也踩到這個坑,哈哈,吾道不孤也。