验证 Java 8 日期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32204953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 19:49:43  来源:igfitidea点击:

Validating Java 8 dates

javajava-8

提问by Barium Scoorge

I'd like to validate several date formats, as below examples :

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

YYYY
YYYY-MM
YYYY-MM-DD

Validation must ensure that date format is correct and the date exists.

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

I'm aware that Java 8 provides a new Date API, so I'm wondering if it's able to do such job.

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

Is there a better way using Java 8 date API ? Is it still a good practice to use Calendar class with lenient parameter ?

有没有更好的方法使用 Java 8 date API?使用带有宽松参数的 Calendar 类仍然是一个好习惯吗?

回答by Tagir Valeev

You can specify missing fields with parseDefaultingto make all the formatters working:

您可以指定缺少的字段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;
}

回答by Jean-Fran?ois Savard

To validate the YYYY-MM-DDformat, you can simply use LocalDate.parseintroduced in java.timesince JDK 8.

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

Obtains an instance of LocalDate from a text string such as 2007-12-03.

The string must represent a valid date and is parsed using DateTimeFormatter.ISO_LOCAL_DATE.

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

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

A DateTimeParseExceptionwill be thrown if the date is invalid.

DateTimeParseException如果日期无效,将抛出A。

For the other two formats you gave us, the exception would be thrown. That is logical because they are not real date, simply part of a date.

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



LocalDate also provide a method of(int year, int month, int dayOfMonth)thus if you really want to validate simply the year in some case, the year with the month in other case or the full date then you could do something like this :

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

Note that you mentionned several formatsbut did not provide them, so I assumed these were the only 3.

请注意,您提到了几种格式但没有提供它们,所以我认为这些是仅有的 3 种。

回答by Olivier Grégtheitroade

Use optional fields and parseBest

使用可选字段和 parseBest

You only want to validate, I understand that, but afterwards you'll most likely want to extract the data in an appopriate way. Fortunately, and indeed as you wrote, Java 8 provides such a method, parseBest.

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

parseBestworks with optional fields. So define the format you want to parse first: yyyy[-MM[-dd]], with the brackets ([and ]) wrapping the optional fields.

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

parseBestalso requires you to provide several TemporalQuery<R>. Actually, it's just a functional wrapper around a template method <R> R queryFrom(TemporalAccessor). So we can actually define a TemporalQuery<R>as simply as Year::from. Good: that's exactly what we want. The thing is that parseBestis not really well named: it will parse all in order and stop after the first proper TemporalQuerythat matches. So in your case, we have to go from most precise to less precise. Here are the various types that you want to handle: LocalDate, YearMonthand Year. So let's just define the TemporalQuery[]as LocalDate::from, YearMonth::from, Year::from. Now, if parseBestdoesn't recognize your input, it will throw an exception.

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

All in all, we'll construct the parseBestas follow:

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

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

So let's write it properly:

所以让我们正确地写它:

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

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

But... you only want to validate... Well in that case, a date is computed and the expensive job is already done. So let's just define the validation as follow:

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

public static boolean isValidDate(String dateAsString) {
  try {
    parseDate(dateAsString);
    return true;
  } catch (DateTimeParseException e) {
    return false;
  }
}

I know, it's bad to use exceptions to handle cases like this, but while the current API is very powerful this very specific case wasn't taken in account so let's just stick to it and use it as is.

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

Here's the full code:

这是完整的代码:

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 {
      parseDate(dateAsString);
      return true;
    } catch (DateTimeParseException e) {
      return false;
    }
  }

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

Try it online!

在线试试吧!

Output:

输出:

2018: valid
2018-05: valid
2018-05-22: valid
abc: invalid
2018-: invalid

You want more than validating, like getting the actual value?

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

Note that you can still use the data retrieved from parseBestfor further uses like this:

请注意,您仍然可以将检索到的数据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
}

回答by Sakai

You can use lenient, is your default is true:

您可以使用 lenient,您的默认值为 true:

in SimpleDAteFormat

在 SimpleDateFormat 中

SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);

or in JSON validate:

或在 JSON 中验证:

@JsonFormat(lenient = OptBoolean.FALSE)

回答by rahulnikhare

Please use the below code. This will validate and also works in every format.

请使用以下代码。这将验证并适用于各种格式。

public class DateValidator {
    public static void main(String[] args) {

        String datetoCheck = "999999";

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
        try {
            LocalDate localDate = LocalDate.parse(datetoCheck, formatter);
            System.out.println(localDate);
        } catch ( DateTimeException ex ) {
            ex.printStackTrace();
        }
    }
}

回答by makson

public static final boolean validateInputDate(final String isoDate, final String dateFormat){
    final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
    try {
        final Date date = simpleDateFormat.parse(isoDate);
        System.out.println("Date: " + date);
        return true;
    } catch (ParseException e) {
        e.printStackTrace();
        return false;
    }
}