我感到奇怪的是,Date
在Java中创建对象的最明显的方法已被弃用,并且似乎已被使用宽大日历的不太明显的方法所“替代”。
如何检查以日,月和年的组合形式给出的日期是否为有效日期?
例如,2008-02-31(如yyyy-mm-dd)将是无效日期。
Answers:
当前的方法是使用日历类。它具有setLenient方法,该方法将验证日期和引发异常,如果超出示例,则抛出异常。
忘记添加:如果您获得日历实例并使用日期设置时间,那么这就是获得验证的方式。
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}
java.util.Date
,java.util.Calendar
和java.text.SimpleDateFormat
现在已被遗留)由Java 8及更高版本中内置的java.time类取代。请参见Oracle教程。
密钥是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;
}
}
setLenient
:SimpleDateFormat
始终解析直到匹配模式,然后忽略字符串的其余部分,从而得到201
年份。
java.util.Date
,java.util.Calendar
和java.text.SimpleDateFormat
现在的遗产,由取代java.time内置到Java 8和更高等级。请参见Oracle教程。
如@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”的解释不同) )
因此,完整的日期字符串测试可能看起来像这样:regex匹配的组合,然后是强制日期转换。正则表达式的诀窍是使其对语言环境友好。
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();
我最近听说乔达时间,以为我可以比较一下。两点:
使用起来非常简单:
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;
}
Date
,SimpleDateFormat
等)现在被现代的java.time类所取代。同样,Joda-Time项目处于维护模式,建议迁移到java.time类。
您可以使用SimpleDateFormat
例如:
boolean isLegalDate(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
return sdf.parse(s, new ParsePosition(0)) != null;
}
java.util.Date
,java.util.Calendar
和java.text.SimpleDateFormat
现在的遗产,由取代java.time内置到Java 8和更高等级。请参见Oracle教程。
使用严格模式上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
ResolverStyle.SMART
ResolverStyle.STRICT
我们可以看到,在ResolverStyle.LENIENT
模式下,无效日期被向前移动了相等的天数。在ResolverStyle.SMART
模式(默认)下,做出合理的决定是将日期保留在一个月之内,并与a年的2月29日进行匹配,因为该月没有第31天。该ResolverStyle.STRICT
模式引发异常,抱怨没有这样的日期。
所有这三个都是合理的,具体取决于您的业务问题和策略。听起来您的情况是您希望严格模式拒绝无效日期而不是对其进行调整。
该java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.Date
,Calendar
,和SimpleDateFormat
。
要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要类。java.sql.*
在哪里获取java.time类?
该ThreeTen-额外项目与其他类扩展java.time。该项目是将来可能向java.time添加内容的试验场。你可能在这里找到一些有用的类,比如Interval
,YearWeek
,YearQuarter
,和更多。
使用Java 8和更高版本中内置的Date and Time API(java.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;
}
使用标准库的另一种严格解决方案是执行以下操作:
1)使用您的模式创建严格的SimpleDateFormat
2)尝试使用格式对象解析用户输入的值
3)如果成功,则使用相同的日期格式(来自(1))将由(2)生成的日期重新格式化。
4)将重新格式化的日期与用户输入的原始值进行比较。如果它们相等,则输入的值将严格匹配您的模式。
这样,您不需要创建复杂的正则表达式-就我而言,我需要支持所有SimpleDateFormat的模式语法,而不是仅限于某些类型,例如几天,几个月和几年。
以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+");
}
我认为最简单的方法是将字符串转换为日期对象,然后将其转换回字符串。如果两个字符串仍然匹配,则给定的日期字符串会很好。
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;
}
}
看起来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;
}
关于使用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 "";
}
}
使用“旧式”日期格式,我们可以格式化结果并将其与源进行比较。
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;
}
}
该执行对模式为'01 .01.2004'的source = 0.1.0.04说'false'
如果您需要严格的验证,则将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;
}