在做单表动态增删改查功能时,有这样一个需求:判断传入的字符串是否符合字段要求的日期格式。
页面提交的字段值统一用 Map<String, String> 接收,后端再根据字段配置判断类型。比如日期字段约定使用 yyyy-MM-dd HH:mm:ss,就需要在保存之前检查一下。
首先想到的是用 SimpleDateFormat 来 parse 字符串,如果抛出异常则不符合,反之则符合,代码如下:
private static boolean isValidFormat(String format, String value) {
boolean ok = true;
DateFormat df = new SimpleDateFormat(format);
try {
df.parse(value);
} catch (ParseException e) {
ok = false;
}
return ok;
}第一次测试
使用 yyyy-MM-dd HH:mm:ss 和 2019-02-31 13:12:20 进行测试:
@Test
public void test_1() {
String format = "yyyy-MM-dd HH:mm:ss";
String value = "2019-02-31 13:12:20";
Assert.assertFalse(isValidFormat(format, value));
}测试没有通过,2 月 31 日也被当成了正确的日期。
查看 DateFormat 的说明,默认情况下解析是宽松的,超出的日期会向后计算。这里的 2 月 31 日会被解析成 3 月 3 日。
在 parse 之前关闭宽松模式:
df.setLenient(false);再次执行,测试通过。
第二次测试
把秒数改成一位:
@Test
public void test_2() {
String format = "yyyy-MM-dd HH:mm:ss";
String value = "2019-02-18 13:12:2";
Assert.assertFalse(isValidFormat(format, value));
}测试还是失败。虽然设置了非宽松模式,2019-02-18 13:12:2 仍然可以被解析。
setLenient(false) 检查的是日期和时间的取值是否合法,并没有要求秒数一定写成两位。对于这里用分隔符隔开的数字字段,解析时不会按格式字母的数量限制位数。2 秒是有效值,所以 ss 也能把它解析出来。
再加一个长度判断
这里使用的格式固定为 yyyy-MM-dd HH:mm:ss,长度是 19。既然少一位也能解析,就在解析之前先判断字符串长度:
private static boolean isValidDate(String str) {
boolean convertSuccess = true;
String pattern = "yyyy-MM-dd HH:mm:ss";
if (str.length() != pattern.length()) {
return false;
}
SimpleDateFormat format = new SimpleDateFormat(pattern);
try {
// 设置lenient为false.
// 否则SimpleDateFormat会宽松验证
format.setLenient(false);
format.parse(str);
} catch (ParseException e) {
convertSuccess = false;
}
return convertSuccess;
}长度不一致直接返回 false,长度一致再解析。这样,前面秒数少一位的字符串就不会通过了。
再试一下:
System.out.println(isValidDate("2019-02-18 13:12:20")); // true
System.out.println(isValidDate("2019-02-31 13:12:20")); // false
System.out.println(isValidDate("2019-02-18 13:12:2")); // false
System.out.println(isValidDate("2016-02-29 13:12:20")); // true
System.out.println(isValidDate("2019-02-29 13:12:20")); // false不过,长度判断只限制了总长度,各部分的位数并没有逐一检查。比如 2019-2-018 13:12:20 也是 19 位,仍然能通过这个方法。
单表校验时,先跳过空值,再根据字段类型调用日期校验:
for (FieldMetaInfo fieldMetaInfo : fieldMetaInfoList) {
String value = paramMap.get(fieldMetaInfo.getField());
if (StringUtils.isBlank(value)) {
continue;
}
String title = "【" + fieldMetaInfo.getTitle() + "】";
if (DbDataTypesUtils.isDate(fieldMetaInfo.getJavaType()) && !isValidDate(value)) {
return new HttpProtocol(ProtocolStatus.FAILURE, "请检查日期格式是否正确" + title);
}
}isValidDate 本身没有处理 null,这里由调用处先过滤。日期格式不对时,把字段名称一起返回,页面上就能知道是哪一项有问题。