如何在 Java 中检查日期的完整性
我觉得奇怪的是,在 Java 中创建 Date 对象的最明显的方法已被弃用,并且似乎已被不太明显的宽松日历“取代”。
如何检查以日、月、年组合形式给出的日期是否有效?
例如,2008-02-31(如 yyyy-mm-dd)将是无效日期。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(25)
关键是 df.setLenient(false);。这对于简单的情况来说已经足够了。如果您正在寻找更强大的(我对此表示怀疑)和/或替代库,例如 joda-time< /a>,然后查看用户“tardate”的回答
Key is
df.setLenient(false);
. This is more than enough for simple cases. If you are looking for a more robust (I doubt that) and/or alternate libraries like joda-time, then look at the answer by user "tardate"如 @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” )
使用标准库的严格解决方案
因此,完整的字符串到日期测试可能如下所示:正则表达式匹配的组合,然后是强制日期转换。正则表达式的技巧是使其适合区域设置。
请注意,正则表达式假定格式字符串仅包含日、月、年和分隔符。除此之外,格式可以是任何区域设置格式:“d/MM/yy”、“yyyy-MM-dd”等。当前语言环境的格式字符串可以这样获取:
Joda Time - Better Alternative?
我最近听说过 joda 时间,我想比较一下。两点:
使用起来非常简单:
As shown by @Maglob, the basic approach is to test the conversion from string to date using SimpleDateFormat.parse. That will catch invalid day/month combinations like 2008-02-31.
However, in practice that is rarely enough since SimpleDateFormat.parse is exceedingly liberal. There are two behaviours you might be concerned with:
Invalid characters in the date string
Surprisingly, 2008-02-2x will "pass" as a valid date with locale format = "yyyy-MM-dd" for example. Even when isLenient==false.
Years: 2, 3 or 4 digits?
You may also want to enforce 4-digit years rather than allowing the default SimpleDateFormat behaviour (which will interpret "12-02-31" differently depending on whether your format was "yyyy-MM-dd" or "yy-MM-dd")
A Strict Solution with the Standard Library
So a complete string to date test could look like this: a combination of regex match, and then a forced date conversion. The trick with the regex is to make it locale-friendly.
Note that the regex assumes the format string contains only day, month, year, and separator characters. Aside from that, format can be in any locale format: "d/MM/yy", "yyyy-MM-dd", and so on. The format string for the current locale could be obtained like this:
Joda Time - Better Alternative?
I've been hearing about joda time recently and thought I'd compare. Two points:
It's quite simple to use:
tl;dr
使用严格模式 上
java.time.DateTimeFormatter
解析本地日期
。DateTimeParseException
的陷阱。解析后,您可以检查合理的值。例如,最近一百年内的出生日期。
避免遗留日期时间类
避免使用最早版本的 Java 附带的麻烦的旧日期时间类。现在被 java.time 取代类。
LocalDate
&DateTimeFormatter
&ResolverStyle
LocalDate
类表示仅日期值,没有时间和时区。java.time.DateTimeFormatter
类可以设置为使用
ResolverStyle
枚举。我们在上面的代码中插入一行来尝试每种模式。结果:
ResolverStyle.LENIENT
ld:2000-03-02
ResolverStyle.SMART
ld:2000-02-29
ResolverStyle.STRICT< /code>
错误:java.time.format.DateTimeParseException:无法解析文本“31/02/2000”:无效日期“FEBRUARY 31”
我们可以在
ResolverStyle.LENIENT
模式,无效日期向前推进了同等天数。在ResolverStyle.SMART
模式(默认),会做出一个逻辑决定,将日期保留在月份内,并使用该月的最后一天,即闰年的 2 月 29 日,因为该日期没有第 31 天月。
ResolverStyle.STRICT
模式抛出异常,抱怨没有这样的日期。
根据您的业务问题和政策,这三者都是合理的。听起来在您的情况下您希望严格模式拒绝无效日期而不是调整它。
关于 java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如
java.util.Date
< /a>, <代码>日历,&SimpleDateFormat< /代码>
。
要了解更多信息,请参阅 Oracle 教程 。并在 Stack Overflow 上搜索许多示例和解释。规范为 JSR 310。
Joda-Time 项目,现已在 维护模式,建议迁移到 java.time 类。
您可以直接与数据库交换java.time对象。使用符合 JDBC 驱动程序 jeps/170" rel="noreferrer">JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* 类。
从哪里获取 java.time 类?
ThreeTen -Extra 项目通过附加类扩展了 java.time。该项目是 java.time 未来可能添加的内容的试验场。您可能会在这里找到一些有用的类,例如
Interval
,YearWeek
,YearQuarter
,以及更多。tl;dr
Use the strict mode on
java.time.DateTimeFormatter
to parse aLocalDate
. Trap for theDateTimeParseException
.After parsing, you might check for reasonable value. For example, a birth date within last one hundred years.
Avoid legacy date-time classes
Avoid using the troublesome old date-time classes shipped with the earliest versions of Java. Now supplanted by the java.time classes.
LocalDate
&DateTimeFormatter
&ResolverStyle
The
LocalDate
class represents a date-only value without time-of-day and without time zone.The
java.time.DateTimeFormatter
class can be set to parse strings with any of three leniency modes defined in theResolverStyle
enum. We insert a line into the above code to try each of the modes.The results:
ResolverStyle.LENIENT
ld: 2000-03-02
ResolverStyle.SMART
ld: 2000-02-29
ResolverStyle.STRICT
ERROR: java.time.format.DateTimeParseException: Text '31/02/2000' could not be parsed: Invalid date 'FEBRUARY 31'
We can see that in
ResolverStyle.LENIENT
mode, the invalid date is moved forward an equivalent number of days. InResolverStyle.SMART
mode (the default), a logical decision is made to keep the date within the month and going with the last possible day of the month, Feb 29 in a leap year, as there is no 31st day in that month. TheResolverStyle.STRICT
mode throws an exception complaining that there is no such date.All three of these are reasonable depending on your business problem and policies. Sounds like in your case you want the strict mode to reject the invalid date rather than adjust it.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as
java.util.Date
,Calendar
, &SimpleDateFormat
.To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for
java.sql.*
classes.Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as
Interval
,YearWeek
,YearQuarter
, and more.您可以使用 SimpleDateFormat
例如喜欢:
You can use SimpleDateFormat
For example something like:
目前的方法是使用日历类。它具有 setLenient 方法将验证日期并抛出异常(如果超出范围,如您的示例所示)。
忘记补充:
如果您获取日历实例并使用日期设置时间,这就是您获取验证的方式。
The current way is to use the calendar class. It has the setLenient method that will validate the date and throw and exception if it is out of range as in your example.
Forgot to add:
If you get a calendar instance and set the time using your date, this is how you get the validation.
java.time
使用 日期和时间 API (java.time 类)内置于 Java 8 及更高版本中,您可以使用
LocalDate
< /a> 类。java.time
With the Date and Time API (java.time classes) built into Java 8 and later, you can use the
LocalDate
class.基于 Aravind 的回答来解决问题ceklock 在他的评论中指出,我添加了一个方法来验证
dateString
不包含任何无效字符。我是这样做的:
Building on Aravind's answer to fix the problem pointed out by ceklock in his comment, I added a method to verify that the
dateString
doesn't contain any invalid character.Here is how I do:
使用标准库的另一种严格解决方案是执行以下操作:
1) 使用您的模式创建严格的 SimpleDateFormat
2) 尝试使用格式对象解析用户输入的值
3) 如果成功,则使用重新格式化 (2) 产生的日期相同的日期格式(来自 (1))
4) 将重新格式化的日期与用户输入的原始值进行比较。如果它们相等,则输入的值与您的模式严格匹配。
这样,您不需要创建复杂的正则表达式 - 在我的例子中,我需要支持所有 SimpleDateFormat 的模式语法,而不是仅限于某些类型,例如天、月和年。
An alternative strict solution using the standard library is to perform the following:
1) Create a strict SimpleDateFormat using your pattern
2) Attempt to parse the user entered value using the format object
3) If successful, reformat the Date resulting from (2) using the same date format (from (1))
4) Compare the reformatted date against the original, user-entered value. If they're equal then the value entered strictly matches your pattern.
This way, you don't need to create complex regular expressions - in my case I needed to support all of SimpleDateFormat's pattern syntax, rather than be limited to certain types like just days, months and years.
我建议您使用 apache 中的 org.apache.commons.validator.GenericValidator 类。
GenericValidator.isDate(String value, String datePattern, boolean strict);
注意:strict - 是否与 datePattern 完全匹配。
I suggest you to use
org.apache.commons.validator.GenericValidator
class from apache.GenericValidator.isDate(String value, String datePattern, boolean strict);
Note: strict - Whether or not to have an exact match of the datePattern.
我认为最简单的方法就是将字符串转换为日期对象,然后将其转换回字符串。如果两个字符串仍然匹配,则给定的日期字符串就可以。
I think the simpliest is just to convert a string into a date object and convert it back to a string. The given date string is fine if both strings still match.
假设这两个都是字符串(否则它们已经是有效的日期),这是一种方法:
这是我得到的输出:
如您所见,它确实很好地处理了这两种情况。
Assuming that both of those are Strings (otherwise they'd already be valid Dates), here's one way:
Here's the output I get:
As you can see, it does handle both of your cases nicely.
这对我来说非常有用。 Ben 上面建议的方法。
This is working great for me. Approach suggested above by Ben.
看起来 SimpleDateFormat 没有严格检查模式,即使在 setLenient(false); 方法应用于它之后,所以我使用了下面的方法验证输入的日期是否为有效日期或不符合提供的模式。
looks like SimpleDateFormat is not checking the pattern strictly even after setLenient(false); method is applied on it, so i have used below method to validate if the date inputted is valid date or not as per supplied pattern.
关于 SimpleDateFormat 使用的两点注释。
IME,这比为每个日期解析实例化一个实例更好。
Two comments on the use of SimpleDateFormat.
IME that is better that instantiating an instance for each parse of a date.
上面的日期解析方法很好,我只是在现有方法中添加了新的检查,使用格式化程序双重检查转换后的日期与原始日期,因此它几乎适用于我验证的每种情况。例如 02/29/2013 是无效日期。
给定函数根据当前可接受的日期格式解析日期。如果日期未成功解析,则返回 true。
Above methods of date parsing are nice , i just added new check in existing methods that double check the converted date with original date using formater, so it works for almost each case as i verified. e.g. 02/29/2013 is invalid date.
Given function parse the date according to current acceptable date formats. It returns true if date is not parsed successfully.
这是我在不使用外部库的情况下对 Node 环境所做的操作:
以下是如何使用它:
Here's what I did for Node environment using no external libraries:
And here is how to use it:
这是我要检查的日期格式:
Here is I would check the date format:
如果您喜欢严格验证,请将 Lenient 设置为 false
setLenient to false if you like a strict validation
使用“传统”日期格式,我们可以格式化结果并将其与源进行比较。
此摘录对 source=01.01.04 表示“false”,模式为“01.01.2004”
With 'legacy' date format, we can format the result and compare it back to the source.
This execerpt says 'false' to source=01.01.04 with pattern '01.01.2004'
我们可以直接使用 org.apache.commons.validator.GenericValidator 的方法,而无需添加整个库:
We can use the
org.apache.commons.validator.GenericValidator
's method directly without adding the whole library:对于 Android 开发者来说是一种简单而优雅的方式(不需要 Java 8):
A simple and elegant way for Android developers (Java 8 not required):
下面的代码适用于 dd/MM/yyyy 格式,也可用于检查 NotNull、NotEmpty。
公共静态布尔validateJavaDate(字符串strDate){
Below code works with dd/MM/yyyy format and can be used to check NotNull,NotEmpty as well.
public static boolean validateJavaDate(String strDate) {
不使用任何外部库的简单方法是:
A simple way without using any external library would be: