Java檢查日期字符串是否合法的方法總結(jié)
WHY
后端接口在接收數(shù)據(jù)的時(shí)候,都需要進(jìn)行檢查。檢查全部通過(guò)后,才能夠執(zhí)行業(yè)務(wù)邏輯。對(duì)于時(shí)間格式,我們一般需要檢查這么幾方面:
- 字符串格式是否正確,比如格式是不是yyyy-MM-dd
- 時(shí)間在合法范圍內(nèi),比如我們需要限定在一個(gè)月內(nèi)的時(shí)間
- 字符串可以解析為正常的時(shí)間,比如 2 月 30 號(hào)就不是正常時(shí)間
對(duì)于時(shí)間格式的判斷,我們可以通過(guò)正則表達(dá)式來(lái)檢查。不過(guò)考慮到正則表達(dá)式的性能、輸入數(shù)據(jù)的復(fù)雜性,一般能用別的方式,就不選正則表達(dá)式。我們還是選擇一種更加通用、更加高效的檢查方式。
首先,定義時(shí)間校驗(yàn)器的接口:
public interface DateValidator {
boolean isValid(String dateStr);
}
接口方法接收一個(gè)字符串,返回布爾類(lèi)型,表示字符串是否是合法的時(shí)間格式。
HOW
接下來(lái)就是通過(guò)不同方式實(shí)現(xiàn)DateValidator。
1.使用 DateFormat 檢查
Java 提供了格式化和解析時(shí)間的工具:DateFormat抽象類(lèi)和SimpleDataFormat實(shí)現(xiàn)類(lèi)。我們借此實(shí)現(xiàn)時(shí)間校驗(yàn)器:
public?class?DateValidatorUsingDateFormat?implements?DateValidator?{
????private?final?String?dateFormat;
????public?DateValidatorUsingDateFormat(String?dateFormat)?{
????????this.dateFormat?=?dateFormat;
????}
????@Override
????public?boolean?isValid(String?dateStr)?{
????????final?DateFormat?sdf?=?new?SimpleDateFormat(this.dateFormat);
????????sdf.setLenient(false);
????????try?{
????????????sdf.parse(dateStr);
????????}?catch?(ParseException?e)?{
????????????return?false;
????????}
????????return?true;
????}
}
這里需要注意一下,DateFormat和SimpleDataFormat是非線程安全的,所以每次方法調(diào)用時(shí),都需要新建實(shí)例。
我們通過(guò)單元測(cè)試驗(yàn)證下:
class?DateValidatorUsingDateFormatTest?{
????@Test
????void?isValid()?{
????????final?DateValidator?validator?=?new?DateValidatorUsingDateFormat("yyyy-MM-dd");
????????Assertions.assertTrue(validator.isValid("2021-02-28"));
????????Assertions.assertFalse(validator.isValid("2021-02-30"));
????}
}
在 Java8 之前,一般都是用這種方式來(lái)驗(yàn)證。Java8 之后,我們有了更多的選擇。
2.使用 LocalDate 檢查
Java8 引入了更加好用日期和時(shí)間 API(想要了解更多內(nèi)容,請(qǐng)移步參看 Java8 中的時(shí)間類(lèi)及常用 API)。其中包括LocalDate類(lèi),是一個(gè)不可變且線程安全的時(shí)間類(lèi)。
LocalDate提供了兩個(gè)靜態(tài)方法,用來(lái)解析時(shí)間。這兩個(gè)方法內(nèi)部都是使用java.time.format.DateTimeFormatter來(lái)處理數(shù)據(jù):
//?使用?DateTimeFormatter.ISO_LOCAL_DATE?處理數(shù)據(jù)
public?static?LocalDate?parse(CharSequence?text)?{
????return?parse(text,?DateTimeFormatter.ISO_LOCAL_DATE);
}
//?使用提供的?DateTimeFormatter?處理數(shù)據(jù)
public?static?LocalDate?parse(CharSequence?text,?DateTimeFormatter?formatter)?{
????????Objects.requireNonNull(formatter,?"formatter");
????return?formatter.parse(text,?LocalDate::from);
}
通過(guò)LocalDate的parse方法實(shí)現(xiàn)我們的校驗(yàn)器:
public?class?DateValidatorUsingLocalDate?implements?DateValidator?{
????private?final?DateTimeFormatter?dateFormatter;
????public?DateValidatorUsingLocalDate(DateTimeFormatter?dateFormatter)?{
????????this.dateFormatter?=?dateFormatter;
????}
????@Override
????public?boolean?isValid(String?dateStr)?{
????????try?{
????????????LocalDate.parse(dateStr,?this.dateFormatter);
????????}?catch?(DateTimeParseException?e)?{
????????????return?false;
????????}
????????return?true;
????}
}
java.time.format.DateTimeFormatter類(lèi)是不可變的,也就是天然的線程安全,我們可以在不同線程使用同一個(gè)校驗(yàn)器實(shí)例。
我們通過(guò)單元測(cè)試驗(yàn)證下:
class?DateValidatorUsingLocalDateTest?{
????@Test
????void?isValid()?{
????????final?DateTimeFormatter?dateFormatter?=?DateTimeFormatter.ISO_LOCAL_DATE;
????????final?DateValidator?validator?=?new?DateValidatorUsingLocalDate(dateFormatter);
????????Assertions.assertTrue(validator.isValid("2021-02-28"));
????????Assertions.assertFalse(validator.isValid("2021-02-30"));
????}
}
既然LocalDate#parse是通過(guò)DateTimeFormatter實(shí)現(xiàn)的,那我們也可以直接使用DateTimeFormatter。
3.使用 DateTimeFormatter 檢查
DateTimeFormatter解析文本總共分兩步。第一步,根據(jù)配置將文本解析為日期和時(shí)間字段;第二步,用解析后的字段創(chuàng)建日期和時(shí)間對(duì)象。
實(shí)現(xiàn)驗(yàn)證器:
public?class?DateValidatorUsingDateTimeFormatter?implements?DateValidator?{
????private?final?DateTimeFormatter?dateFormatter;
????public?DateValidatorUsingDateTimeFormatter(DateTimeFormatter?dateFormatter)?{
????????this.dateFormatter?=?dateFormatter;
????}
????@Override
????public?boolean?isValid(String?dateStr)?{
????????try?{
????????????this.dateFormatter.parse(dateStr);
????????}?catch?(DateTimeParseException?e)?{
????????????return?false;
????????}
????????return?true;
????}
}
通過(guò)單元測(cè)試驗(yàn)證:
class?DateValidatorUsingDateTimeFormatterTest?{
????private?static?final?DateTimeFormatter?DATE_FORMATTER?=?DateTimeFormatter.ofPattern("uuuu-MM-dd",?Locale.CHINA);
????@Test
????void?isValid()?{
????????final?DateTimeFormatter?dateFormatter?=?DATE_FORMATTER.withResolverStyle(ResolverStyle.STRICT);
????????final?DateValidator?validator?=?new?DateValidatorUsingDateTimeFormatter(dateFormatter);
????????Assertions.assertTrue(validator.isValid("2021-02-28"));
????????Assertions.assertFalse(validator.isValid("2021-02-30"));
????}
}
可以看到,我們指定了轉(zhuǎn)換模式是ResolverStyle.STRICT,這個(gè)類(lèi)型是說(shuō)明解析模式。共有三種:
- STRICT:嚴(yán)格模式,日期、時(shí)間必須完全正確。
- SMART:智能模式,針對(duì)日可以自動(dòng)調(diào)整。月的范圍在 1 到 12,日的范圍在 1 到 31。比如輸入是 2 月 30 號(hào),當(dāng)年 2 月只有 28 天,返回的日期就是 2 月 28 日。
- LENIENT:寬松模式,主要針對(duì)月和日,會(huì)自動(dòng)后延。結(jié)果類(lèi)似于
LocalData#plusDays或者LocalDate#plusMonths。
我們通過(guò)例子看下區(qū)別:
class?DateValidatorUsingDateTimeFormatterTest?{
????private?static?final?DateTimeFormatter?DATE_FORMATTER?=?DateTimeFormatter.ofPattern("uuuu-MM-dd",?Locale.CHINA);
????@Test
????void?testResolverStyle()?{
????????Assertions.assertEquals(LocalDate.of(2021,?2,28),?parseDate("2021-02-28",?ResolverStyle.STRICT));
????????Assertions.assertNull(parseDate("2021-02-29",?ResolverStyle.STRICT));
????????Assertions.assertEquals(LocalDate.of(2021,?2,28),?parseDate("2021-02-28",?ResolverStyle.STRICT));
????????Assertions.assertNull(parseDate("2021-13-28",?ResolverStyle.STRICT));
????????Assertions.assertEquals(LocalDate.of(2021,?2,28),?parseDate("2021-02-28",?ResolverStyle.SMART));
????????Assertions.assertEquals(LocalDate.of(2021,?2,28),?parseDate("2021-02-29",?ResolverStyle.SMART));
????????Assertions.assertNull(parseDate("2021-13-28",?ResolverStyle.SMART));
????????Assertions.assertNull(parseDate("2021-13-29",?ResolverStyle.SMART));
????????Assertions.assertEquals(LocalDate.of(2021,?2,28),?parseDate("2021-02-28",?ResolverStyle.LENIENT));
????????Assertions.assertEquals(LocalDate.of(2021,?3,1),?parseDate("2021-02-29",?ResolverStyle.LENIENT));
????????Assertions.assertEquals(LocalDate.of(2022,?1,28),?parseDate("2021-13-28",?ResolverStyle.LENIENT));
????????Assertions.assertEquals(LocalDate.of(2022,?2,2),?parseDate("2021-13-33",?ResolverStyle.LENIENT));
????}
????private?static?LocalDate?parseDate(String?dateString,?ResolverStyle?resolverStyle)?{
????????try?{
????????????return?LocalDate.parse(dateString,?DATE_FORMATTER.withResolverStyle(resolverStyle));
????????}?catch?(DateTimeParseException?e)?{
????????????return?null;
????????}
????}
}
從例子可以看出,ResolverStyle.STRICT是嚴(yán)格控制,用來(lái)做時(shí)間校驗(yàn)比較合適;ResolverStyle.LENIENT可以最大程度將字符串轉(zhuǎn)化為時(shí)間對(duì)象,在合理范圍內(nèi)可以隨便玩;ResolverStyle.SMART名為智能,但智力有限,兩不沾邊,優(yōu)勢(shì)不夠明顯。JDK 提供的DateTimeFormatter實(shí)現(xiàn),都是ResolverStyle.STRICT模式。
說(shuō)了 JDK 自帶的實(shí)現(xiàn),接下來(lái)說(shuō)說(shuō)第三方組件的實(shí)現(xiàn)方式。
4.使用 Apache 出品的 commons-validator 檢查
Apache Commons 項(xiàng)目提供了一個(gè)校驗(yàn)器框架,包含多種校驗(yàn)規(guī)則,包括日期、時(shí)間、數(shù)字、貨幣、IP 地址、郵箱、URL 地址等。本文主要說(shuō)檢查時(shí)間,所以重點(diǎn)看看GenericValidator類(lèi)提供的isDate方法:
public?class?GenericValidator?implements?Serializable?{
????//?其他方法
????public?static?boolean?isDate(String?value,?Locale?locale)?{
????????return?DateValidator.getInstance().isValid(value,?locale);
????}
????public?static?boolean?isDate(String?value,?String?datePattern,?boolean?strict)?{
????????return?org.apache.commons.validator.DateValidator.getInstance().isValid(value,?datePattern,?strict);
????}
}
先引入依賴:
<dependency> ????<groupId>commons-validator</groupId> ????<artifactId>commons-validator</artifactId> ????<version>1.7</version> </dependency>
實(shí)現(xiàn)驗(yàn)證器:
public?class?DateValidatorUsingCommonsValidator?implements?DateValidator?{
????private?final?String?dateFormat;
????public?DateValidatorUsingCommonsValidator(String?dateFormat)?{
????????this.dateFormat?=?dateFormat;
????}
????@Override
????public?boolean?isValid(String?dateStr)?{
????????return?GenericValidator.isDate(dateStr,?dateFormat,?true);
????}
}
通過(guò)單元測(cè)試驗(yàn)證:
class?DateValidatorUsingCommonsValidatorTest?{
????@Test
????void?isValid()?{
????????final?DateValidator?dateValidator?=?new?DateValidatorUsingCommonsValidator("yyyy-MM-dd");
????????Assertions.assertTrue(dateValidator.isValid("2021-02-28"));
????????Assertions.assertFalse(dateValidator.isValid("2021-02-30"));
????}
}
看org.apache.commons.validator.DateValidator#isValid源碼可以發(fā)現(xiàn),內(nèi)部是通過(guò)DateFormat和SimpleDateFormat實(shí)現(xiàn)的。
總結(jié)
在本文中,我們通過(guò)四種方式實(shí)現(xiàn)了時(shí)間字符串校驗(yàn)邏輯。其中DateFormat和SimpleDataFormat是非線程安全的,所以每次方法調(diào)用時(shí),都需要新建實(shí)例;通過(guò)觀察apache.commons.validator.DateValidator#isValid的源碼發(fā)現(xiàn),它的內(nèi)部也是通過(guò)DateFormat和SimpleDateFormat實(shí)現(xiàn)的;而LocalDate和DateTimeFormatter則為JDK8中提供的實(shí)現(xiàn)方法。
到此這篇關(guān)于Java檢查日期字符串是否合法的方法總結(jié)的文章就介紹到這了,更多相關(guān)Java檢查日期字符串內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis/Mybatis-Plus駝峰式命名映射的實(shí)現(xiàn)
本文主要介紹了Mybatis-Plus駝峰式命名映射的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Spring Boot 配置隨機(jī)數(shù)的技巧代碼詳解
這篇文章主要介紹了Spring Boot 配置隨機(jī)數(shù)技巧,spring boot 支持在系統(tǒng)加載的時(shí)候配置隨機(jī)數(shù),具體實(shí)例代碼大家參考下本文2018-05-05
基于Java的guava開(kāi)源庫(kù)工具類(lèi)
guava是谷歌基于java封裝好的開(kāi)源庫(kù),這篇文章主要通過(guò)介紹幾個(gè)好用的guava工具類(lèi),感興趣的朋友可以參考下面文章內(nèi)容2021-09-09
mybatis新增到數(shù)據(jù)庫(kù)后返回當(dāng)前ID問(wèn)題
這篇文章主要介紹了mybatis新增到數(shù)據(jù)庫(kù)后返回當(dāng)前ID問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Java雜談之如何優(yōu)化寫(xiě)出漂亮高效的代碼
不一致的代碼會(huì)造成認(rèn)知上的負(fù)擔(dān),在一個(gè)系統(tǒng)中,做類(lèi)似的事情,卻有不同的做法,或者起到類(lèi)似作用的事物,卻有不同的名字,讓人困惑2021-09-09
Java通過(guò)Scanner了解if...else if語(yǔ)句
這篇文章主要介紹了Java通過(guò)Scanner了解if...else if語(yǔ)句,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01
圖解Java經(jīng)典算法插入排序的原理與實(shí)現(xiàn)
插入排序的算法描述是一種簡(jiǎn)單直觀的排序算法。其原理是通過(guò)構(gòu)建有序序列,對(duì)于未排序數(shù)據(jù),在已排序序列中從后向前掃描,找到相應(yīng)位置并插入。本文將用Java語(yǔ)言實(shí)現(xiàn)插入排序算法并進(jìn)行可視化,感興趣的可以了解一下2022-09-09
打包部署若依(RuoYi)SpringBoot后端和Vue前端圖文教程
若依是一個(gè)使用Spring Boot作為后端和Vue.js作為前端的全棧應(yīng)用開(kāi)發(fā)平臺(tái),下面這篇文章主要給大家介紹了關(guān)于打包部署若依(RuoYi)SpringBoot后端和Vue前端的相關(guān)資料,需要的朋友可以參考下2024-05-05

