Java 8 date validation

I would like to confirm several date formats as shown below:

YYYY YYYY-MM YYYY-MM-DD 

Validation must ensure that the date format and date are correct.

I know that Java 8 provides a new date API, so I'm wondering if it can do such a job.

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

+9
java java-8
source share
6 answers

You can specify missing fields with parseDefaulting to make all formatting elements work:

 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; } 
+6
source share

To check the format of YYYY-MM-DD , you can simply use LocalDate.parse introduced in java.time with JDK 8.

Retrieves 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.

A DateTimeParseException will be thrown if the date is invalid.

For the other two formats you gave us, an exception will be thrown. This is logical because they are not a real date, just part of the date.


LocalDate also provides a method of(int year, int month, int dayOfMonth) , so if you really want to just check the year in some case, the year with month in another case, or with the full date, then you can do something like of this:

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

Please note that you specified several formats but did not provide them, so I assumed that these were only 3.

+4
source share

Use optional fields and parseBest

You just want to check, I understand this, but later on you will most likely want to extract the data in a suitable way. Fortunately, and as you already wrote, Java 8 has such a method, parseBest .

parseBest works with optional fields. Therefore, first determine the format you want to yyyy[-MM[-dd]] : yyyy[-MM[-dd]] , with brackets ( [ and ] ) enclosing optional fields.

parseBest also requires that you provide several TemporalQuery<R> . This is actually just a functional wrapper around the <R> R queryFrom(TemporalAccessor) template method. Thus, we can define TemporalQuery<R> simply as Year::from . Good: this is exactly what we want. The fact is that parseBest not quite correctly named: it will parse everything in order and stop after the first corresponding TemporalQuery that matches. So in your case, we must move from the most accurate to the less accurate. Here are the various types you want to handle: LocalDate , YearMonth and Year . So, let's just define TemporalQuery[] as LocalDate::from, YearMonth::from, Year::from . Now, if parseBest does not recognize your input, it will throw an exception.

In general, we parseBest as follows:

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

So let's write it right:

 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 just want to check ... Well, in this case the date is calculated, and the expensive work has already been done. So, let's just define validation as follows:

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

I know that it is wrong to use exceptions to handle such cases, but although the current API is very powerful, this very specific case has not been taken into account, so let's just stick to it and use it as is.

Here is the complete 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!

Exit:

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

Do you want more than checking, for example getting the actual value?

Note that you can still use data retrieved from parseBest for future reference, like this:

 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 } 
+3
source share
 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; } } 
0
source share

You can use lenient, by default this is true:

in SimpleDAteFormat

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

or in JSON, confirm:

 @JsonFormat(lenient = OptBoolean.FALSE) 
0
source share

Please use the code below. It will check, and also works in any 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(); } } } 
0
source share

All Articles