0

How to parse a string with always different formats of date (sometimes "yyyy-MM-dd HH:mm:ss", or "yyyy-MM-dd'T'HH:mm" or also "yyyy-MM-dd HH:mm")?

If I use the following code - it fails if the string in value has other format than "yyyy-MM-dd HH:mm"

public static Date test(String value) throws ... {

  SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
  return df.parse(value);


}
Igor
  • 556
  • 3
  • 19

2 Answers2

3

I would suggest you just try to parse each specific format, catching ParseException and just moving onto the next format:

private static final String[] patterns = {
    "yyyy-MM-dd HH:mm",
    "yyyy-MM-dd'T'HH:mm",
    "yyyy-MM-dd HH:mm"
};

public static Date test(String value) throws ... {
    for (String pattern : patterns) {
        try {
           DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
           return df.parse(value);
        } catch (ParseException e) {
           // No-op - move onto the next pattern
        }
    }
    // Do whatever you want here - throw ParseException, or return null
}

That's simple, but abuses exceptions. You could use the more obscure parse(String, ParsePosition) call instead:

public static Date test(String value) throws ... {
    for (String pattern : patterns) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date date = df.parse(value, new ParsePosition(0));
        if (date != null) {
            return date;
        }
    }
    // Do whatever you want here - throw ParseException, or return null
}

That may perform better - you'd have to benchmark it to say for sure.

It's unfortunate that you have to recreate the SimpleDateFormat objects on each call, but that's an issue with it not being thread-safe. Another alternative is to use Joda Time - which is a much nicer API overall - but it's not very clear how you'd do the exception-free approach there. (It may well be possible, just a bit obscure again. I suspect that DateTimeFormatter.parseInto would be your friend here.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • and what if the new unknown pattern will come? – Igor Jan 21 '14 at 17:04
  • 1
    @Igor: Well where would it come from? (If you want something answered, it should be in your question, by the way.) You need to have a list of patterns you expect *somewhere*. You could load them from a file or a database if you really wanted to make it very dynamic. – Jon Skeet Jan 21 '14 at 17:10
  • This is a part of source code our's server application and the strings with dates in differenz formats are coming from clients (client apps on IPhones/IPads) We have had always only "yyyy-MM-dd HH:mm:ss" as incoming pattern, but yesterday I saw in log also this patter "yyyy-MM-dd HH:mm" and error "Unparseable date..." I dont know what kind of patterns will else come to us. Our IOS Developer will fix creation of date in the next version (he will make it independend from Iphone settings), but now I should immediately correct it on server side. Of course I had to write it in my question. Sorry – Igor Jan 22 '14 at 11:57
  • 1
    @Igor: I would be very reluctant to try to write any code which would guess a format based on arbitrary data. In some cases you may well find it's ambiguous between different cultures, e.g. MM/dd/yyyy and dd/MM/yyyy. – Jon Skeet Jan 22 '14 at 11:58
1

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Also, quoted below is a notice from the home page of Joda-Time:

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

Solution using java.time, the modern Date-Time API:

DateTimeFormatterBuilder allows building a DateTimeFormatter with default time units. Also, you can specify optional parts of the pattern using a square bracket or by using the .optionalXXX function of DateTimeFormatterBuilder.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter parser = new DateTimeFormatterBuilder()
                .appendPattern("uuuu-MM-dd['T'][ ]HH:mm[:ss]")
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)                                                                       
                .toFormatter(Locale.ENGLISH);
        
        // Test
        Stream.of(
                    "2021-07-10T10:20:30",
                    "2021-07-10 10:20:30",
                    "2021-07-10T10:20",
                    "2021-07-10 10:20"
        ).forEach(s -> System.out.println(LocalDateTime.parse(s,  parser)));
    }
}

Output:

2021-07-10T10:20:30
2021-07-10T10:20:30
2021-07-10T10:20
2021-07-10T10:20

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110