Elegant solution to parse date - java

I want to parse some text into a date. However, there is no guarantee that the text has the desired format. It may be 2012-12-12 or 2012 or even .
Currently, I am down the path to nested try-catch blocks, but that's not a good direction (I suppose).
LocalDate parse;
try {
parse = LocalDate.parse(record, DateTimeFormatter.ofPattern("uuuu/MM/dd"));
} catch (DateTimeParseException e) {
try {
Year year = Year.parse(record);
parse = LocalDate.from(year.atDay(1));
} catch (DateTimeParseException e2) {
try {
// and so on
} catch (DateTimeParseException e3) {}
}
}
What's an elegant solution to this problem? Is it possible to use Optionals which is absent in case a exception happened during evaluation? If yes, how?

This can be done in an elegant fashion using DateTimeFormatter optional sections. An optional section is started by the [ token and is ended by the ] token.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[yyyy[-MM-dd]]");
System.out.println(formatter.parse("2012-12-12")); // prints "{},ISO resolved to 2012-12-12"
System.out.println(formatter.parse("2012")); // prints "{Year=2012},ISO"
System.out.println(formatter.parse("")); // prints "{},ISO"

The DateTimeFormatterBuilder class contains the building blocks to make this work:
LocalDate now = LocalDate.now();
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendPattern("[uuuu[-MM[-dd]]]")
.parseDefaulting(ChronoField.YEAR, now.getYear())
.parseDefaulting(ChronoField.MONTH_OF_YEAR, now.getMonthValue())
.parseDefaulting(ChronoField.DAY_OF_MONTH, now.getDayOfMonth())
.toFormatter();
System.out.println(LocalDate.parse("2015-06-30", fmt));
System.out.println(LocalDate.parse("2015-06", fmt));
System.out.println(LocalDate.parse("2015", fmt));
System.out.println(LocalDate.parse("", fmt));
The parseDefaulting() method allows a default value to be set for a specific field. In all cases, a LocalDate can be parsed from the result, because enough information is available.
Note also the use of "[...]" sections in the pattern to define what is optional.

Related

More beautiful Multiple DateTimeFormatter?

I got multiple string date to convert to OffsetDateTime and I did that with multiple try and catch, I think I will not have other DateTimeFormatter to write. So, how to make that more beautiful ?
code:
public static OffsetDateTime convertStringDateToOffsetDate(String dateStr){
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withLocale( Locale.US );
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f4 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f5 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f6 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX").withZone(ZoneId.of("Europe/Paris"));
OffsetDateTime myDate = null;
try{
myDate = ZonedDateTime.parse(dateStr, f).toOffsetDateTime();
} catch(DateTimeParseException e){
try{
myDate = ZonedDateTime.parse(dateStr, f2).toOffsetDateTime();
} catch (DateTimeParseException ex) {
try{
myDate = ZonedDateTime.parse(dateStr, f3).toOffsetDateTime();
} catch (DateTimeParseException exc) {
try{
myDate = ZonedDateTime.parse(dateStr, f4).toOffsetDateTime();
} catch (DateTimeParseException exce) {
try{
myDate = ZonedDateTime.parse(dateStr, f5).toOffsetDateTime();
} catch(DateTimeParseException excep){
myDate = ZonedDateTime.parse(dateStr, f6).toOffsetDateTime();
}
}
}
}
}
return myDate;
}
public static OffsetDateTime convertStringDateToOffsetDate(String dateStr){
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd['T'][ ][HH:mm:ss][.][SSSSSS][SSSSS][SSSS][SSS][XXX][XX][X]").withZone(ZoneId.of("Europe/Paris"));
return ZonedDateTime.parse(dateStr, f).toOffsetDateTime();
}
This should handle all your patterns. No multiple formatters or regex is needed.
You can declare parts of the format string to be optional, using the [] syntax. This may simply get you to a single pattern that takes care of it all. However, this setup where one pattern has US locale but the others don't, that part is not going to fit in a single format string. So, you can reduce the # of format strings you have, but probably not to a single one.
Then, use a list, and a helper method, to achieve clean code:
private static final List<DateTimeFormatter> FORMATS = List.of(
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withLocale( Locale.US ),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.][SSSSSS][XXX]").withZone(ZoneId.of("Europe/Paris")));
public static OffsetDateTime parse(String dateStr) throws DateTimeParseException {
DateTimeParseException ex = null;
for (var format : FORMATS) try {
return ZonedDateTime.parse(dateStr, format).toOffsetDateTime();
} catch (DateTimeParseException e) {
ex = e;
}
throw ex;
}
Here’s my stab at it.
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart()
.appendOffsetId()
.optionalEnd()
.toFormatter(Locale.ROOT);
private static final ZoneId DEFAULT_ZONE = ZoneId.of("Europe/Paris");
public static OffsetDateTime convertStringDateToOffsetDate(String dateStr) {
TemporalAccessor parsed
= PARSER.parseBest(dateStr, OffsetDateTime::from, LocalDateTime::from);
if (parsed instanceof OffsetDateTime) {
return (OffsetDateTime) parsed;
} else {
return ((LocalDateTime) parsed).atZone(DEFAULT_ZONE).toOffsetDateTime();
}
}
To try it out:
String[] testStrings = {
"2021-01-01T12:34:56.789-07:00",
"2021-02-01T12:34:56",
"2021-03-01T12:34:56-06:00",
"2021-04-01T12:34:56.987654",
"2021-05-01T12:34:56.789",
"2021-06-01T12:34:56.987654-05:00"
};
for (String testString : testStrings) {
System.out.format("%-32s -> %s%n", testString, convertStringDateToOffsetDate(testString));
}
Output:
2021-01-01T12:34:56.789-07:00 -> 2021-01-01T12:34:56.789-07:00
2021-02-01T12:34:56 -> 2021-02-01T12:34:56+01:00
2021-03-01T12:34:56-06:00 -> 2021-03-01T12:34:56-06:00
2021-04-01T12:34:56.987654 -> 2021-04-01T12:34:56.987654+02:00
2021-05-01T12:34:56.789 -> 2021-05-01T12:34:56.789+02:00
2021-06-01T12:34:56.987654-05:00 -> 2021-06-01T12:34:56.987654-05:00
You notice:
It handles all 6 formats from your question.
For the strings that have a UTC offset in them, the offset is retained. For strings that haven’t got one, the correct offset for Paris is assumed (+01:00 in February and +02:00 in April and May).
I believe that it has the following advantages:
I need only one formatter.
I have written no format pattern string at all, just assembled my formatter from built-in parts.
The DateTimeFormatter.parseBest method that I use to parse will try first to create an OffsetDateTime and if unsuccessful, it will resort to creating and returning a LocalDateTime. In the latter case I will need to convert it. The downside of my solution is that I need to go through a TemporalAccessor, which is an interface that I consider low-level and that we usually should not use in application code.
The built-in DateTimeFOrmatter.ISO_LOCAL_DATE_TIME already handles the presence and absence of up to 9 decimals on the seconds. So by reusing this in my formatter I already handle the cases of no decimals and of 3 and 6 decimals.
One challenge of your requirement to use Europe/Paris time zone for the strings that haven’t got an offset in them is, while a DateTimeFormatter can have many default values, it cannot have a default time zone. The withZone method gives us a formatter with an override zone, but this is something else. That formatter will enforce the override zone on the result of either formatting or parsing. While it wasn’t clear from your question I was assuming that you didn’t want this.
Edit: does the formatter need a locale? I used .toFormatter(Locale.ROOT) for building the formatter from the builder. Technically the locale isn’t necessary in this case since my formatter doesn’t include any parts that depend on locale, and in the first version of this answer I had left the locale out (calling the no-arg toFormatter method instead). However I tend to agree with Arvind Kumar Avinash in the comment:
Just small nitpicking: Please always use Locale with a date-time
parsing/formatting type … because it is a Locale-sensitive type. It
may not be relevant for the date-time strings dealt with in this
solution but we should stick to it as if it were a rule.
It was probably just me being arrogant and assuming that the reader was able to determine that there were no locale-sensitive parts in the formatter. Supplying a locale is the better habit (otherwise at least stick in a comment why there isn’t one).
You can use a single try-catch inside a loop, where the exception gets ignored.
List<DateTimeFormatter> list = Arrays.toList<>(f, f1, f2, f3, f4, f5, f6);
for(DateTimeFormatter formatter : list)
{
try
{
myDate = ZonedDateTime.parse(dateStr, formatter).toOffsetDateTime();
break;
}
catch(Exception e)
{
}
}
But keep in mind that exceptions have a bad performance (writing stacktrace to variable takes time), so maybe the comment from M. Dudek to use regex could be the better answer.

Java 8 DateTimeFormatterBuilder().appendOptional not working

My requirement is to validate that a date String is in the correct format based on a set of valid formats specified.
Valid formats:
MM/dd/yy
MM/dd/yyyy
I created a simple test method that uses the Java 8 DateTimeFormatterBuilder to create a flexible formatter that supports multiple optional formats. Here is the code:
public static void test() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
.toFormatter();
String dateString = "10/30/2017";
try {
LocalDate.parse(dateString, formatter);
System.out.println(dateString + " has a valid date format");
} catch (Exception e) {
System.out.println(dateString + " has an invalid date format");
}
}
When I run this, here is the output
10/30/2017 has an invalid date format
As you see in the code, the valid date formats are MM/dd/yy and MM/dd/yyyy.
My expectation was that the date 10/30/2017 should be valid as it matches MM/dd/yyyy. However, 10/30/2017 is being reported as invalid.
What is going wrong ? Why is this not working ?
I also tried
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy[yy]"))
in place of
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
but still had the same issue.
This code runs as expected if I use:
String dateString = "10/30/17";
in place of
String dateString = "10/30/2017";
I have 2 questions
What is going wrong here ? Why is it not working for "10/30/2017" ?
Using Java 8, how to correctly create a flexible Date formatter (a formatter that supports multiple optional formats) ? I know the use of [] to create optional sections in the pattern string itself. I'm looking for something more similar to what I am trying (avoiding [] inside the pattern string and using separate optional clauses for each separate format string)
The formatter does not work the way you expect, the optional part means
if there is nothing extra attached to the first pattern (e.g., "MM/dd/yy"), that is fine,
if there is something extra, it needs to match the second pattern (e.g, "MM/dd/yyyy")
To make it a bit clearer, try to run the sample code below to understand it better:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
.toFormatter();
String[] dateStrings = {
"10/30/17", // valid
"10/30/2017", // invalid
"10/30/1710/30/2017", // valid
"10/30/201710/30/17" // invalid
};
for (String dateString : dateStrings) {
try {
LocalDate.parse(dateString, formatter);
System.out.println(dateString + " has a valid date format");
} catch (Exception e) {
System.err.println(dateString + " has an invalid date format");
}
}
==
10/30/17 has a valid date format
10/30/1710/30/2017 has a valid date format
10/30/2017 has an invalid date format
10/30/201710/30/17 has an invalid date format
==
This is only a simple solution, if performance is of your concern, the validation by catching the parsing exception should be the last resort
you may check the string by length or regex first before doing the date string parsing
you may also replace the stream with a method containing a simple for loop, etc.
String[] patterns = { "MM/dd/yy", "MM/dd/yyyy" };
Map<String, DateTimeFormatter> formatters = Stream.of(patterns).collect(Collectors.toMap(
pattern -> pattern,
pattern -> new DateTimeFormatterBuilder().appendOptional(DateTimeFormatter.ofPattern(pattern)).toFormatter()
));
String dateString = "10/30/17";
boolean valid = formatters.entrySet().stream().anyMatch(entry -> {
// relying on catching parsing exception will have serious expense on performance
// a simple check will already improve a lot
if (dateString.length() == entry.getKey().length()) {
try {
LocalDate.parse(dateString, entry.getValue());
return true;
}
catch (DateTimeParseException e) {
// ignore or log it
}
}
return false;
});
The builder's appendValueReduced() method was designed to handle this case.
When parsing a complete value for a field, the formatter will treat it as an absolute value.
When parsing an partial value for a field, the formatter will interpret it relative to a base that you specify. For example, if you want two-digit years to be interpreted as being between 1970 and 2069, you can specify 1970 as your base. Here's an illustration:
LocalDate century = LocalDate.ofEpochDay(0); /* Beginning Jan. 1, 1970 */
DateTimeFormatter f = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("MM/dd/"))
.appendValueReduced(ChronoField.YEAR, 2, 4, century)
.toFormatter();
System.out.println(LocalDate.parse("10/30/2017", f)); /* 2017-10-30 */
System.out.println(LocalDate.parse("10/30/17", f)); /* 2017-10-30 */
System.out.println(LocalDate.parse("12/28/1969", f)); /* 1969-12-28 */
System.out.println(LocalDate.parse("12/28/69", f)); /* 2069-12-28 */

DateTimeFormatter Accepting Multiple Dates and Converting to One (java.time library)

I am trying to write a DateTimeFormatter that will allow me to take in multiple different String formats, and then convert the String formats to a specific type. Due to the scope of the project and the code that already exists, I cannot use a different type of formatter.
E.g., I want to accept MM/dd/yyyy as well as yyyy-MM-dd'T'HH:mm:ss but then when I print I only want to print to MM/dd/yyyy format and have it in the format when I call LocalDate.format(formatter);
Could someone suggest ideas on how to do this with the java.time.format.*;
Here is how I could do it in org.joda:
// MM/dd/yyyy format
DateTimeFormatter monthDayYear = DateTimeFormat.forPattern("MM/dd/yyyy");
// array of parsers, with all possible input patterns
DateTimeParser[] parsers = {
// parser for MM/dd/yyyy format
monthDayYear.getParser(),
// parser for yyyy-MM-dd'T'HH:mm:ss format
DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").getParser()
};
DateTimeFormatter parser = new DateTimeFormatterBuilder()
// use the monthDayYear formatter for output (monthDayYear.getPrinter())
// and parsers array for input (parsers)
.append(monthDayYear.getPrinter(), parsers)
// create formatter (using UTC to avoid DST problems)
.toFormatter()
.withZone(DateTimeZone.UTC);
I have not found a good/working example of this online.
I've tested this with JDK 1.8.0_131 for Mac OS X and JDK 1.8.0111 for Windows (both worked).
I've created a DateTimeFormatter with optional sections (delimited by []), to parse both cases (MM/dd/yyyy and yyyy-MM-dd'T'HH:mm:ss).
The same formatter worked for your case (LocalDate), but there are some considerations below.
// parse both formats (use optional section, delimited by [])
DateTimeFormatter parser = DateTimeFormatter.ofPattern("[MM/dd/yyyy][yyyy-MM-dd'T'HH:mm:ss]");
// parse MM/dd/yyyy
LocalDate d1 = LocalDate.parse("10/16/2016", parser);
// parse yyyy-MM-dd'T'HH:mm:ss
LocalDate d2 = LocalDate.parse("2016-10-16T10:20:30", parser);
// parser.format(d1) is the same as d1.format(parser)
System.out.println(parser.format(d1));
System.out.println(parser.format(d2));
The output is:
10/16/2016
10/16/2016
PS: this works only with LocalDate. If I try to format an object with time fields (like LocalDateTime), both formats are used:
System.out.println(parser.format(LocalDateTime.now()));
This prints:
06/18/20172017-06-18T07:40:55
Note that it formatted with both patterns. My guess is that the formatter checks if the object has the fields in each optional section. As the LocalDate has no time fields (hour/minute/second), the second pattern fails and it prints only the first one (MM/dd/yyyy). But the LocalDateTime has all the time fields, and both patterns are valid, so both are used to format.
My conclusion is: this isn't a general solution (like the Joda-Time's version), it's more like a "lucky" case where the patterns involved created the desired situation. But I wouldn't rely on that for all cases.
Anyway, if you are only using LocalDate, you can try to use this code. But if you're working with another types, then you'll probably have to use another formatter for the output, like this:
// parser/formatter for month/day/year
DateTimeFormatter mdy = DateTimeFormatter.ofPattern("MM/dd/yyyy");
// parser for both patterns
DateTimeFormatter parser = new DateTimeFormatterBuilder()
// optional MM/dd/yyyy
.appendOptional(mdy)
// optional yyyy-MM-dd'T'HH:mm:ss (use built-in formatter)
.appendOptional(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// create formatter
.toFormatter();
// parse MM/dd/yyyy
LocalDate d1 = LocalDate.parse("10/16/2016", parser);
// parse yyyy-MM-dd'T'HH:mm:ss
LocalDate d2 = LocalDate.parse("2016-10-16T10:20:30", parser);
// use mdy to format
System.out.println(mdy.format(d1));
System.out.println(mdy.format(d2));
// format object with time fields: using mdy formatter to avoid multiple pattern problem
System.out.println(mdy.format(LocalDateTime.now()));
The output is:
10/16/2016
10/16/2016
06/18/2017
The parsing part can be written, and has been added in the ThreeTen-Extra library. The relevant code is here and is included below for clarity. The key trick is using parseUnresolved() to find out which format is correct:
public static <T> T parseFirstMatching(CharSequence text, TemporalQuery<T> query, DateTimeFormatter... formatters) {
Objects.requireNonNull(text, "text");
Objects.requireNonNull(query, "query");
Objects.requireNonNull(formatters, "formatters");
if (formatters.length == 0) {
throw new DateTimeParseException("No formatters specified", text, 0);
}
if (formatters.length == 1) {
return formatters[0].parse(text, query);
}
for (DateTimeFormatter formatter : formatters) {
try {
ParsePosition pp = new ParsePosition(0);
formatter.parseUnresolved(text, pp);
int len = text.length();
if (pp.getErrorIndex() == -1 && pp.getIndex() == len) {
return formatter.parse(text, query);
}
} catch (RuntimeException ex) {
// should not happen, but ignore if it does
}
}
throw new DateTimeParseException("Text '" + text + "' could not be parsed", text, 0);
}
Unfortunately, there is no way to write a single DateTimeFormatter that supports flexible parsing and prints using a specific output format as per Joda-Time.
What you're asking is not possible.
DateTimeFormatter is a final class, so you cannot subclass it to implement your own behavior.
The constructor is package-private, so you can't call it yourself. The only way to create a DateTimeFormatter is by using a DateTimeFormatterBuilder. Note that the static helper methods for creating a DateTimeFormatter are internally using DateTimeFormatterBuilder, e.g.
public static DateTimeFormatter ofPattern(String pattern) {
return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter();
}
DateTimeFormatterBuilder is also a final class, and cannot be subclassed, and it doesn't provide any methods for supplying multiple alternate formats to use, like you want.
In short, DateTimeFormatter is closed and cannot be extended. If your code can only use a DateTimeFormatter, then you are out of luck.
The Answer by Andreas is correct and should be accepted.
Check length of string
As an alternative, you can simply test the length of your string and apply one of two formatters.
DateTimeFormatter fDateOnly = DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) ;
DateTimeFormatter fDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME ;
LocalDate ld = null ;
if( input.length() == 10 ) {
try {
ld = LocalDate.parse( input , fDateOnly ) ;
} catch (DateTimeParseException e ) {
…
}
} else if ( input.length() == 19 ) {
try {
LocalDateTime ldt = LocalDateTime.parse( input , fDateTime ) ;
ld = ldt.toLocalDate() ;
} catch (DateTimeParseException e ) {
…
}
} else {
// Received unexpected input.
…
}
String output = ld.format( fDateOnly ) ;
Be aware that you can let java.time automatically localize when generating a string representing the value of your date-time rather than hard-code a specific format. See DateTimeFormatter.ofLocalizedDate.

How to validate date(dd-mm-yyyy) without using regular expression in java? [duplicate]

I am using NetBeans IDE 7.2.
I have two separate classes newDateTest.java and newDateMethod.java, I am currently using my method class to validate a date from a user input which I have used in my test class.
So far in my test class I have the following:
try
{
Prompt ="please enter a date in the format dd-mm-yyyy";
System.out.println(Prompt);
String inputDate = in.next();
isValid = newDateMethod.validDate(input, input, input);
if (isValid){
System.out.println("VALID DATE");
} else {
System.out.println("INVALID DATE");
}
} catch (ArrayIndexOutOfBoundsException oob) {
System.out.println(oob);
}
However I have no idea how to validate the date in my method class as I am fairly new to Java.
Can anyone come to a solution? The sort of thing I've been taught to use is Date Formatter but I'm not sure whether this is appropriate here? If so, I wouldn't know how to use it
Like this:
Date date = null;
String inputDate = "07-01-2013";
try {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
formatter.setLenient(false);
date = formatter.parse(inputDate);
} catch (ParseException e) {
e.printStackTrace();
}
Updated on 13-Jul-2021:
I heartily agree with Ole V.V.'s comment below. All Java and Kotlin developers should prefer the java.time package.
I'll add a more modern example when time permits.
Have a look at SimpleDateFormat.parse(...) and do remember to surround with try-catch.
The standard JDK class for that is SimpleDateFormat:
DateFormat fmt = new SimpleDateFormat("yourformathere");
// use fmt.parse() to check for validity
Alternatively, I'd recommend using Joda Time's DateTimeFormat.
Rather than relying on exceptions which tend to have a small performance overhead, you can also use the DateValidator from the Apache commons routines package like this:
if (DateValidator.getInstance().validate(inputDate, "dd-MM-yyyy") != null) {
// Date is valid
}
else {
// Date is invalid
}
Small disclaimer though, I haven't looked at the implementation of the validate method and I'm not sure if it uses for instance the SimpleDateFormat...
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work. It also gives you much preciser validation than the old SimpleDateFormat class used in some of the other answers.
String[] exampleInputStrings = { "07-01-2013", "07-01-017",
"07-01-2ooo", "32-01-2017", "7-1-2013", "07-01-2013 blabla" };
for (String inputDate : exampleInputStrings) {
try {
LocalDate date = LocalDate.parse(inputDate, DATE_FORMATTER);
System.out.println(inputDate + ": valid date: " + date );
} catch (DateTimeParseException dtpe) {
System.out.println(inputDate + ": invalid date: " + dtpe.getMessage());
}
}
Output from my example code is:
07-01-2013: valid date: 2013-01-07
07-01-017: invalid date: Text '07-01-017' could not be parsed at index 6
07-01-2ooo: invalid date: Text '07-01-2ooo' could not be parsed at index 6
32-01-2017: invalid date: Text '32-01-2017' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
7-1-2013: invalid date: Text '7-1-2013' could not be parsed at index 0
07-01-2013 blabla: invalid date: Text '07-01-2013 blabla' could not be parsed, unparsed text found at index 10
For a good validation you should probably add a range check. Use the isBefore and/or the isAfter method of LocalDate.
Also if you are going to do anything with the date more than validating it, you should keep the LocalDate from the parsing around in your program (not the string).
Link
Oracle tutorial: Date Time explaining how to use java.time.
You should use SimpleDateFormat.parse(String) method. if the passed date is of wrong format it throws an exception in which case you return false.
public boolean validateDate(String date) {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
try {
sdf.parse(date);
return true;
}
catch(ParseException ex) {
return false;
}
}
One can use joda-time.
DateTimeFormat.forPattern(INPUTED_DATE_FORMAT);
//one can also use it with locale
DateTimeFormat.forPattern(USER_DATE_FORMAT).withLocale(locale);
fmt.parseDateTime(INPUTED_DATE);
If parseDateTime throw IllegalArgumentException then date is not valid.

Validating a date in Java

I am using NetBeans IDE 7.2.
I have two separate classes newDateTest.java and newDateMethod.java, I am currently using my method class to validate a date from a user input which I have used in my test class.
So far in my test class I have the following:
try
{
Prompt ="please enter a date in the format dd-mm-yyyy";
System.out.println(Prompt);
String inputDate = in.next();
isValid = newDateMethod.validDate(input, input, input);
if (isValid){
System.out.println("VALID DATE");
} else {
System.out.println("INVALID DATE");
}
} catch (ArrayIndexOutOfBoundsException oob) {
System.out.println(oob);
}
However I have no idea how to validate the date in my method class as I am fairly new to Java.
Can anyone come to a solution? The sort of thing I've been taught to use is Date Formatter but I'm not sure whether this is appropriate here? If so, I wouldn't know how to use it
Like this:
Date date = null;
String inputDate = "07-01-2013";
try {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
formatter.setLenient(false);
date = formatter.parse(inputDate);
} catch (ParseException e) {
e.printStackTrace();
}
Updated on 13-Jul-2021:
I heartily agree with Ole V.V.'s comment below. All Java and Kotlin developers should prefer the java.time package.
I'll add a more modern example when time permits.
Have a look at SimpleDateFormat.parse(...) and do remember to surround with try-catch.
The standard JDK class for that is SimpleDateFormat:
DateFormat fmt = new SimpleDateFormat("yourformathere");
// use fmt.parse() to check for validity
Alternatively, I'd recommend using Joda Time's DateTimeFormat.
Rather than relying on exceptions which tend to have a small performance overhead, you can also use the DateValidator from the Apache commons routines package like this:
if (DateValidator.getInstance().validate(inputDate, "dd-MM-yyyy") != null) {
// Date is valid
}
else {
// Date is invalid
}
Small disclaimer though, I haven't looked at the implementation of the validate method and I'm not sure if it uses for instance the SimpleDateFormat...
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work. It also gives you much preciser validation than the old SimpleDateFormat class used in some of the other answers.
String[] exampleInputStrings = { "07-01-2013", "07-01-017",
"07-01-2ooo", "32-01-2017", "7-1-2013", "07-01-2013 blabla" };
for (String inputDate : exampleInputStrings) {
try {
LocalDate date = LocalDate.parse(inputDate, DATE_FORMATTER);
System.out.println(inputDate + ": valid date: " + date );
} catch (DateTimeParseException dtpe) {
System.out.println(inputDate + ": invalid date: " + dtpe.getMessage());
}
}
Output from my example code is:
07-01-2013: valid date: 2013-01-07
07-01-017: invalid date: Text '07-01-017' could not be parsed at index 6
07-01-2ooo: invalid date: Text '07-01-2ooo' could not be parsed at index 6
32-01-2017: invalid date: Text '32-01-2017' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
7-1-2013: invalid date: Text '7-1-2013' could not be parsed at index 0
07-01-2013 blabla: invalid date: Text '07-01-2013 blabla' could not be parsed, unparsed text found at index 10
For a good validation you should probably add a range check. Use the isBefore and/or the isAfter method of LocalDate.
Also if you are going to do anything with the date more than validating it, you should keep the LocalDate from the parsing around in your program (not the string).
Link
Oracle tutorial: Date Time explaining how to use java.time.
You should use SimpleDateFormat.parse(String) method. if the passed date is of wrong format it throws an exception in which case you return false.
public boolean validateDate(String date) {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
try {
sdf.parse(date);
return true;
}
catch(ParseException ex) {
return false;
}
}
One can use joda-time.
DateTimeFormat.forPattern(INPUTED_DATE_FORMAT);
//one can also use it with locale
DateTimeFormat.forPattern(USER_DATE_FORMAT).withLocale(locale);
fmt.parseDateTime(INPUTED_DATE);
If parseDateTime throw IllegalArgumentException then date is not valid.

Categories

Resources