验证Java 8日期

Bar*_*rge 9 java java-8

我想验证几种日期格式,如下例所示:

YYYY
YYYY-MM
YYYY-MM-DD
Run Code Online (Sandbox Code Playgroud)

验证必须确保日期格式正确且日期存在.

我知道Java 8提供了一个新的Date API,所以我想知道它是否能够完成这样的工作.

有没有更好的方法使用Java 8 date API?将Calendar类与lenient参数一起使用仍然是一个好习惯吗?

Tag*_*eev 6

您可以指定缺少的字段parseDefaulting以使所有格式化程序工作:

public static boolean isValid(String input) {
    DateTimeFormatter[] formatters = {
            new DateTimeFormatterBuilder().appendPattern("yyyy")
                    .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                    .toFormatter(),
            new DateTimeFormatterBuilder().appendPattern("yyyy-MM")
                    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                    .toFormatter(),
            new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd")
                    .parseStrict().toFormatter() };
    for(DateTimeFormatter formatter : formatters) {
        try {
            LocalDate.parse(input, formatter);
            return true;
        } catch (DateTimeParseException e) {
        }
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)


Jea*_*ard 5

要验证YYYY-MM-DD格式,您可以简单地使用JDK 8中LocalDate.parse引入的java.time

从文本字符串(例如2007-12-03)获取LocalDate的实例。

该字符串必须表示一个有效日期,并使用DateTimeFormatter.ISO_LOCAL_DATE进行解析。

DateTimeParseException如果日期无效,将引发A。

对于您提供给我们的其他两种格式,将引发异常。这是合乎逻辑的,因为它们不是真实日期,而只是日期的一部分。


LocalDate还提供了一种方法,of(int year, int month, int dayOfMonth)因此,如果您确实确实希望仅在某些情况下验证年份,在其他情况下验证带有年份的年份或完整日期,则可以执行以下操作:

public static final boolean validateInputDate(final String isoDate)
{
    String[] dateProperties = isoDate.split("-");

    if(dateProperties != null)
    {
        int year = Integer.parseInt(dateProperties[0]);

        // A valid month by default in the case it is not provided.
        int month = dateProperties.length > 1 ? Integer.parseInt(dateProperties[1]) : 1;

        // A valid day by default in the case it is not provided.
        int day = dateProperties.length > 2 ? Integer.parseInt(dateProperties[2]) : 1;

        try
        {
            LocalDate.of(year, month, day);
            return true;
        }
        catch(DateTimeException e)
        {
            return false;
        }
    }

    return false;
}
Run Code Online (Sandbox Code Playgroud)

请注意,您提到了几种格式,但没有提供它们,因此我认为只有3种。


Oli*_*ire 5

使用可选字段和 parseBest

您只想验证,我理解这一点,但之后您很可能希望以适当的方式提取数据。幸运的是,正如您所写的,Java 8 提供了这样一种方法,parseBest.

parseBest适用于可选字段。因此,首先定义您要解析的格式:yyyy[-MM[-dd]],用括号 ([]) 包装可选字段。

parseBest还需要你提供几个TemporalQuery<R>。实际上,它只是一个模板方法的功能包装器<R> R queryFrom(TemporalAccessor)。所以我们实际上可以TemporalQuery<R>简单地定义 a Year::from。好:这正是我们想要的。问题是它的parseBest名字不是很好:它将按顺序解析所有内容并在TemporalQuery匹配的第一个正确之后停止。所以在你的情况下,我们必须从最精确到不太精确。以下是您要处理的各种类型:LocalDateYearMonthYear。所以让我们定义TemporalQuery[]as LocalDate::from, YearMonth::from, Year::from。现在,如果parseBest不能识别您的输入,它将抛出异常。

总而言之,我们将构建parseBest如下:

parseBest(DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]"), LocalDate::from, YearMonth::from, Year::from);
Run Code Online (Sandbox Code Playgroud)

所以让我们正确地写它:

static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]");

static TemporalAccessor parseDate(String dateAsString) {
  return FORMATTER.parseBest(dateAsString, LocalDate::from, YearMonth::from, Year::from);
}
Run Code Online (Sandbox Code Playgroud)

但是......你只想验证......那么在这种情况下,计算日期并且已经完成了昂贵的工作。所以让我们定义验证如下:

public static boolean isValidDate(String dateAsString) {
  try {
    parseDate(dateAsString);
    return true;
  } catch (DateTimeParseException e) {
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道,使用异常来处理这样的情况是不好的,但是虽然当前的 API 非常强大,但没有考虑到这个非常具体的情况,所以让我们坚持下去并按原样使用它。

但是现在,我们遇到了这个问题:可以解析无效的日期。例如"2018-15"将被解析为Year{2018}因为YearTemporalAccessor解析器可以使用的最好的:YearMonth会失败,但它是 a YearMonth,所以它必须根据那个是有效的。所以让我们在FORMATTER.

public static boolean isValidDate(String dateAsString) {
  try {
    TemporalAccessor date = parseDate(dateAsString);
    return FORMATTER.format(date).equals(dateAsString);
  } catch (DateTimeParseException e) {
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是完整的代码:

import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
class Main {

  private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd]]");

  static TemporalAccessor parseDate(String dateAsString) {
    return FORMATTER.parseBest(dateAsString, LocalDate::from, YearMonth::from, Year::from);
  }

  public static boolean isValidDate(String dateAsString) {
    try {
      TemporalAccessor parsedDate = parseDate(dateAsString);
      return FORMATTER.format(parsedDate).equals(dateAsString);
    } catch (DateTimeParseException e) {
      return false;
    }
  }

  public static void main(String[] args) {   &#32;
    String[] datesAsString = {
      "2018",
      "2018-05",
      "2018-05-22",
      "abc",
      "2018-",
      "2018-15",
    };
    for (String dateAsString: datesAsString) {
      System.out.printf("%s: %s%n", dateAsString, isValidDate(dateAsString) ? "valid" : "invalid");
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在线试试吧!

输出:

2018: valid
2018-05: valid
2018-05-22: valid
abc: invalid
2018-: invalid
2018-15: invalid
Run Code Online (Sandbox Code Playgroud)

您想要的不仅仅是验证,比如获取实际值?

请注意,您仍然可以将检索到的数据parseBest用于进一步的用途,如下所示:

TemporalAccessor dateAccessor = parseDate(dateAsString);
if (dateAccessor instanceof Year) {
  Year year = (Year)dateAccessor;
  // Use year
} else if (dateAccessor instanceof YearMonth) {
  YearMonth yearMonth = (YearMonth)dateAccessor;
  // Use yearMonth
} else if (dateAccessor instanceof LocalDate) {
  LocalDate localDate = (LocalDate)dateAccessor;
  // Use localDate
}
Run Code Online (Sandbox Code Playgroud)

但要小心,因为如前所述,"2018-15"仍然会被解析为 asYear{2018}和 not YearMonth{2018,15},或者"2018-02-30"仍然会被解析为 aYearMonth而不是 a LocalDate