90

我觉得奇怪的是,Date在 Java 中创建对象的最明显方法已被弃用,并且似乎已被不那么明显的使用宽松日历“取代”。

您如何检查以日、月和年的组合形式给出的日期是否为有效日期?

例如,2008-02-31(如 yyyy-mm-dd)将是无效日期。

4

22 回答 22

84

关键是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;
        }
}
于 2010-12-24T19:46:19.473 回答
49

如@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 - 更好的选择?

我最近一直在听说joda time,我想我会比较一下。两点:

  1. 与 SimpleDateFormat 不同,似乎更擅长严格处理日期字符串中的无效字符
  2. 还看不到强制执行 4 位数年份的方法(但我想您可以为此创建自己的DateTimeFormatter )

使用起来非常简单:

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;
}
于 2009-05-21T10:08:16.217 回答
40

您可以使用SimpleDateFormat

例如:

boolean isLegalDate(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setLenient(false);
    return sdf.parse(s, new ParsePosition(0)) != null;
}
于 2008-10-22T19:35:44.750 回答
39

当前的方法是使用日历类。它具有setLenient方法,该方法将验证日期并在超出范围时抛出异常,如您的示例所示。

忘了补充:如果您获得日历实例并使用您的日期设置时间,这就是您获得验证的方式。

Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
    cal.getTime();
}
catch (Exception e) {
  System.out.println("Invalid date");
}
于 2008-10-22T18:08:07.970 回答
34
于 2016-09-22T22:25:09.833 回答
14

java.time

通过Java 8 及更高版本中内置的日期和时间 APIjava.timeLocalDate类),您可以使用该类。

public static boolean isDateValid(int year, int month, int day) {
    try {
        LocalDate.of(year, month, day);
    } catch (DateTimeException e) {
        return false;
    }
    return true;
}
于 2014-05-02T22:55:28.550 回答
8

基于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+");
}
于 2015-05-29T11:54:10.963 回答
7

使用标准库的另一种严格解决方案是执行以下操作:

1)使用您的模式创建一个严格的 SimpleDateFormat

2) 尝试使用格式对象解析用户输入的值

3)如果成功,使用相同的日期格式(来自(1))重新格式化(2)产生的日期

4) 将重新格式化的日期与用户输入的原始值进行比较。如果它们相等,则输入的值与您的模式严格匹配。

这样,您不需要创建复杂的正则表达式——在我的例子中,我需要支持所有 SimpleDateFormat 的模式语法,而不是仅限于某些类型,例如天、月和年。

于 2010-04-12T14:00:28.113 回答
5

我建议你使用org.apache.commons.validator.GenericValidatorapache 的类。

GenericValidator.isDate(String value, String datePattern, boolean strict);

注意:严格 - 是否与 datePattern 完全匹配。

于 2013-07-17T21:33:01.917 回答
5

我认为最简单的就是将字符串转换为日期对象并将其转换回字符串。如果两个字符串仍然匹配,则给定的日期字符串很好。

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;
}
于 2016-02-04T09:49:20.663 回答
2

假设这两个都是字符串(否则它们已经是有效的日期),这是一种方法:

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

如您所见,它确实可以很好地处理您的两种情况。

于 2010-12-24T19:45:01.603 回答
2

这对我很有用。本建议的方法。

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;
    }
}
于 2013-09-06T20:38:08.320 回答
2

看起来即使在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;
}
于 2019-07-30T09:04:15.807 回答
1

关于使用 SimpleDateFormat 的两条评论。

如果声明为静态访问,则应将其声明为静态实例,因为它不是线程安全的,因此应同步访问

比为每个日期解析实例化一个实例更好的 IME。

于 2009-05-21T15:03:45.370 回答
0

上面的日期解析方法很好,我只是在现有方法中添加了新的检查,使用格式化程序仔细检查转换后的日期和原始日期,所以它几乎适用于我验证的每种情况。例如 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;
    }
于 2013-03-13T10:11:44.423 回答
0

这是我为 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
于 2016-09-01T16:51:24.840 回答
0
// 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;
}
于 2017-07-05T05:09:04.280 回答
0

这是我要检查的日期格式:

 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");
}
于 2018-10-31T21:07:13.677 回答
0
        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 "";
        }

    }
于 2019-07-04T12:52:23.463 回答
0

如果您喜欢严格验证,则将 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;
}
于 2019-07-05T17:28:20.473 回答
0

使用“旧”日期格式,我们可以格式化结果并将其与源进行比较。

    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”

于 2019-10-02T21:32:38.420 回答
0

我们可以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;
}
于 2021-06-21T13:40:24.473 回答