一、概念
讨论在Java中检查一个字符串是否包含有效日期的各种方法,包括Java8之前与Java8之后及Apache Commons Validator的解决方案。
二、日期概述
不管是应该还是方法,在接收数据时,我们都必须在处理之前验证它的有效性。
在输入日期的情况下我们需要验证下面几点:
- 输入包含有效的日期格式,如MM/DD/YYYY
- 输入的各个部分都在有效范围内
- 可以解析为日历中的有效日期
我们可以用正则表达式来完成上述工作,但是正则在处理各种输入格式时很复杂且容易出错,并且还会降低性能。
下面讨论各种灵活、稳键、高效的日期验证的方法。
首先我们编写一个日期验证的接口。
public interface DateValidator {
boolean isValid(String dateStr);
}
下面就以不同的方法来实现这个接口。
三、使用DateFormat
Java从一开始就提供了格式化和解析日期的工具。这些功能在抽像类DateFormat和它的实现类SimpleDateFormat中。
让我们使用DateFormat来实现日期验证吧。
public class DateValidatorUsingDateFormat implements DateValidator {
private String dateFormat;
public DateValidatorUsingDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
@Override
public boolean isValid(String dateStr) {
DateFormat sdf = new SimpleDateFormat(this.dateFormat);
// 使用严格的解析
sdf.setLenient(false);
try {
sdf.parse(dateStr);
} catch (ParseException e) {
return false;
}
return true;
}
}
因为DateFormat相关类是线程不安全,我们需要为每个方法调用创建一个新的实例。下面我们就来写单元测试:
public class DateValidatorUsingDateFormatUnitTest {
@Test
public void givenValidator_whenValidDatePassed_ThenTrue() {
DateValidator validator = new DateValidatorUsingDateFormat("MM/dd/yyyy");
assertTrue(validator.isValid("02/28/2019"));
}
@Test
public void givenValidator_whenInvalidDatePassed_ThenFalse() {
DateValidator validator = new DateValidatorUsingDateFormat("MM/dd/yyyy");
assertFalse(validator.isValid("02/30/2019"));
}
}
这是Java 8之前的通常的解决方案。
四、使用LocalDate
Java 8引入了改进的日期和时间API。添加了LocalDate类,只表示日期。这个类是线程安全的。
LocalDate提供了两个表态方法来解析日期,都使用DateTimeFormatter来解析:
public static LocalDate parse(CharSequence text)
// parses dates using using DateTimeFormatter.ISO_LOCAL_DATE
public static LocalDate parse(CharSequence text, DateTimeFormatter formatter)
// parses dates using the provided formatter
下面用这个解析方法来实现日期验证
public class DateValidatorUsingLocalDate implements DateValidator {
private 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;
}
}
下面写单元测试
public class DateValidatorUsingLocalDateUnitTest {
DateTimeFormatter dateFormatter = DateTimeFormatter.BASIC_ISO_DATE;
DateValidator validator = new DateValidatorUsingLocalDate(dateFormatter);
@Test
public void givenValidator_whenValidDatePassed_ThenTrue() {
assertTrue(validator.isValid("20190228"));
}
@Test
public void givenValidator_whenInValidDatePassed_ThenFalse() {
assertFalse(validator.isValid("20190230"));
}
}
五、使用DateTimeFormater
上一节中LocalDate使用DateTimeFormatter对象进行解析,我们也可以直接使用DateTimeFormatter类进行格式化解析。
DateTimeFormatter分两个阶段解析文本。阶段1,它根据配置将日期字符串解析为日期和时间字段。阶段2,它将这些日期和时间字段解析为对象。ResolverStyle控制阶段2,它有3个值:
// 结论
// ResolverStyle formatter.parse(text) LocalDate.parse(text, formatter)
// ------------- --------------------- ---------------------------------
// STRICT 6.31,6.32不合理输出 6.31,6.32抛异常
// SMART 6.31按6.30算 6.32抛异常 同左
// LENIENT 往后延6.31按7.1算 6.32按7.2算 同左
// DateTimeFormatter.ofPatter("yyyy-MM-dd") 是ResolverStyle.SMART模式
下面是直接使用DateTimeFormatter编写日期验证:
public class DateValidatorUsingDateTimeFormatter implements DateValidator {
private 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;
}
}
写测试
public class DateValidatorUsingDateTimeFormatterUnitTest {
@Test
public void givenValidator_whenValidDatePassed_ThenTrue() {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.SIMPLIFIED_CHINESE)
.withResolverStyle(ResolverStyle.STRICT);
DateValidator validator = new DateValidatorUsingDateTimeFormatter(dateFormatter);
assertTrue(validator.isValid("2019-02-28"));
}
@Test
public void givenValidator_whenInValidDatePassed_ThenFalse() {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.SIMPLIFIED_CHINESE)
.withResolverStyle(ResolverStyle.STRICT);
DateValidator validator = new DateValidatorUsingDateTimeFormatter(dateFormatter);
assertFalse(validator.isValid("2019-02-30"));
}
}
六、使用Apache Commons Validator
Apache Commons项目提供了一个验证框架,可以验证日期、时间、数字、货币、IP、Email和URL。
本文主要看GenericValidator类,它提供了几种方法来验证String是否包含有效日期:
public static boolean isDate(String value, Locale locale)
public static boolean isDate(String value,String datePattern, boolean strict)
要使用这个库先添加依赖
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
用GenericValidator类来实现日期验证
assertTrue(GenericValidator.isDate("2019-02-28", "yyyy-MM-dd", true));
assertFalse(GenericValidator.isDate("2019-02-29", "yyyy-MM-dd", true));
七、总结
本文总结了验证字符串是否包含有效日期的几种方法。