2020年來臨以前,日期格式化操做也爲程序員準備了一個跨年級別的bug,不知你的系統是否遇到?html
臨近2020年元旦的幾天,很多網站出現了相似2020/12/29,2020/12/30,2020/12/31這樣的日期顯示。神奇不?就連微信的提供的訂閱號助手工具都出現了這樣的錯誤。java
下面兩張圖是本公衆號「程序新視界」在12月31日訂閱號助手助手中的截圖。程序員
新增粉絲時間顯示的部份內容。api
評論區的時間顯示的部份內容。微信
上圖中,新增粉絲顯示的時間和評論的時間均爲「2020/12/31」。那麼,下面咱們就來分析一下出現此bug的緣由。實例勝千言,先用示例還原一下此bug。oracle
示例一,還原示例:ide
public class DateFormatBug {
public static void main(String[] args) throws ParseException {
// 示例一
printBugDate();
}
private static void printBugDate() 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);
}
}複製代碼
猜猜第一行和第二行打印的結果分別是什麼?若是猜對一個說明很聰明,由於上面已經說了,此實例爲還原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
A week is defined by:
(1) The first day-of-week. For example, the ISO-8601 standard considers Monday to be the first day-of-week.
(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.
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被修復了。
通過這個問題,咱們是否會思考一個問題:高手與新手的差不在哪裏?或許上面的問題就可以回答,一樣是日期格式化,在正常狀況下兩種寫法均可正常運行,沒法區分高手與新手。而只有在某時某刻某些極端狀況下才能看出高手之高。
那麼高手是從哪裏來的?是從坑中跳出來的。或許他比你早掉進去早出來了,或許他博覽羣書有那麼一次「不期而遇」,也或許就是像你如今同樣看到這篇文章。
原文連接:《跨年bug,是否與你不期而遇?》