日期格式化YYYY-MM-DD HH:MM:SS跨年的BUG

達升笑聊it 發佈 2020-01-04T04:41:18+00:00

哈哈,不管是前端還是後台好多都會遇到時間問題,是不是有的出現了13月呢2020年來臨之前,日期格式化操作也為程式設計師準備了一個跨年級別的bug,不知你的系統是否遇到?

哈哈,不管是前端還是後台好多都會遇到時間問題,是不是有的出現了13月呢

2020年來臨之前,日期格式化操作也為程式設計師準備了一個跨年級別的bug,不知你的系統是否遇到?

臨近2020年元旦的幾天,不少網站出現了類似2020/12/29,2020/12/30,2020/12/31這樣的日期顯示。神奇不?就連微信的提供的訂閱號助手工具都出現了這樣的錯誤。

下面兩張圖是本公眾號「程序新視界」在12月31日訂閱號助手助手中的截圖。

新增粉絲時間顯示的部分內容。



評論區的時間顯示的部分內容。

上圖中,新增粉絲顯示的時間和評論的時間均為「2020/12/31」。那麼,下面我們就來分析一下出現此bug的原因。實例勝千言,先用示例還原一下此bug。

示例一,還原示例:

  1. public class DateFormatBug {

  2. public static void main(String[] args) throws ParseException {
  3. // 示例一
  4. printBugDate();
  5. }

  6. private static void printBugDate() throws ParseException {
  7. SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
  8. Date date = sdf.parse("2020-1-1 13:12:12");
  9. System.out.println(date);
  10. String dateStr = sdf.format(date);
  11. System.out.println(dateStr);
  12. }
  13. }

猜猜第一行和第二行列印的結果分別是什麼?如果猜對一個說明很聰明,因為上面已經說了,此實例為還原bug。

列印日誌為:

Sun Dec 29 13:12:12 CST 2019

2020-12-29 13:12:12

神奇不?把字符串「2020-1-1 13:12:12」解析成日期列印出來竟然成2019年12月29日了!!!然後再對日期進行處理竟然變成2020-12-29日了!徹底亂套了,我們想要的是2020-1-1的日期啊。

示例二,延伸示例:

private static void printBugDateExtend() throws ParseException {

SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");

Date date = sdf.parse("2019-12-31 13:12:12");

System.out.println(date);

String dateStr = sdf.format(date);

System.out.println(dateStr);

}

這次只是把日期從2020-1-1改為2019-12-31,猜猜列印結果是什麼?保證你猜不到。下面是列印結果:

Sun Dec 30 13:12:12 CST 2018

2019-12-30 13:12:12

2018年12月30?這次連年份都錯了。

好了,不賣關子了,如果你的IDE上安裝的有阿里巴巴操作規範手冊的插件。你會發現「YYYY-MM-dd HH:mm:ss」上面已經有提示信息了。同樣,如果你打開新版(華山版)阿里巴巴Java開發手冊,對此問題已經進行了明確的說明了。

那麼,哪裡可以獲得新版的《阿里巴巴Java開發手冊》?關注公眾號「程序新視界」的朋友,只需回復「005」即可獲得PDF版本。新的一年,沒事看看大廠的開發手冊,借鑑一下經驗,避免踩坑也是不錯的。當然,關注公眾號持續學習也是另外一種不錯的選擇。

同時,也可參看javadoc中對week-based-year的說明,相關連結如下:https://docs.oracle.com/javase/8/docs/api/java/time/temporal/WeekFields.html

  1. A week is defined by:

  2. (1) The first day-of-week. For example, the ISO-8601 standard considers Monday to be the first day-of-week.

  3. (2) The minimal number of days in the first week. For example, the ISO-8601 standard counts the first week as needing at least 4 days.

  4. Together these two values allow a year or month to be divided into weeks.

總結一下就是:在基於周的年份中,每周僅屬於某一年。一年中的第1周要求從一周的第一天開始,並且天數要大於4天(the minimum number of days),所以跨年周具體是哪一年還得看具體情況。

不過無論怎樣,最好的方法就是避免使用大寫的「Y」來代表年份。

盡信書不如無書,我們將代碼中「Y」修改為「y」,再執行一遍看看效果。

private static void printDate() throws ParseException {

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

Date date = sdf.parse("2020-1-1 13:12:12");

System.out.println(date);

String dateStr = sdf.format(date);

System.out.println(dateStr);

}

private static void printDateExtend() throws ParseException {

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

Date date = sdf.parse("2019-12-31 13:12:12");

System.out.println(date);

String dateStr = sdf.format(date);

System.out.println(dateStr);

}

列印結果顯示如下:

Wed Jan 01 13:12:12 CST 2020

2020-01-01 13:12:12

Tue Dec 31 13:12:12 CST 2019

2019-12-31 13:12:12

很顯然,這個跨年bug被修復了。

經過這個問題,我們是否會思考一個問題:高手與新手的差不在哪裡?或許上面的問題就能夠回答,同樣是日期格式化,在正常情況下兩種寫法都可正常運行,無法區分高手與新手。而只有在某時某刻某些極端情況下才能看出高手之高。

那麼高手是從哪裡來的?是從坑中跳出來的。或許他比你早掉進去早出來了,或許他博覽群書有那麼一次「不期而遇」,也或許就是像你現在一樣看到這篇文章。


轉載原文:https://www.choupangxia.com/2020/01/02/yyyy-mm-dd-hh-bug/

推薦公眾號:程序新視界

關鍵字: