如何在Java中检查日期

时间:2008-10-22 18:04:09

标签: java validation calendar date

我觉得奇怪的是,在Java中创建Date对象的最明显的方法已经被弃用,并且似乎被“替换”了一个不太明显的使用宽松日历。

如何检查日期,月份和年份组合的日期是否为有效日期?

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

22 个答案:

答案 0 :(得分:69)

键是 df.setLenient(false); 。这对于简单的案例来说已经足够了。如果您正在寻找更健壮(我怀疑)和/或替代库(如joda-time),请查看answer by the user "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;
        }
}

答案 1 :(得分:45)

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

我最近听说过joda time并且认为我会比较。两点:

  1. 与SimpleDateFormat
  2. 不同,似乎更擅长对日期字符串中的无效字符进行严格处理
  3. 看不到用它来强制执行4位数年份的方法(但我想你可以为此目的创建自己的DateTimeFormatter
  4. 使用起来非常简单:

    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;
    }
    

答案 2 :(得分:36)

您可以使用SimpleDateFormat

例如:

boolean isLegalDate(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setLenient(false);
    return sdf.parse(s, new ParsePosition(0)) != null;
}

答案 3 :(得分:29)

目前的方法是使用日历类。它具有setLenient方法,如果超出范围,将验证日期和抛出以及异常,如示例所示。

忘记添加: 如果您获得日历实例并使用日期设置时间,则可通过此方式进行验证。

Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
    cal.getTime();
}
catch (Exception e) {
  System.out.println("Invalid date");
}

答案 4 :(得分:14)

TL;博士

使用strict mode上的java.time.DateTimeFormatter来解析LocalDate。陷阱DateTimeParseException

LocalDate.parse(                   // Represent a date-only value, without time-of-day and without time zone.
    "31/02/2000" ,                 // Input string.
    DateTimeFormatter              // Define a formatting pattern to match your input string.
    .ofPattern ( "dd/MM/uuuu" )
    .withResolverStyle ( ResolverStyle.STRICT )  // Specify leniency in tolerating questionable inputs.
)

解析后,您可能会检查合理的值。例如,过去一百年内的出生日期。

birthDate.isAfter( LocalDate.now().minusYears( 100 ) )

避免遗留日期时间类

避免使用最早版本的Java附带的麻烦的旧日期时间类。现在取代java.time类。

LocalDate& DateTimeFormatter& ResolverStyle

LocalDate类表示没有时间且没有时区的仅限日期的值。

String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
    LocalDate ld = LocalDate.parse ( input , f );
    System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
    System.out.println ( "ERROR: " + e );
}

java.time.DateTimeFormatter类可以设置为使用ResolverStyle枚举中定义的三种宽松模式中的任何一种来解析字符串。我们在上面的代码中插入一行来尝试每种模式。

f = f.withResolverStyle ( ResolverStyle.LENIENT );

结果:

  • ResolverStyle.LENIENT
    ld:2000-03-02
  • ResolverStyle.SMART
    ld:2000-02-29
  • ResolverStyle.STRICT
    错误:java.time.format.DateTimeParseException:Text' 31/02/02'无法解析:日期无效' 2月31日

我们可以看到,在ResolverStyle.LENIENT模式下,无效日期会向前移动相同的天数。在ResolverStyle.SMART模式(默认值)下,做出逻辑决定,将日期保持在月份内,并在2月29日的闰年中与该月的最后一天保持一致,因为没有第31天月。 ResolverStyle.STRICT模式抛出异常,抱怨没有这样的日期。

根据您的业务问题和政策,所有这三项都是合理的。在您的情况下,您希望严格模式拒绝无效日期,而不是调整它。

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 5 :(得分:12)

java.time

在Java 8及更高版本中内置Date and Time APIjava.time类)后,您可以使用LocalDate类。

public static boolean isDateValid(int year, int month, int day) {
    boolean dateIsValid = true;
    try {
        LocalDate.of(year, month, day);
    } catch (DateTimeException e) {
        dateIsValid = false;
    }
    return dateIsValid;
}

答案 6 :(得分:6)

根据@Pangea的回答来解决@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+");
}

答案 7 :(得分:6)

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

1)使用您的模式

创建一个严格的SimpleDateFormat

2)尝试使用格式对象

解析用户输入的值

3)如果成功,使用相同的日期格式(从(1))重新格式化由(2)得到的日期

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

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

答案 8 :(得分:4)

我建议您使用apache中的org.apache.commons.validator.GenericValidator类。

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

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

答案 9 :(得分:3)

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

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;
}

答案 10 :(得分:2)

这对我很有用。 Ben提出的方法。

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;
    }
}

答案 11 :(得分: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

正如您所看到的,它可以很好地处理您的两个案例。

答案 12 :(得分:2)

看起来 SimpleDateFormat 并没有严格检查模式,即使在 setLenient(false); 方法上应用了该模式之后,我也使用了下面的方法来验证输入的日期是否为有效日期(按照提供的格式)。

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;
}

答案 13 :(得分:1)

关于使用SimpleDateFormat的两条评论。

  

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

对于每个日期解析实例化实例更好的IME。

答案 14 :(得分:0)

上面的日期解析方法很好,我只是添加了新的检查现有方法,使用formater将原始日期仔细检查转换日期,因此它几乎适用于我验证的每种情况。例如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;
    }

答案 15 :(得分: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

答案 16 :(得分: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;
}

答案 17 :(得分: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");
}

答案 18 :(得分: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 "";
        }

    }

答案 19 :(得分: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;
}

答案 20 :(得分: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;
    }
}

此执行程序对来源= 1.0.1.04的模式为'01 .01.2004'的内容为'false'

答案 21 :(得分:0)

我们可以直接使用org.apache.commons.validator.GenericValidator的方法,而无需添加整个库:

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;
}