跨年的你,有沒有被“YYYY-MM-dd”叫回去加班呀
危機四伏的2020已經悄然而過
不知大家是否還記得
年初的微博上長久掛著#重啟2020#的熱搜
在最艱難的日子裡
普通人要少看手機少看新聞
才能緩解內心的壓抑和惶恐
這一年,一些人沒有扛過去
但更多人,終究走下來了
此時此刻我們已經跨年結束
來到了全新的2021年……
感慨不說了
咳咳,想知道跨年可還安穩?
"YYYY-MM-dd"有沒有給你送來新年的彩蛋?
事故模擬
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-fJ6pieKA-1609596215025)(http://qiniu.hughpro.com/20210102205020.png)]
看到上面程式執行的結果,大家肯定會有疑惑,為毛不一樣呀(內心小聲嘀咕:“不應該啊,之前沒出現過問題的”)
事故分析
既然有疑惑,那我們就翻翻Java Api找找看,隨便找個線上文件,找到SimpleDateFormat
裡面就有關於日期格式的說明,我幫大家找好了:Java Api線上連結
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-YZS7QXt7-1609596215029)(http://qiniu.hughpro.com/20210102210732.png)]
在Api裡面y
的釋義是year
,很好理解就是年的意思,那Y
的釋義是Week year
,代表的是本週所在年份,而只要本週跨年,那麼本週都是算到下一年的,所以就出現了上面的事故現場。
在外國一週是從週日開始到週六結束,其實這個問題在2020年12月27日就已經發生了,上面的測試程式碼,改成27,一樣是會變成2021年12月27日。
在專案中,應該儘量避免每次轉換日期都手動進行編寫格式的操作,這邊總會不可避免的產生這樣的問題,統一定義一個DateUtil
類,裡面提供各種轉換格式的方法以及所以常用的到的格式的常量定義,這樣跨年的彩蛋就會更換啦,哈哈
public class DateUtil{
/**
* 日期格式,年份,例如:2004,2008
*/
public static final String DATE_FORMAT_YYYY = "yyyy" ;
/**
* 日期格式,年份和月份,例如:200707,200808
*/
public static final String DATE_FORMAT_YYYYMM = "yyyyMM";
/**
* 日期格式,年份和月份,例如:200707,2008-08
*/
public static final String DATE_FORMAT_YYYY_MM = "yyyy-MM";
/**
* 日期格式,年月日,例如:050630,080808
*/
public static final String DATE_FORMAT_YYMMDD = "yyMMdd";
/**
* 日期格式,年月日,用橫槓分開,例如:06-12-25,08-08-08
*/
public static final String DATE_FORMAT_YY_MM_DD = "yy-MM-dd";
/**
* 日期格式,年月日,例如:20050630,20080808
*/
public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd";
/**
* 日期格式,年月日,用橫槓分開,例如:2006-12-25,2008-08-08
*/
public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd";
/**
* 日期格式,年月日,例如:2016.10.05
*/
public static final String DATE_FORMAT_POINTYYYYMMDD = "yyyy.MM.dd";
/**
* 日期格式,年月日,例如:2016年10月05日
*/
public static final String DATE_TIME_FORMAT_YYYY年MM月DD日 = "yyyy年MM月dd日";
/**
* 日期格式,年月日時分,例如:200506301210,200808081210
*/
public static final String DATE_FORMAT_YYYYMMDDHHmm = "yyyyMMddHHmm";
/**
* 日期格式,年月日時分,例如:20001230 12:00,20080808 20:08
*/
public static final String DATE_TIME_FORMAT_YYYYMMDD_HH_MI = "yyyyMMdd HH:mm";
/**
* 日期格式,年月日時分,例如:2000-12-30 12:00,2008-08-08 20:08
*/
public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI = "yyyy-MM-dd HH:mm";
/**
* 日期格式,年月日時分秒,例如:20001230120000,20080808200808
*/
public static final String DATE_TIME_FORMAT_YYYYMMDDHHMISS = "yyyyMMddHHmmss";
/**
* 日期格式,年月日時分秒,年月日用橫槓分開,時分秒用冒號分開
* 例如:2005-05-10 23:20:00,2008-08-08 20:08:08
*/
public static final String DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS = "yyyy-MM-dd HH:mm:ss";
/**
* 日期格式,年月日時分秒毫秒,例如:20001230120000123,20080808200808456
*/
public static final String DATE_TIME_FORMAT_YYYYMMDDHHMISSSSS = "yyyyMMddHHmmssSSS";
/**
* 日期格式,月日時分,例如:10-05 12:00
*/
public static final String DATE_FORMAT_MMDDHHMI = "MM-dd HH:mm";
}
補充
另外,SimpleDateFormat
是執行緒不安全的,不推薦使用了,當併發較高的時候,容易出現問題,那如何解決呢?
-
使用區域性變數的方式
public class DateUtil{ public static String formatDate(Date date)throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static Date parse(String strDate) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.parse(strDate); } }
在需要用到
SimpleDateFormat
的地方新建一個例項,不管什麼時候,將有執行緒安全問題的物件由共享變為區域性私有都能避免多執行緒問題,不過也加重了建立物件的負擔。在一般情況下,這樣其實對效能影響並不是很明顯的。 -
加同步鎖
public class DateUtil { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String formatDate(Date date)throws ParseException { synchronized (sdf){ return sdf.format(date); } } public static Date parse(String strDate) throws ParseException{ synchronized (sdf){ return sdf.parse(strDate); } } }
這樣,當一個執行緒正在呼叫的時候,其他想要呼叫此方法的執行緒就要block,多執行緒併發量大的時候會對效能有一定的影響。
-
使用
ThreadLocal
public class DateUtil { private static ThreadLocal<DateFormat> sdfThreadLocal = new ThreadLocal<DateFormat>(){ @Override public SimpleDateFormat initialValue(){ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static String formatDate(Date date)throws ParseException { return sdfThreadLocal.get().format(date); } public static Date parse(String strDate) throws ParseException{ return sdfThreadLocal.get().parse(strDate); } }
使用
ThreadLocal
, 也是將共享變數變為獨享,執行緒獨享肯定能比方法獨享在併發環境中能減少不少建立物件的開銷。 -
使用
DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); //字串轉日期 LocalDate localDate = LocalDate.parse("2021-01-01 21:50:50",formatter); //日期轉字串 LocalTime localTime = LocalTime.now(); String strLocalTime = localTime.format(formatter); //yyyyMMdd String strLocalTime1 = localTime.format(DateTimeFormatter.BASIC_ISO_DATE);
上面程式碼僅僅是示例,關於
DateTimeFormatter
還有好多便捷的用法,它本身提供了一些自帶的格式,不需再自己編寫。和SimpleDateFormat
不同的是,DateTimeFormatter
不但是不變物件,它還是執行緒安全的,使用的時候,DateTimeFormatter
可以只建立一個例項,到處引用。
預祝大家2021年一切順順利利,疫情早點結束!
有任何問題歡迎關注公眾號私信我,一起探討,一起學習