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