我觉得奇怪的是,Date
在 Java 中创建对象的最明显方法已被弃用,并且似乎已被不那么明显的使用宽松日历“取代”。
您如何检查以日、月和年的组合形式给出的日期是否为有效日期?
例如,2008-02-31(如 yyyy-mm-dd)将是无效日期。
我觉得奇怪的是,Date
在 Java 中创建对象的最明显方法已被弃用,并且似乎已被不那么明显的使用宽松日历“取代”。
您如何检查以日、月和年的组合形式给出的日期是否为有效日期?
例如,2008-02-31(如 yyyy-mm-dd)将是无效日期。
关键是df.setLenient(false); . 这对于简单的情况来说绰绰有余。如果您正在寻找更强大(我怀疑)和/或替代库(如 joda-time),请查看用户“tardate”的答案
final static String DATE_FORMAT = "dd-MM-yyyy";
public static boolean isDateValid(String date)
{
try {
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
df.setLenient(false);
df.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
如@Maglob 所示,基本方法是使用SimpleDateFormat.parse测试从字符串到日期的转换。这将捕获无效的日/月组合,如 2008-02-31。
然而,在实践中这很少足够,因为 SimpleDateFormat.parse 非常自由。您可能会担心两种行为:
日期字符串中的无效字符 令人惊讶的是,2008-02-2x 将作为有效日期“通过”,例如区域设置格式 =“yyyy-MM-dd”。即使 isLenient==false。
年份:2、3 或 4 位数字? 您可能还希望强制执行 4 位数年份,而不是允许默认的 SimpleDateFormat 行为(这将根据您的格式是“yyyy-MM-dd”还是“yy-MM-dd”以不同方式解释“12-02-31” )
因此,完整的字符串到日期测试可能如下所示:正则表达式匹配的组合,然后是强制日期转换。正则表达式的诀窍是使其对语言环境友好。
Date parseDate(String maybeDate, String format, boolean lenient) {
Date date = null;
// test date string matches format structure using regex
// - weed out illegal characters and enforce 4-digit year
// - create the regex based on the local format string
String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {
// date string matches format structure,
// - now test it can be converted to a valid date
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
sdf.applyPattern(format);
sdf.setLenient(lenient);
try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
}
return date;
}
// used like this:
Date date = parseDate( "21/5/2009", "d/M/yyyy", false);
请注意,正则表达式假定格式字符串仅包含日、月、年和分隔符。除此之外,格式可以是任何语言环境格式:“d/MM/yy”、“yyyy-MM-dd”等。当前语言环境的格式字符串可以这样获得:
Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();
我最近一直在听说joda time,我想我会比较一下。两点:
使用起来非常简单:
import org.joda.time.format.*;
import org.joda.time.DateTime;
org.joda.time.DateTime parseDate(String maybeDate, String format) {
org.joda.time.DateTime date = null;
try {
DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
date = fmt.parseDateTime(maybeDate);
} catch (Exception e) { }
return date;
}
您可以使用SimpleDateFormat
例如:
boolean isLegalDate(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
return sdf.parse(s, new ParsePosition(0)) != null;
}
当前的方法是使用日历类。它具有setLenient方法,该方法将验证日期并在超出范围时抛出异常,如您的示例所示。
忘了补充:如果您获得日历实例并使用您的日期设置时间,这就是您获得验证的方式。
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}
基于Aravind解决ceklock 在他的评论中指出的问题的答案,我添加了一种方法来验证dateString
不包含任何无效字符。
这是我的做法:
private boolean isDateCorrect(String dateString) {
try {
Date date = mDateFormatter.parse(dateString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return matchesOurDatePattern(dateString); //added my method
}
catch (ParseException e) {
return false;
}
}
/**
* This will check if the provided string matches our date format
* @param dateString
* @return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
*/
private boolean matchesDatePattern(String dateString) {
return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
使用标准库的另一种严格解决方案是执行以下操作:
1)使用您的模式创建一个严格的 SimpleDateFormat
2) 尝试使用格式对象解析用户输入的值
3)如果成功,使用相同的日期格式(来自(1))重新格式化(2)产生的日期
4) 将重新格式化的日期与用户输入的原始值进行比较。如果它们相等,则输入的值与您的模式严格匹配。
这样,您不需要创建复杂的正则表达式——在我的例子中,我需要支持所有 SimpleDateFormat 的模式语法,而不是仅限于某些类型,例如天、月和年。
我建议你使用org.apache.commons.validator.GenericValidator
apache 的类。
GenericValidator.isDate(String value, String datePattern, boolean strict);
注意:严格 - 是否与 datePattern 完全匹配。
我认为最简单的就是将字符串转换为日期对象并将其转换回字符串。如果两个字符串仍然匹配,则给定的日期字符串很好。
public boolean isDateValid(String dateString, String pattern)
{
try
{
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
if (sdf.format(sdf.parse(dateString)).equals(dateString))
return true;
}
catch (ParseException pe) {}
return false;
}
假设这两个都是字符串(否则它们已经是有效的日期),这是一种方法:
package cruft;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateValidator
{
private static final DateFormat DEFAULT_FORMATTER;
static
{
DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
DEFAULT_FORMATTER.setLenient(false);
}
public static void main(String[] args)
{
for (String dateString : args)
{
try
{
System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
}
catch (ParseException e)
{
System.out.println("could not parse " + dateString);
}
}
}
public static Date convertDateString(String dateString) throws ParseException
{
return DEFAULT_FORMATTER.parse(dateString);
}
}
这是我得到的输出:
java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011
Process finished with exit code 0
如您所见,它确实可以很好地处理您的两种情况。
这对我很有用。本建议的方法。
private static boolean isDateValid(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
Date d = asDate(s);
if (sdf.format(d).equals(s)) {
return true;
} else {
return false;
}
} catch (ParseException e) {
return false;
}
}
看起来即使在setLenient(false);之后SimpleDateFormat也没有严格检查模式;方法应用在它上面,所以我使用下面的方法来验证输入的日期是否是有效的日期,或者不是按照提供的模式。
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
boolean valid = true;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
try {
formatter.parse(dateString);
} catch (DateTimeParseException e) {
valid = false;
}
return valid;
}
关于使用 SimpleDateFormat 的两条评论。
如果声明为静态访问,则应将其声明为静态实例,因为它不是线程安全的,因此应同步访问
比为每个日期解析实例化一个实例更好的 IME。
上面的日期解析方法很好,我只是在现有方法中添加了新的检查,使用格式化程序仔细检查转换后的日期和原始日期,所以它几乎适用于我验证的每种情况。例如 02/29/2013 是无效日期。给定函数根据当前可接受的日期格式解析日期。如果日期未成功解析,则返回 true。
public final boolean validateDateFormat(final String date) {
String[] formatStrings = {"MM/dd/yyyy"};
boolean isInvalidFormat = false;
Date dateObj;
for (String formatString : formatStrings) {
try {
SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
sdf.applyPattern(formatString);
sdf.setLenient(false);
dateObj = sdf.parse(date);
System.out.println(dateObj);
if (date.equals(sdf.format(dateObj))) {
isInvalidFormat = false;
break;
}
} catch (ParseException e) {
isInvalidFormat = true;
}
}
return isInvalidFormat;
}
这是我为 Node 环境所做的,没有使用外部库:
Date.prototype.yyyymmdd = function() {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return zeroPad([yyyy, mm, dd].join('-'));
};
function zeroPad(date_string) {
var dt = date_string.split('-');
return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}
function isDateCorrect(in_string) {
if (!matchesDatePattern) return false;
in_string = zeroPad(in_string);
try {
var idate = new Date(in_string);
var out_string = idate.yyyymmdd();
return in_string == out_string;
} catch(err) {
return false;
}
function matchesDatePattern(date_string) {
var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
return dateFormat.test(date_string);
}
}
以下是如何使用它:
isDateCorrect('2014-02-23')
true
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
int daysInMonth;
boolean leapYear;
leapYear = checkLeap(year);
if (month == 4 || month == 6 || month == 9 || month == 11)
daysInMonth = 30;
else if (month == 2)
daysInMonth = (leapYear) ? 29 : 28;
else
daysInMonth = 31;
return daysInMonth;
}
// to check a year is leap or not
private boolean checkLeap(int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
这是我要检查的日期格式:
public static boolean checkFormat(String dateTimeString) {
return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
|| dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
public static String detectDateFormat(String inputDate, String requiredFormat) {
String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
String dateFormat;
if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMddyyyy";
} else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
dateFormat = "ddMMyyyy";
} else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
dateFormat = "yyyyMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
dateFormat = "yyyyddMM";
} else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
dateFormat = "ddMMMyyyy";
} else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMMddyyyy";
} else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
dateFormat = "yyyyMMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
dateFormat = "yyyyddMMM";
} else {
return "Pattern Not Added";
//add your required regex
}
try {
String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));
return formattedDate;
} catch (Exception e) {
//
return "";
}
}
如果您喜欢严格验证,则将 setLenient 设置为 false
public boolean isThisDateValid(String dateToValidate, String dateFromat){
if(dateToValidate == null){
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
try {
//if not valid, it will throw ParseException
Date date = sdf.parse(dateToValidate);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
return false;
}
return true;
}
使用“旧”日期格式,我们可以格式化结果并将其与源进行比较。
public boolean isValidFormat(String source, String pattern) {
SimpleDateFormat sd = new SimpleDateFormat(pattern);
sd.setLenient(false);
try {
Date date = sd.parse(source);
return date != null && sd.format(date).equals(source);
} catch (Exception e) {
return false;
}
}
这段摘录对 source=01.01.04 说“假”,模式为“01.01.2004”
我们可以org.apache.commons.validator.GenericValidator
直接使用 's 方法而无需添加整个库:
public static boolean isValidDate(String value, String datePattern, boolean strict) {
if (value == null
|| datePattern == null
|| datePattern.length() <= 0) {
return false;
}
SimpleDateFormat formatter = new SimpleDateFormat(datePattern, Locale.ENGLISH);
formatter.setLenient(false);
try {
formatter.parse(value);
} catch(ParseException e) {
return false;
}
if (strict && (datePattern.length() != value.length())) {
return false;
}
return true;
}