公司最近出啦个新的需求:开发一个单号集中生成中心,就是单号自动生成的一套规则,而且是用户可以自定义参与制定编号规则的。既然是自定义单号规则,有两点是很重要的:1,单号的可读性,也就是看到这个单号就可以知道这是那种单据的单子在什么时候创建的。2,可自定义性,就是说有些内容是允许客户在规则之内自定义的。本菜在制定规则的时候暂把规则制定为:XXX{YYMMdd}{3D},分解出来的:"XXX"代表前缀用户可以自定义可以根据这个查询单据类型,这个没毛病。“{3D}”表示后缀是由三位数字组成保持单号的唯一性。“{YYMMdd}"这个表示日期用户可以根据这个知道建单时间,但麻烦也出在这里。组内大佬提出,日期格式是可以由客户任意变化的,也就是说用户可以随意输入符合规定的日期格式形式。用户输入是具有不确定性的,所以我们必须要有一个规则来判断出用户输入的是否合理。由此…
我们开发不知道用户输入的到底是个啥!(比如用户输入:XXX{ymd}{3D}),XXX{ddmmyy}{4D}… ,而且由于数据库表和其他数据是一起混用的,所已并没有特定的数据结构,来定位单号组成的类型。在数据库中都是以一个字符串形式存储。所以在生成单号的时候我们只能使用正则切分按“{”来切分各字段,还有这几种规则形式是可以自由组合的。所以说你根本不值到你要生成的单号需不需要生成含日期格式的,必须循环逐个检查。
将所有符合日期格式的字符串的正则表达是一一列举,一一检查,这个工作量太大。本菜想啦想就放弃啦,有兴趣的小伙伴可以去收集下(收集完记得分享一波)
总所周知要得到真正的日期,除啦日期格式之外,还需要个日期转换的类:SimpleDateFormat 这是最常用的,虽然线程不安全,但在封闭对象中使用起来还是比较方便的:
SimpleDateFormat format = new SimpleDateFormat(“yyyyMMdd”);
String formatDate = format.format(new Date());
一般获取当前日期的字符串形式都是这样。
当传入的日期格式:new SimpleDateFormat(“ddSSJJ”);类似这种就会抛出异常,这是我们只要把异常捕捉住就可以轻松方便的知道日期格式是否合格啦。例如:
try {
// 判断是否是日期格式
SimpleDateFormat format = new SimpleDateFormat(strSn);
String formatDate = format.format(new Date());
LOG.info(strSn + "是日期格式,输出:" + formatDate);
return formatDate;
} catch (Exception e) {
// 非日期格式
LOG.error(strSn + "不是日期格式", e);
return null;
}
简洁易懂,清晰明了。几行代码轻松搞定日期格式的检测,但是组内大佬有提出啦,每次生成单号是都需要大量检测单号规则,日志上打印出大片的错误信息(也就是这个: LOG.error(strSn + “不是日期格式”, e);),到时候可能会迷惑人找真正错误信息。但去掉错误日志打印他又说不符合规范,于是乎又得重新想其他办法…
个人觉得方法二是最简单直接的方法。如果使用方法一,不是我会不会把所有的日期格式的正则写漏,就是写出来也得花耗非常大的时间,而且是无脑的劳动。打死我也不想干啊!!!但大佬提出的需求又不能不满足对吧!于是乎想在第二种的方法上优化,前提还是不能把错误日志吃掉,大佬规定:有错误日志必须打印出来,不管有用没用。(打印出来啦,他又嫌弃没用==!)
想到SimpleDateFormat能在日期格式不对的情况下抛出异常说明内部肯定有一套自己的检测机制,只要找到这套机制对它进行重写,让它符合我要求的不就行啦!确定好思路之后就往下扒源码。最后根据源码,编出一套日期检测的工具类:
/**
* 检查字符类型是否符合日期输出的格式
*
* @since 2019年1月2日 下午3:08:35
* @author ripley
*
*/
public class CheckDateUtil extends SimpleDateFormat {
private static final long serialVersionUID = 1L;
/**
* True if standalone form needs to be used.
*/
transient private static boolean forceStandaloneForm = false;
/**
* Unlocalized date-time pattern characters. For example: 'y', 'd', etc. All locales use the
* same these unlocalized pattern characters.
*/
static final String patternChars = "GyMdkHmsSEDFwWahKzZYuXL";
static final int PATTERN_ERA = 0; // G
static final int PATTERN_YEAR = 1; // y
static final int PATTERN_MONTH = 2; // M
static final int PATTERN_DAY_OF_MONTH = 3; // d
static final int PATTERN_HOUR_OF_DAY1 = 4; // k
static final int PATTERN_HOUR_OF_DAY0 = 5; // H
static final int PATTERN_MINUTE = 6; // m
static final int PATTERN_SECOND = 7; // s
static final int PATTERN_MILLISECOND = 8; // S
static final int PATTERN_DAY_OF_WEEK = 9; // E
static final int PATTERN_DAY_OF_YEAR = 10; // D
static final int PATTERN_DAY_OF_WEEK_IN_MONTH = 11; // F
static final int PATTERN_WEEK_OF_YEAR = 12; // w
static final int PATTERN_WEEK_OF_MONTH = 13; // W
static final int PATTERN_AM_PM = 14; // a
static final int PATTERN_HOUR1 = 15; // h
static final int PATTERN_HOUR0 = 16; // K
static final int PATTERN_ZONE_NAME = 17; // z
static final int PATTERN_ZONE_VALUE = 18; // Z
static final int PATTERN_WEEK_YEAR = 19; // Y
static final int PATTERN_ISO_DAY_OF_WEEK = 20; // u
static final int PATTERN_ISO_ZONE = 21; // X
static final int PATTERN_MONTH_STANDALONE = 22; // L
/**
* Tags for the compiled pattern.
*/
private final static int TAG_QUOTE_ASCII_CHAR = 100;
private final static int TAG_QUOTE_CHARS = 101;
public static boolean compile(String pattern) {
int length = pattern.length();
boolean inQuote = false;
StringBuilder compiledCode = new StringBuilder(length * 2);
StringBuilder tmpBuffer = null;
int count = 0, tagcount = 0;
int lastTag = -1, prevTag = -1;
for (int i = 0; i < length; i++) {
char c = pattern.charAt(i);
if (c == '\'') {
// '' is treated as a single quote regardless of being
// in a quoted section.
if ((i + 1) < length) {
c = pattern.charAt(i + 1);
if (c == '\'') {
i++;
if (count != 0) {
encode(lastTag, count, compiledCode);
tagcount++;
prevTag = lastTag;
lastTag = -1;
count = 0;
}
if (inQuote) {
tmpBuffer.append(c);
} else {
compiledCode.append((char) (TAG_QUOTE_ASCII_CHAR << 8 | c));
}
continue;
}
}
if (!inQuote) {
if (count != 0) {
encode(lastTag, count, compiledCode);
tagcount++;
prevTag = lastTag;
lastTag = -1;
count = 0;
}
if (tmpBuffer == null) {
tmpBuffer = new StringBuilder(length);
} else {
tmpBuffer.setLength(0);
}
inQuote = true;
} else {
int len = tmpBuffer.length();
if (len == 1) {
char ch = tmpBuffer.charAt(0);
if (ch < 128) {
compiledCode.append((char) (TAG_QUOTE_ASCII_CHAR << 8 | ch));
} else {
compiledCode.append((char) (TAG_QUOTE_CHARS << 8 | 1));
compiledCode.append(ch);
}
} else {
encode(TAG_QUOTE_CHARS, len, compiledCode);
compiledCode.append(tmpBuffer);
}
inQuote = false;
}
continue;
}
if (inQuote) {
tmpBuffer.append(c);
continue;
}
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
if (count != 0) {
encode(lastTag, count, compiledCode);
tagcount++;
prevTag = lastTag;
lastTag = -1;
count = 0;
}
if (c < 128) {
// In most cases, c would be a delimiter, such as ':'.
compiledCode.append((char) (TAG_QUOTE_ASCII_CHAR << 8 | c));
} else {
// Take any contiguous non-ASCII alphabet characters and
// put them in a single TAG_QUOTE_CHARS.
int j;
for (j = i + 1; j < length; j++) {
char d = pattern.charAt(j);
if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) {
break;
}
}
compiledCode.append((char) (TAG_QUOTE_CHARS << 8 | (j - i)));
for (; i < j; i++) {
compiledCode.append(pattern.charAt(i));
}
i--;
}
continue;
}
int tag;
if ((tag = patternChars.indexOf(c)) == -1) {
// throw new IllegalArgumentException("Illegal pattern character " + "'" + c + "'");
// 日期格式错误
return false;
}
if (lastTag == -1 || lastTag == tag) {
lastTag = tag;
count++;
continue;
}
encode(lastTag, count, compiledCode);
tagcount++;
prevTag = lastTag;
lastTag = tag;
count = 1;
}
if (inQuote) {
// 日期格式错误
return false;
}
if (count != 0) {
encode(lastTag, count, compiledCode);
tagcount++;
prevTag = lastTag;
}
forceStandaloneForm = (tagcount == 1 && prevTag == PATTERN_MONTH);
// Copy the compiled pattern to a char array
int len = compiledCode.length();
char[] r = new char[len];
compiledCode.getChars(0, len, r, 0);
return true;
}
/**
* Encodes the given tag and length and puts encoded char(s) into buffer.
*/
private static void encode(int tag, int length, StringBuilder buffer) {
if (tag == PATTERN_ISO_ZONE && length >= 4) {
throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
}
if (length < 255) {
buffer.append((char) (tag << 8 | length));
} else {
buffer.append((char) ((tag << 8) | 0xff));
buffer.append((char) (length >>> 16));
buffer.append((char) (length & 0xffff));
}
}
}
然后检测代码就可以这样写啦:
// 判断是否是日期格式
boolean isDate = CheckDateUtil.compile(strSn);
if (isDate) {
SimpleDateFormat format = new SimpleDateFormat(strSn);
String formatDate = format.format(new Date());
LOG.info(strSn + "是日期格式,输出:" + formatDate);
return formatDate;
} else {
// 非日期格式
LOG.info(strSn + "不是日期格式");
return null;
}
虽然大佬们的要求比较啰嗦,但坚持做下来还是有不少收获的。(记录此次收获)
源码在此->完整代码