我发现很奇怪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时间,并且认为我会比较.两点:
与SimpleDateFormat不同,似乎更擅长对日期字符串中的无效字符进行严格处理
还没有看到用它强制执行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; }
您可以使用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"); }
使用严格模式上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:无法解析文本'31/02/2000':无效日期'FEBRUARY 31'
我们可以看到在ResolverStyle.LENIENT
模式下,无效日期向前移动相同的天数.在ResolverStyle.SMART
模式(默认值)下,做出逻辑决定,将日期保持在月份内,并在2月29日的闰年中与当月的最后一天保持一致,因为该月没有第31天.该ResolverStyle.STRICT
模式抛出异常,抱怨没有这样的日期.
根据您的业务问题和政策,所有这三项都是合理的.在您的情况下,您希望严格模式拒绝无效日期而不是调整它.
该java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date
,Calendar
,和SimpleDateFormat
.
现在处于维护模式的Joda-Time项目建议迁移到java.time类.
要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.
您可以直接与数据库交换java.time对象.使用符合JDBC 4.2或更高版本的JDBC驱动程序.不需要字符串,不需要课程.java.sql.*
从哪里获取java.time类?
Java SE 8, Java SE 9及更高版本
内置.
带有捆绑实现的标准Java API的一部分.
Java 9增加了一些小功能和修复.
Java SE 6和 Java SE 7
许多java.time功能都被反向移植到ThreeTen-Backport中的 Java 6和7 .
Android的
更高版本的Android捆绑java.time类的实现.
对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述).请参阅如何使用ThreeTenABP ....
该ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval
,YearWeek
,YearQuarter
,和更多.
使用Java 8及更高版本中内置的Date和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; }
建立在应允@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+"); }
使用标准库的另一种严格解决方案是执行以下操作:
1)使用您的模式创建严格的SimpleDateFormat
2)尝试使用format对象解析用户输入的值
3)如果成功,使用相同的日期格式(从(1))重新格式化由(2)得到的日期
4)将重新格式化的日期与用户输入的原始值进行比较.如果它们相等,则输入的值严格匹配您的模式.
这样,您不需要创建复杂的正则表达式 - 在我的情况下,我需要支持所有SimpleDateFormat的模式语法,而不是仅限于某些类型,例如几天,几个月和几年.