跨年過程中因日期格式化引發的生產故障:格式化方式YYYYMM-dd和yyyyMMdd的區別

故障背景

某公司進行一年一度的跨年上線工作,在2018年的12月30號上午忽然接到保障業務無法運行,而且影響全國業務。

故障分析

第一步:因爲近期沒有業務上線活動,首先排除因上線因此引起的故障
第二步:分析業務日誌。發現業務中報錯的原因是沒有查到業務表中前期存的業務數據
第三步:查詢前期存表操作,查找是什麼原因導致業務數據沒有存到數據庫中。然後發現業務中並沒有拋出任何存表異常(這是一個坑,筆者也跳進去了饒了好幾次才發現)。此處解釋一下:因爲業務數據量比較大,因此業務上採取的是分年月的分表規則進行業務信息存儲,同時此時屬於跨年的時間節點,業務上一般會提前一週將下一年的表結構刷入數據庫中。
第四步:故障定位。上面的數據庫確實沒報錯,然後通過顯示mybatis中sql語句方式,看到sql入的表是T_BUSI_201912的表,也就是說本來如表應該是T_BUSI_201812的表現在存到表T_BUSI_201912,很明顯,這裏的分表日期計算錯了。因爲T_BUSI_201912表結果已經提前刷入生產環境,因此沒有出現入庫異常。

故障重現

根據上面的分析,我們找到了錯誤的原因開發者使用的是YYYYMMdd格式化當前日期。那現在就用測試代碼重現一下當時的故障。測試代碼如下所示:

    /**
     * 故障重現
     */
    public static void  troubleReproduce() throws ParseException {
        //業務中使用的日期格式化方式
        String formate1 = "YYYYMMdd";
        //比較格式化方式
        String formate2 = "yyyyMMdd";
        //這個是故障發生的時間
        String dateTime = "20181230";
        //這個是故障發生前一天的時間
        String dateTimeTest = "20181229";
        //這個是將字符串轉換成時間
        Date date = new SimpleDateFormat(formate2).parse(dateTime);
        Date dateTest = new SimpleDateFormat(formate2).parse(dateTimeTest);

        System.out.println("使用[" + formate1 + "]格式化前後{"+dateTime+"} -> " + (new SimpleDateFormat(formate1).format(date)));
        System.out.println("使用[" + formate2 + "]格式化前後{"+dateTime+"} -> " + (new SimpleDateFormat(formate2).format(date)));
        System.out.println("-----------------分割線---------------------");
        System.out.println("使用[" + formate1 + "]格式化前後{"+dateTimeTest+"} -> " + (new SimpleDateFormat(formate1).format(dateTest)));
        System.out.println("使用[" + formate2 + "]格式化前後{"+dateTimeTest+"} -> " + (new SimpleDateFormat(formate2).format(dateTest)));
    }

打印結果如下:

使用[YYYYMMdd]格式化前後{20181230} -> 20191230
使用[yyyyMMdd]格式化前後{20181230} -> 20181230
-----------------分割線---------------------
使用[YYYYMMdd]格式化前後{20181229} -> 20181229
使用[yyyyMMdd]格式化前後{20181229} -> 20181229

這個例子就重現了案發現場,如果是2018年12月29號的時候系統都能正常運行,到了2018年12月30號的時候系統就無法正常運行了,應該存T_BUSI_201812表的數據錯誤的存入T_BUSI_201912中。

解釋總結

上面成功找到了問題的原因,那我們要刨根問題爲什麼YYYYMMdd和yyyyMMdd得到的結果是不一樣的,爲什麼YYYYMMdd出現的結果是不對的?
因爲YYYY是week-based-year,表示當天所在的周屬於的年份,2018年的12月30號是週日,週日在西方人認爲是一週的第一天,因此本週存在跨年(2019年),所以就算入下一個年份中了。 但是2018年12月29號是本週的最後一天,這一週都在2018年,因此顯示是正常的。
所以這一點程序員要謹記,儘量不要使用YYYY這樣的年份規則。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章