如何在 Java 中检查日期的完整性

发布于 2024-10-08 22:48:08 字数 141 浏览 2 评论 0 原文

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

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

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

I find it curious that the most obvious way to create Date objects in Java has been deprecated and appears to have been "substituted" with a not so obvious to use lenient calendar.

How do you check that a date, given as a combination of day, month, and year, is a valid date?

For instance, 2008-02-31 (as in yyyy-mm-dd) would be an invalid date.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

喵星人汪星人 2024-10-15 22:48:08

关键是 df.setLenient(false);。这对于简单的情况来说已经足够了。如果您正在寻找更强大的(我对此表示怀疑)和/或替代库,例如 joda-time< /a>,然后查看用户“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;
        }
}

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"

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;
        }
}
ゃ懵逼小萝莉 2024-10-15 22:48:08

如 @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 - Better Alternative?

我最近听说过 joda 时间,我想比较一下。两点:

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

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.

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

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:

Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();

Joda Time - Better Alternative?

I've been hearing about joda time recently and thought I'd compare. Two points:

  1. Seems better at being strict about invalid characters in the date string, unlike SimpleDateFormat
  2. Can't see a way to enforce 4-digit years with it yet (but I guess you could create your own DateTimeFormatter for this purpose)

It's quite simple to use:

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;
}
虫児飞 2024-10-15 22:48:08

tl;dr

使用严格模式java.time.DateTimeFormatter 解析 本地日期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< /code>
    错误:java.time.format.DateTimeParseException:无法解析文本“31/02/2000”:无效日期“FEBRUARY 31”

我们可以在 ResolverStyle.LENIENT 模式,无效日期向前推进了同等天数。在 ResolverStyle.SMART 模式(默认),会做出一个逻辑决定,将日期保留在月份内,并使用该月的最后一天,即闰年的 2 月 29 日,因为该日期没有第 31 天月。 ResolverStyle.STRICT 模式抛出异常,抱怨没有这样的日期。

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


全部表格Java 中的日期时间类型,包括现代的和传统的。


关于 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 类?

其中的表格与 Java 或 Android 版本配合使用的 java.time 库

ThreeTen -Extra 项目通过附加类扩展了 java.time。该项目是 java.time 未来可能添加的内容的试验场。您可能会在这里找到一些有用的类,例如 Interval YearWeek YearQuarter ,以及更多

tl;dr

Use the strict mode on java.time.DateTimeFormatter to parse a LocalDate. Trap for the 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.
)

After parsing, you might check for reasonable value. For example, a birth date within last one hundred years.

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

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.

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

The java.time.DateTimeFormatter class can be set to parse strings with any of three leniency modes defined in the ResolverStyle enum. We insert a line into the above code to try each of the modes.

f = f.withResolverStyle ( ResolverStyle.LENIENT );

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. In ResolverStyle.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. The ResolverStyle.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.


Table of all date-time types in Java, both modern and legacy.


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?

Table of which java.time library to use with which version of Java or Android

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.

夏雨凉 2024-10-15 22:48:08

您可以使用 SimpleDateFormat

例如喜欢:

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

You can use SimpleDateFormat

For example something like:

boolean isLegalDate(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setLenient(false);
    return sdf.parse(s, new ParsePosition(0)) != null;
}
橘虞初梦 2024-10-15 22:48:08

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

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

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

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.

Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
    cal.getTime();
}
catch (Exception e) {
  System.out.println("Invalid date");
}
溺ぐ爱和你が 2024-10-15 22:48:08

java.time

使用 日期和时间 API (java.time 类)内置于 Java 8 及更高版本中,您可以使用 LocalDate< /a> 类。

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

java.time

With the Date and Time API (java.time classes) built into Java 8 and later, you can use the LocalDate class.

public static boolean isDateValid(int year, int month, int day) {
    try {
        LocalDate.of(year, month, day);
    } catch (DateTimeException e) {
        return false;
    }
    return true;
}
め七分饶幸 2024-10-15 22:48:08

基于 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+");
}

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:

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+");
}
山川志 2024-10-15 22:48:08

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

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.

奢欲 2024-10-15 22:48:08

我建议您使用 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.

永言不败 2024-10-15 22:48:08

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

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

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.

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;
}
不顾 2024-10-15 22:48:08

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

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

如您所见,它确实很好地处理了这两种情况。

Assuming that both of those are Strings (otherwise they'd already be valid Dates), here's one way:

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

Here's the output I get:

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

As you can see, it does handle both of your cases nicely.

聆听风音 2024-10-15 22:48:08

这对我来说非常有用。 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;
    }
}

This is working great for me. Approach suggested above by 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;
    }
}
伴梦长久 2024-10-15 22:48:08

看起来 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;
}

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.

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;
}
你的呼吸 2024-10-15 22:48:08

关于 SimpleDateFormat 使用的两点注释。

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

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

Two comments on the use of SimpleDateFormat.

it should be declared as a static instance
if declared as static access should be synchronized as it is not thread safe

IME that is better that instantiating an instance for each parse of a date.

故事↓在人 2024-10-15 22:48:08

上面的日期解析方法很好,我只是在现有方法中添加了新的检查,使用格式化程序双重检查转换后的日期与原始日期,因此它几乎适用于我验证的每种情况。例如 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;
    }

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.

 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;
    }
走走停停 2024-10-15 22:48:08

这是我在不使用外部库的情况下对 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

Here's what I did for Node environment using no external libraries:

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

And here is how to use it:

isDateCorrect('2014-02-23')
true
森林散布 2024-10-15 22:48:08
// 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;
}
// 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;
}
‘画卷フ 2024-10-15 22:48:08

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

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

Here is I would check the date format:

 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");
}
携君以终年 2024-10-15 22:48:08
        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 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 "";
        }

    }
寂寞陪衬 2024-10-15 22:48:08

如果您喜欢严格验证,请将 Lenient 设置为 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;
}

setLenient to false if you like a strict validation

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;
}
橘和柠 2024-10-15 22:48:08

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

    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 表示“false”,模式为“01.01.2004”

With 'legacy' date format, we can format the result and compare it back to the source.

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

This execerpt says 'false' to source=01.01.04 with pattern '01.01.2004'

小女人ら 2024-10-15 22:48:08

我们可以直接使用 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;
}

We can use the org.apache.commons.validator.GenericValidator's method directly without adding the whole library:

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;
}
欢你一世 2024-10-15 22:48:08

对于 Android 开发者来说是一种简单而优雅的方式(不需要 Java 8):

// month value is 1-based. e.g., 1 for January.
public static boolean isDateValid(int year, int month, int day) {
    Calendar calendar = Calendar.getInstance();
    try {
        calendar.setLenient(false);
        calendar.set(year, month-1, day);
        calendar.getTime();
        return true;
    } catch (Exception e) {
        return false;
    }
}

A simple and elegant way for Android developers (Java 8 not required):

// month value is 1-based. e.g., 1 for January.
public static boolean isDateValid(int year, int month, int day) {
    Calendar calendar = Calendar.getInstance();
    try {
        calendar.setLenient(false);
        calendar.set(year, month-1, day);
        calendar.getTime();
        return true;
    } catch (Exception e) {
        return false;
    }
}
┈┾☆殇 2024-10-15 22:48:08

下面的代码适用于 dd/MM/yyyy 格式,也可用于检查 NotNull、NotEmpty。

公共静态布尔validateJavaDate(字符串strDate){

    if (strDate != null && !strDate.isEmpty() && !strDate.equalsIgnoreCase(" ")) {
        {

            SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
            date.setLenient(false);

            try {
                Date javaDate = date.parse(strDate);
                System.out.println(strDate + " Valid Date format");
            }

            catch (ParseException e) {
                System.out.println(strDate + " Invalid Date format");
                return false;
            }
            return true;
        }

    } else {
        System.out.println(strDate + "----> Date is Null/Empty");
        return false;
    }

}

Below code works with dd/MM/yyyy format and can be used to check NotNull,NotEmpty as well.

public static boolean validateJavaDate(String strDate) {

    if (strDate != null && !strDate.isEmpty() && !strDate.equalsIgnoreCase(" ")) {
        {

            SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
            date.setLenient(false);

            try {
                Date javaDate = date.parse(strDate);
                System.out.println(strDate + " Valid Date format");
            }

            catch (ParseException e) {
                System.out.println(strDate + " Invalid Date format");
                return false;
            }
            return true;
        }

    } else {
        System.out.println(strDate + "----> Date is Null/Empty");
        return false;
    }

}
森林散布 2024-10-15 22:48:08

不使用任何外部库的简单方法是:

private static final Pattern p_DATE = Pattern.compile("^\\s*(\\d+)-(\\d+)-(\\d+)\\s*$");

private static Date parseDate(String v) {
    
    if ( v==null || v.isBlank() ) {
        return null;
    }
    
    // 2023-03-02
    Matcher m = p_DATE.matcher(v);
    
    if ( ! m.find() ) {
        return null;
    }
    
    int day = Integer.parseInt(m.group(3), 10);
    int mon = Integer.parseInt(m.group(2), 10);
    int year = Integer.parseInt(m.group(1), 10);
    
    // convert years prior to 99
    if ( year<99 ) {
        year += 2000;
    }
    
    if ( 
            (day<0 || day>31) ||
            (mon<1 || mon>12) 
    ) {
        return null;
    }
    
    // validate max day
    int maxDayOfTheMonth = getMaxDayOfTheMonth(mon, year);

    if ( day > maxDayOfTheMonth ) {
        return null;
    }
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, day);
    cal.set(Calendar.MONTH, mon-1);
    cal.set(Calendar.YEAR, year);
    
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    
    return cal.getTime();
}

private static int getMaxDayOfTheMonth(int mon, int year) {
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.MONTH, mon-1);
    cal.set(Calendar.YEAR, year);
    
    cal.add(Calendar.MONTH, 1);
    cal.add(Calendar.DAY_OF_YEAR, -1);
    
    return cal.get(Calendar.DAY_OF_MONTH);
}

A simple way without using any external library would be:

private static final Pattern p_DATE = Pattern.compile("^\\s*(\\d+)-(\\d+)-(\\d+)\\s*
quot;);

private static Date parseDate(String v) {
    
    if ( v==null || v.isBlank() ) {
        return null;
    }
    
    // 2023-03-02
    Matcher m = p_DATE.matcher(v);
    
    if ( ! m.find() ) {
        return null;
    }
    
    int day = Integer.parseInt(m.group(3), 10);
    int mon = Integer.parseInt(m.group(2), 10);
    int year = Integer.parseInt(m.group(1), 10);
    
    // convert years prior to 99
    if ( year<99 ) {
        year += 2000;
    }
    
    if ( 
            (day<0 || day>31) ||
            (mon<1 || mon>12) 
    ) {
        return null;
    }
    
    // validate max day
    int maxDayOfTheMonth = getMaxDayOfTheMonth(mon, year);

    if ( day > maxDayOfTheMonth ) {
        return null;
    }
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, day);
    cal.set(Calendar.MONTH, mon-1);
    cal.set(Calendar.YEAR, year);
    
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    
    return cal.getTime();
}

private static int getMaxDayOfTheMonth(int mon, int year) {
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.MONTH, mon-1);
    cal.set(Calendar.YEAR, year);
    
    cal.add(Calendar.MONTH, 1);
    cal.add(Calendar.DAY_OF_YEAR, -1);
    
    return cal.get(Calendar.DAY_OF_MONTH);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文