How to Parse Dates in Multiple Formats Using Simpledateformat

How to parse dates in multiple formats using SimpleDateFormat

You'll need to use a different SimpleDateFormat object for each different pattern. That said, you don't need that many different ones, thanks to this:

Number: For formatting, the number of pattern letters is the minimum number of digits, and shorter numbers are zero-padded to this amount. For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent fields.

So, you'll need these formats:

  • "M/y" (that covers 9/09, 9/2009, and 09/2009)
  • "M/d/y" (that covers 9/1/2009)
  • "M-d-y" (that covers 9-1-2009)

So, my advice would be to write a method that works something like this (untested):

// ...
List<String> formatStrings = Arrays.asList("M/y", "M/d/y", "M-d-y");
// ...

Date tryParse(String dateString)
{
for (String formatString : formatStrings)
{
try
{
return new SimpleDateFormat(formatString).parse(dateString);
}
catch (ParseException e) {}
}

return null;
}

How to Parse different Date formats from String in Java (FAQ)


Avoid legacy date-time classes

The old Date, Calendar, and SimpleDateFormat classes are an awful sour mess of poor design. Never use them. Now supplanted by the java.time classes.

Parsing

First test if input string equals "null".

Next, parse the first format with simply LocalDate.parse( "1987-03-23" ) and trap for exception. That standard ISO 8601 format is handled by default, so no need to specify a formatting pattern.

Lastly, define a formatting pattern DateTimeFormatter.ofPatten( "dd-MM-uuuu" ) and parse with that, calling LocalDate.parse( input , formatter ). Trap for exception.

If all those fail, you have unexpected inputs.

Generating strings

Once you have a LocalDate object in hand, generate a string using LocalDate.format where you pass a DateTimeFormatter. You can define a formatting pattern as seen above. Or, you can call DateTimeFormatter.ofLocalizedDate to let java.time automatically localize for you.

Search Stack Overflow

This has been covered many many times already. Search Stack Overflow for these class names, to see more detail and more code.

I suggest using a search engine with site:stackoverflow.com criteria as the built-in search feature in Stack Overflow is weak and tends to ignore Answers, biasing towards only Questions.

For example, do this:

https://duckduckgo.com/?q=site%3Astackoverflow.com+java+DateTimeFormatter&t=h_&ia=web


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • Java SE 8, Java SE 9, Java SE 10, and later

    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and Java SE 7

    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android

    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Java - Date format for Multiple Scenarios

In case of you have optional parts in pattern you can use [ and ].

For example

public static Instant toInstant(final String timeStr){
final DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH[:mm[:ss[ SSSSSSSS]]]")
.withZone(ZoneId.of("UTC"));
try {
return Instant.from(formatter.parse(timeStr));
}catch (DateTimeException e){
final DateTimeFormatter formatter2 = DateTimeFormatter
.ofPattern("yyyy-MM-dd")
.withZone(ZoneId.of("UTC"));
return LocalDate.parse(timeStr, formatter2).atStartOfDay().atZone(ZoneId.of("UTC")).toInstant();
}
}

cover

  • yyyy-MM-dd
  • yyyy-MM-dd HH
  • yyyy-MM-dd HH:mm
  • yyyy-MM-dd HH:mm:ss
  • yyyy-MM-dd HH:mm:ss SSSSSSSS

How to parse custom multiple Date formats in Java

You can use [] to define the optional parts within a pattern. Additionally you need to set defaults to not get an exception when no time is supplied.

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyMMdd['h'HH]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter();

String dateString1 = "201028h05";
LocalDateTime date1 = LocalDateTime.parse(dateString1, formatter);
System.out.println(date1);

String dateString2 = "201028";
LocalDateTime date2 = LocalDateTime.parse(dateString2, formatter);
System.out.println(date2);

Output:

2020-10-28T05:00
2020-10-28T00:00

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

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

Java or Scala fast way to parse dates with many different formats using java.time

No, there is no nice/magic way to do this, for two main reasons:

  1. There are variations and ambiguities in data formats that make a generic parser very difficult. e.g. 11/11/11

  2. You are looking for very high performance, which rules out any brute-force methods. 1us per date means only a few thousand instructions to do the full parsing.

At some level you are going to have to specify what formats are valid and how to interpret them. The best way to do this is probably one or more regular expressions that extract the appropriate fields from all the allowable combinations of characters that might form a date, and then much simpler validation of the individual fields.

Here is an example that deals with all dates you listed:

val DateMatch = """(\d\d\d\d)[-/ ]?((?:\d\d)|(?:\w\w\w))?[-/ ]?(\d\d)?T?(\d\d)?:?(\d\d)?:?(\d\d)?[\.]*(\d+)?(.*)?""".r

date match {
case DateMatch(year, month, day, hour, min, sec, usec, timezone) =>
(year, Option(month).getOrElse("1"), Option(day).getOrElse(1), Option(hour).getOrElse(0), Option(min).getOrElse(0), Option(sec).getOrElse(0), Option(usec).getOrElse(0), Option(timezone).getOrElse(""))
case _ =>
throw InvalidDateException
}

As you can see it is going to get very hairy once all the possible dates are included. But if the regex engine can handle it then it should be efficient because the regex should compile to a state machine that looks at each character once.

Is there a way to use a single date format string to parse two types of dates?

Doesn't seem to be possible to parse different formats in one DateFormatter

But, you can define one DateFormat for each one, and check for the presence of the "T" character, or the length of the date source, to decide which formatter to use.

I'd leave the try / catch to deal with really unparseable dates



Related Topics



Leave a reply



Submit