Java 上的正则表达式日期格式验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2149680/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Regex date format validation on Java
提问by Sheldon
I'm just wondering if there is a way (maybe with regex) to validate that an input on a Java desktop app is exactly an string formated as: "YYYY-MM-DD".
我只是想知道是否有一种方法(可能使用正则表达式)来验证 Java 桌面应用程序上的输入是否正是格式为“YYYY-MM-DD”的字符串。
I've searched but with no success.
我已经搜索过,但没有成功。
Thank you
谢谢
采纳答案by Greg Bacon
Use the following regular expression:
使用以下正则表达式:
^\d{4}-\d{2}-\d{2}$
as in
如
if (str.matches("\d{4}-\d{2}-\d{2}")) {
...
}
With the matches
method, the anchors ^
and $
(beginning and end of string, respectively) are present implicitly.
使用该matches
方法,锚点^
和$
(分别为字符串的开头和结尾)隐式存在。
回答by Steve B.
You need more than a regex
, for example "9999-99-00" isn't a valid date. There's a SimpleDateFormat
class that's built to do this. More heavyweight, but more comprehensive.
您需要的不仅仅是regex
,例如“9999-99-00”不是有效日期。有一个SimpleDateFormat
类是为做到这一点而构建的。更重量级,但更全面。
e.g.
例如
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
boolean isValidDate(string input) {
try {
format.parse(input);
return true;
}
catch(ParseException e){
return false;
}
}
Unfortunately, SimpleDateFormat
is both heavyweight and not thread-safe.
不幸的是,SimpleDateFormat
它既是重量级的,又不是线程安全的。
回答by Keilly
Construct a SimpleDateFormat with the mask, and then call: SimpleDateFormat.parse(String s, ParsePosition p)
用掩码构造一个 SimpleDateFormat,然后调用: SimpleDateFormat.parse(String s, ParsePosition p)
回答by trashgod
For fine control, consider an InputVerifierusing the SimpleDateFormat("YYYY-MM-dd")
suggested by Steve B.
对于精细控制,请考虑使用Steve B 建议的InputVerifierSimpleDateFormat("YYYY-MM-dd")
。
回答by user85421
Putting it all together:
把它们放在一起:
REGEX
doesn't validate values (like "2010-19-19")SimpleDateFormat
does not check format ("2010-1-2", "1-0002-003" are accepted)
REGEX
不验证值(如“2010-19-19”)SimpleDateFormat
不检查格式(接受“2010-1-2”、“1-0002-003”)
it's necessary to use both to validate format and value:
有必要同时使用两者来验证格式和值:
public static boolean isValid(String text) {
if (text == null || !text.matches("\d{4}-[01]\d-[0-3]\d"))
return false;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);
try {
df.parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
ThreadLocal 可用于避免为每次调用创建新的 SimpleDateFormat。
在多线程上下文中需要它,因为 SimpleDateFormat 不是线程安全的:
private static final ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);
System.out.println("created");
return df;
}
};
public static boolean isValid(String text) {
if (text == null || !text.matches("\d{4}-[01]\d-[0-3]\d"))
return false;
try {
format.get().parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
(same can be done for a Matcher, that also is not thread safe)
(对于匹配器也可以这样做,这也不是线程安全的)
回答by CKuharski
This will do it regex: "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"
This will take care of valid formats and valid dates. It will not validate the correct days of the month i.e. leap year.
这将执行正则表达式:"^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"
这将处理有效格式和有效日期。它不会验证月份的正确日期,即闰年。
String regex = "^((19|20)\d\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$";
Assert.assertTrue("Date: matched.", Pattern.matches(regex, "2011-1-1"));
Assert.assertFalse("Date (month): not matched.", Pattern.matches(regex, "2011-13-1"));
Good luck!
祝你好运!
回答by despot
I would go with a simple regex which will check that days doesn't have more than 31 days and months no more than 12. Something like:
我会使用一个简单的正则表达式来检查天数不超过 31 天,月数不超过 12 天。例如:
(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\d\d)
This is the format "dd-MM-yyyy". You can tweak it to your needs (for example take off the ? to make the leading 0 required - now its optional), and then use a custom logic to cut down to the specific rules like leap years February number of days case, and other months number of days cases. See the DateChecker code below.
这是格式“dd-MM-yyyy”。您可以根据自己的需要调整它(例如去掉 ? 使前导 0 成为必需 - 现在是可选的),然后使用自定义逻辑来减少特定规则,例如闰年二月天数情况,以及其他月天数情况。请参阅下面的 DateChecker 代码。
I am choosing this approach since I tested that this is the best one when performance is taken into account. I checked this (1st) approach versus 2nd approach of validating a date against a regex that takes care of the other use cases, and 3rd approach of using the same simple regex above in combination with SimpleDateFormat.parse(date).
The 1st approach was 4 times faster than the 2nd approach, and 8 times faster than the 3rd approach.See the self contained date checker and performance tester main class at the bottom.
One thing that I left unchecked is the joda time approach(s). (The more efficient date/time library).
我选择这种方法是因为我测试过这是考虑到性能时最好的方法。我检查了这种(第一种)方法与针对处理其他用例的正则表达式验证日期的第二种方法,以及将上述相同的简单正则表达式与 SimpleDateFormat.parse(date) 结合使用的第三种方法。
第一种方法比第二种方法快 4 倍,比第三种方法快 8 倍。请参阅底部的自包含日期检查器和性能测试器主类。我没有检查的一件事是 joda 时间方法。(更高效的日期/时间库)。
Date checker code:
日期检查器代码:
class DateChecker {
private Matcher matcher;
private Pattern pattern;
public DateChecker(String regex) {
pattern = Pattern.compile(regex);
}
/**
* Checks if the date format is a valid.
* Uses the regex pattern to match the date first.
* Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered).
*
* @param date the date that needs to be checked.
* @return if the date is of an valid format or not.
*/
public boolean check(final String date) {
matcher = pattern.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int day = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int year = Integer.parseInt(matcher.group(3));
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return day < 32;
case 4:
case 6:
case 9:
case 11: return day < 31;
case 2:
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
//its a leap year
return day < 30;
} else {
return day < 29;
}
default:
break;
}
}
}
return false;
}
public String getRegex() {
return pattern.pattern();
}
}
Date checking/testing and performance testing:
日期检查/测试和性能测试:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Tester {
private static final String[] validDateStrings = new String[]{
"1-1-2000", //leading 0s for day and month optional
"01-1-2000", //leading 0 for month only optional
"1-01-2000", //leading 0 for day only optional
"01-01-1800", //first accepted date
"31-12-2199", //last accepted date
"31-01-2000", //January has 31 days
"31-03-2000", //March has 31 days
"31-05-2000", //May has 31 days
"31-07-2000", //July has 31 days
"31-08-2000", //August has 31 days
"31-10-2000", //October has 31 days
"31-12-2000", //December has 31 days
"30-04-2000", //April has 30 days
"30-06-2000", //June has 30 days
"30-09-2000", //September has 30 days
"30-11-2000", //November has 30 days
};
private static final String[] invalidDateStrings = new String[]{
"00-01-2000", //there is no 0-th day
"01-00-2000", //there is no 0-th month
"31-12-1799", //out of lower boundary date
"01-01-2200", //out of high boundary date
"32-01-2000", //January doesn't have 32 days
"32-03-2000", //March doesn't have 32 days
"32-05-2000", //May doesn't have 32 days
"32-07-2000", //July doesn't have 32 days
"32-08-2000", //August doesn't have 32 days
"32-10-2000", //October doesn't have 32 days
"32-12-2000", //December doesn't have 32 days
"31-04-2000", //April doesn't have 31 days
"31-06-2000", //June doesn't have 31 days
"31-09-2000", //September doesn't have 31 days
"31-11-2000", //November doesn't have 31 days
"001-02-2000", //SimpleDateFormat valid date (day with leading 0s) even with lenient set to false
"1-0002-2000", //SimpleDateFormat valid date (month with leading 0s) even with lenient set to false
"01-02-0003", //SimpleDateFormat valid date (year with leading 0s) even with lenient set to false
"01.01-2000", //. invalid separator between day and month
"01-01.2000", //. invalid separator between month and year
"01/01-2000", /// invalid separator between day and month
"01-01/2000", /// invalid separator between month and year
"01_01-2000", //_ invalid separator between day and month
"01-01_2000", //_ invalid separator between month and year
"01-01-2000-12345", //only whole string should be matched
"01-13-2000", //month bigger than 13
};
/**
* These constants will be used to generate the valid and invalid boundary dates for the leap years. (For no leap year, Feb. 28 valid and Feb. 29 invalid; for a leap year Feb. 29 valid and Feb. 30 invalid)
*/
private static final int LEAP_STEP = 4;
private static final int YEAR_START = 1800;
private static final int YEAR_END = 2199;
/**
* This date regex will find matches for valid dates between 1800 and 2199 in the format of "dd-MM-yyyy".
* The leading 0 is optional.
*/
private static final String DATE_REGEX = "((0?[1-9]|[12][0-9]|3[01])-(0?[13578]|1[02])-(18|19|20|21)[0-9]{2})|((0?[1-9]|[12][0-9]|30)-(0?[469]|11)-(18|19|20|21)[0-9]{2})|((0?[1-9]|1[0-9]|2[0-8])-(0?2)-(18|19|20|21)[0-9]{2})|(29-(0?2)-(((18|19|20|21)(04|08|[2468][048]|[13579][26]))|2000))";
/**
* This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match.
* Keep in mind that String.matches tries to match only the whole string.
*/
private static final String DATE_REGEX_ONLY_WHOLE_STRING = "^" + DATE_REGEX + "$";
/**
* The simple regex (without checking for 31 day months and leap years):
*/
private static final String DATE_REGEX_SIMPLE = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\d\d)";
/**
* This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match.
*/
private static final String DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING = "^" + DATE_REGEX_SIMPLE + "$";
private static final SimpleDateFormat SDF = new SimpleDateFormat("dd-MM-yyyy");
static {
SDF.setLenient(false);
}
private static final DateChecker dateValidatorSimple = new DateChecker(DATE_REGEX_SIMPLE);
private static final DateChecker dateValidatorSimpleOnlyWholeString = new DateChecker(DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING);
/**
* @param args
*/
public static void main(String[] args) {
DateTimeStatistics dateTimeStatistics = new DateTimeStatistics();
boolean shouldMatch = true;
for (int i = 0; i < validDateStrings.length; i++) {
String validDate = validDateStrings[i];
matchAssertAndPopulateTimes(
dateTimeStatistics,
shouldMatch, validDate);
}
shouldMatch = false;
for (int i = 0; i < invalidDateStrings.length; i++) {
String invalidDate = invalidDateStrings[i];
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, invalidDate);
}
for (int year = YEAR_START; year < (YEAR_END + 1); year++) {
FebruaryBoundaryDates februaryBoundaryDates = createValidAndInvalidFebruaryBoundaryDateStringsFromYear(year);
shouldMatch = true;
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, februaryBoundaryDates.getValidFebruaryBoundaryDateString());
shouldMatch = false;
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, februaryBoundaryDates.getInvalidFebruaryBoundaryDateString());
}
dateTimeStatistics.calculateAvarageTimesAndPrint();
}
private static void matchAssertAndPopulateTimes(
DateTimeStatistics dateTimeStatistics,
boolean shouldMatch, String date) {
dateTimeStatistics.addDate(date);
matchAndPopulateTimeToMatch(date, DATE_REGEX, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegex());
matchAndPopulateTimeToMatch(date, DATE_REGEX_ONLY_WHOLE_STRING, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegexOnlyWholeString());
boolean matchesSimpleDateFormat = matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(date, dateTimeStatistics.getTimesTakenWithSimpleDateFormatParse());
matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
dateTimeStatistics.getTimesTakenWithDateRegexSimple(), shouldMatch,
date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE);
matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
dateTimeStatistics.getTimesTakenWithDateRegexSimpleOnlyWholeString(), shouldMatch,
date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING);
matchAndPopulateTimeToMatch(date, dateValidatorSimple, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimple());
matchAndPopulateTimeToMatch(date, dateValidatorSimpleOnlyWholeString, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimpleOnlyWholeString());
}
private static void matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
List<Long> times,
boolean shouldMatch, String date, boolean matchesSimpleDateFormat, String regex) {
boolean matchesFromRegex = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times);
assert !((matchesSimpleDateFormat && matchesFromRegex) ^ shouldMatch) : "Parsing with SimpleDateFormat and date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch;
}
private static void matchAndPopulateTimeToMatch(String date, String regex, boolean shouldMatch, List<Long> times) {
boolean matches = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times);
assert !(matches ^ shouldMatch) : "date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch;
}
private static void matchAndPopulateTimeToMatch(String date, DateChecker dateValidator, boolean shouldMatch, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches;
timestampStart = System.nanoTime();
matches = dateValidator.check(date);
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
assert !(matches ^ shouldMatch) : "date:" + date + "\ndateValidator with regex:" + dateValidator.getRegex() + "\nshouldMatch:" + shouldMatch;
}
private static boolean matchAndPopulateTimeToMatchAndReturnMatches(String date, String regex, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches;
timestampStart = System.nanoTime();
matches = date.matches(regex);
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
return matches;
}
private static boolean matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(String date, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches = true;
timestampStart = System.nanoTime();
try {
SDF.parse(date);
} catch (ParseException e) {
matches = false;
} finally {
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
}
return matches;
}
private static FebruaryBoundaryDates createValidAndInvalidFebruaryBoundaryDateStringsFromYear(int year) {
FebruaryBoundaryDates februaryBoundaryDates;
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
februaryBoundaryDates = new FebruaryBoundaryDates(
createFebruaryDateFromDayAndYear(29, year),
createFebruaryDateFromDayAndYear(30, year)
);
} else {
februaryBoundaryDates = new FebruaryBoundaryDates(
createFebruaryDateFromDayAndYear(28, year),
createFebruaryDateFromDayAndYear(29, year)
);
}
return februaryBoundaryDates;
}
private static String createFebruaryDateFromDayAndYear(int day, int year) {
return String.format("%d-02-%d", day, year);
}
static class FebruaryBoundaryDates {
private String validFebruaryBoundaryDateString;
String invalidFebruaryBoundaryDateString;
public FebruaryBoundaryDates(String validFebruaryBoundaryDateString,
String invalidFebruaryBoundaryDateString) {
super();
this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString;
this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString;
}
public String getValidFebruaryBoundaryDateString() {
return validFebruaryBoundaryDateString;
}
public void setValidFebruaryBoundaryDateString(
String validFebruaryBoundaryDateString) {
this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString;
}
public String getInvalidFebruaryBoundaryDateString() {
return invalidFebruaryBoundaryDateString;
}
public void setInvalidFebruaryBoundaryDateString(
String invalidFebruaryBoundaryDateString) {
this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString;
}
}
static class DateTimeStatistics {
private List<String> dates = new ArrayList<String>();
private List<Long> timesTakenWithDateRegex = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexOnlyWholeString = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexSimple = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexSimpleOnlyWholeString = new ArrayList<Long>();
private List<Long> timesTakenWithSimpleDateFormatParse = new ArrayList<Long>();
private List<Long> timesTakenWithdateValidatorSimple = new ArrayList<Long>();
private List<Long> timesTakenWithdateValidatorSimpleOnlyWholeString = new ArrayList<Long>();
public List<String> getDates() {
return dates;
}
public List<Long> getTimesTakenWithDateRegex() {
return timesTakenWithDateRegex;
}
public List<Long> getTimesTakenWithDateRegexOnlyWholeString() {
return timesTakenWithDateRegexOnlyWholeString;
}
public List<Long> getTimesTakenWithDateRegexSimple() {
return timesTakenWithDateRegexSimple;
}
public List<Long> getTimesTakenWithDateRegexSimpleOnlyWholeString() {
return timesTakenWithDateRegexSimpleOnlyWholeString;
}
public List<Long> getTimesTakenWithSimpleDateFormatParse() {
return timesTakenWithSimpleDateFormatParse;
}
public List<Long> getTimesTakenWithdateValidatorSimple() {
return timesTakenWithdateValidatorSimple;
}
public List<Long> getTimesTakenWithdateValidatorSimpleOnlyWholeString() {
return timesTakenWithdateValidatorSimpleOnlyWholeString;
}
public void addDate(String date) {
dates.add(date);
}
public void addTimesTakenWithDateRegex(long time) {
timesTakenWithDateRegex.add(time);
}
public void addTimesTakenWithDateRegexOnlyWholeString(long time) {
timesTakenWithDateRegexOnlyWholeString.add(time);
}
public void addTimesTakenWithDateRegexSimple(long time) {
timesTakenWithDateRegexSimple.add(time);
}
public void addTimesTakenWithDateRegexSimpleOnlyWholeString(long time) {
timesTakenWithDateRegexSimpleOnlyWholeString.add(time);
}
public void addTimesTakenWithSimpleDateFormatParse(long time) {
timesTakenWithSimpleDateFormatParse.add(time);
}
public void addTimesTakenWithdateValidatorSimple(long time) {
timesTakenWithdateValidatorSimple.add(time);
}
public void addTimesTakenWithdateValidatorSimpleOnlyWholeString(long time) {
timesTakenWithdateValidatorSimpleOnlyWholeString.add(time);
}
private void calculateAvarageTimesAndPrint() {
long[] sumOfTimes = new long[7];
int timesSize = timesTakenWithDateRegex.size();
for (int i = 0; i < timesSize; i++) {
sumOfTimes[0] += timesTakenWithDateRegex.get(i);
sumOfTimes[1] += timesTakenWithDateRegexOnlyWholeString.get(i);
sumOfTimes[2] += timesTakenWithDateRegexSimple.get(i);
sumOfTimes[3] += timesTakenWithDateRegexSimpleOnlyWholeString.get(i);
sumOfTimes[4] += timesTakenWithSimpleDateFormatParse.get(i);
sumOfTimes[5] += timesTakenWithdateValidatorSimple.get(i);
sumOfTimes[6] += timesTakenWithdateValidatorSimpleOnlyWholeString.get(i);
}
System.out.println("AVG from timesTakenWithDateRegex (in nanoseconds):" + (double) sumOfTimes[0] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[1] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimple (in nanoseconds):" + (double) sumOfTimes[2] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[3] / timesSize);
System.out.println("AVG from timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) sumOfTimes[4] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimple + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[2] + sumOfTimes[4]) / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[3] + sumOfTimes[4]) / timesSize);
System.out.println("AVG from timesTakenWithdateValidatorSimple (in nanoseconds):" + (double) sumOfTimes[5] / timesSize);
System.out.println("AVG from timesTakenWithdateValidatorSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[6] / timesSize);
}
}
static class DateChecker {
private Matcher matcher;
private Pattern pattern;
public DateChecker(String regex) {
pattern = Pattern.compile(regex);
}
/**
* Checks if the date format is a valid.
* Uses the regex pattern to match the date first.
* Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered).
*
* @param date the date that needs to be checked.
* @return if the date is of an valid format or not.
*/
public boolean check(final String date) {
matcher = pattern.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int day = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int year = Integer.parseInt(matcher.group(3));
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return day < 32;
case 4:
case 6:
case 9:
case 11: return day < 31;
case 2:
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
//its a leap year
return day < 30;
} else {
return day < 29;
}
default:
break;
}
}
}
return false;
}
public String getRegex() {
return pattern.pattern();
}
}
}
Some useful notes:
- to enable the assertions (assert checks) you need to use -ea argument when running the tester. (In eclipse this is done by editing the Run/Debug configuration -> Arguments tab -> VM Arguments -> insert "-ea"
- the regex above is bounded to years 1800 to 2199
- you don't need to use ^ at the beginning and $ at the end to match only the whole date string. The String.matches takes care of that.
- make sure u check the valid and invalid cases and change them according the rules that you have.
- the "only whole string" version of each regex gives the same speed as the "normal" version (the one without ^ and $). If you see performance differences this is because java "gets used" to processing the same instructions so the time lowers. If you switch the lines where the "normal" and the "only whole string" version execute, you will see this proven.
一些有用的注意事项:
- 要启用断言(断言检查),您需要在运行测试程序时使用 -ea 参数。(在 Eclipse 中,这是通过编辑运行/调试配置 -> 参数选项卡 -> VM 参数 -> 插入“-ea”来完成的
- 上面的正则表达式被限制在 1800 到 2199
年 - 你不需要在开头和 $ 结尾只匹配整个日期字符串。String.matches 负责这一点。
- 确保你检查有效和无效的情况,并根据你拥有的规则更改它们。
- 每个正则表达式的“唯一完整字符串”版本与“正常”版本(没有 ^ 和 $ 的版本)具有相同的速度。如果您看到性能差异,这是因为 java “习惯于”处理相同的指令,因此时间减少了。如果您切换“正常”和“仅整个字符串”版本执行的行,您将看到这一点得到证实。
Hope this helps someone!
Cheers,
Despot
希望这对某人有帮助!
干杯,
暴君
回答by Mike
java.time
时间
The proper (and easy) way to do date/time validation using Java 8+ is to use the java.time.format.DateTimeFormatter
class. Using a regex for validation isn't really ideal for dates. For the example case in this question:
使用 Java 8+ 进行日期/时间验证的正确(且简单)方法是使用java.time.format.DateTimeFormatter
该类。使用正则表达式进行验证对于日期来说并不是很理想。对于此问题中的示例案例:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
LocalDate date = formatter.parse(text, LocalDate::from);
} catch (DateTimeParseException e) {
// Thrown if text could not be parsed in the specified format
}
This code will parse the text, validate that it is a valid date, and also return the date as a LocalDate
object. Note that the DateTimeFormatter
class has a number of static predefined date formats matching ISO standards if your use case matches any of them.
此代码将解析文本,验证它是否为有效日期,并将日期作为LocalDate
对象返回。请注意,DateTimeFormatter
如果您的用例与 ISO 标准匹配,则该类具有许多与 ISO 标准匹配的静态预定义日期格式。
回答by Ali
If you want a simple regex then it won't be accurate. https://www.freeformatter.com/java-regex-tester.html#ad-outputoffers a tool to test your Java regex. Also, at the bottom you can find some suggested regexes for validating a date.
如果你想要一个简单的正则表达式,那么它就不准确了。 https://www.freeformatter.com/java-regex-tester.html#ad-output提供了一个工具来测试您的 Java 正则表达式。此外,在底部您可以找到一些用于验证日期的建议正则表达式。
ISO date format (yyyy-mm-dd):
ISO 日期格式 (yyyy-mm-dd):
^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$
ISO date format (yyyy-mm-dd) with separators '-' or '/' or '.' or ' '. Forces usage of same separator accross date.
ISO 日期格式 (yyyy-mm-dd) 带分隔符“-”或“/”或“.” 或者 ' '。强制跨日期使用相同的分隔符。
^[0-9]{4}([- /.])(((0[13578]|(10|12))(0[1-9]|[1-2][0-9]|3[0-1]))|(02(0[1-9]|[1-2][0-9]))|((0[469]|11)(0[1-9]|[1-2][0-9]|30)))$
United States date format (mm/dd/yyyy)
美国日期格式 (mm/dd/yyyy)
^(((0[13578]|(10|12))/(0[1-9]|[1-2][0-9]|3[0-1]))|(02/(0[1-9]|[1-2][0-9]))|((0[469]|11)/(0[1-9]|[1-2][0-9]|30)))/[0-9]{4}$
Hours and minutes, 24 hours format (HH:MM):
小时和分钟,24 小时格式 (HH:MM):
^(20|21|22|23|[01]\d|\d)((:[0-5]\d){1,2})$
Good luck
祝你好运
回答by JoboFive
Below added code is working for me if you are using pattern dd-MM-yyyy.
如果您使用模式 dd-MM-yyyy,下面添加的代码对我有用。
public boolean isValidDate(String date) {
boolean check;
String date1 = "^(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-([12][0-9]{3})$";
check = date.matches(date1);
return check;
}