views:

461

answers:

6

What's the nicest way to parse a date that can be in one of the following formats

 "dd-MM-yyyy HH:mm"
 "dd/MM/yyyy HH:mm"
 "dd.MM.yyyy HH:mm"

without creating 3 SimpleDateFormats and parsing against each one.

Thanks

+4  A: 

Could you run two replace operations first, so that you reduce all three formats to a single one?

kgiannakakis
Ah.. i didnot see this one before posting my answer +1. great!
Aviator
+4  A: 

It's probably easiest to "tweak" the source string into a canonical format:

if (text.length() == 16)
{
    if ((text.charAt(2) == '/' && text.charAt(5) == '/') ||
        (text.charAt(2) == '.' && text.charAt(5) == '.'))
    {
        text = text.substring(0, 2) + "-" + text.substring(3, 5) 
           + "-" + text.substring(6);
    }
}

Then use the format string using "-".

Note that this is very specific, only replacing exactly the characters you're interested in, to avoid unwanted side-effects.

Jon Skeet
Thanks, I'm gonna start working on a generic date parser for our code base and this will be a good start
Tarski
A: 

ParseExact can take an array of formats. You still have to specify all formats, but it's a single operation.

Tor Haugen
This is a Java question, there isn't a parseExact method
Tarski
that's not a method of SimpleDateFormat, or the standard case for a Java method. Are you referencing C# or similar?
Rich Seller
Sorry, my mistake. Yes, that's C#.
Tor Haugen
+3  A: 

You may use Apache commons lang DateUtils.parseDate

import java.text.ParseException;
import org.apache.commons.lang.time.DateUtils;

public class Test {

public static void main(String[] args) throws ParseException {

 String[] values = new String[]{"31-12-2009 12:00", "31/12/2009 12:00", "31.12.2009 12:00"};
 String[] parsePatterns = new String[]{"dd-MM-yyyy HH:mm", "dd/MM/yyyy HH:mm", "dd.MM.yyyy HH:mm"};

 for (String value : values) {
  System.out.println(DateUtils.parseDate(value, parsePatterns));
 }
}
}

Well, internally it creates SimpleDateFormats, but whats wrong with that?

Tim Büthe
+1  A: 

how about a regex:

"\\d\\d[./-]\\d\\d[./-]\\d\\d\\d\\d \\d\\d:\\d\\d"

In code this would mean something like this:

Pattern pattern = 
Pattern.compile("(\\d\\d)([./-])(\\d\\d)([./-])(\\d\\d\\d\\d) (\\d\\d):(\\d\\d)");

Matcher matcher = 
pattern.matcher("31-07-1983 15:30");

if (matcher.find() && matcher.group(2).equals(matcher.group(4))) {
  int day = Integer.parseInt(matcher.group(1));
  int month = Integer.parseInt(matcher.group(3));
  int year = Integer.parseInt(matcher.group(5));
  int hour = Integer.parseInt(matcher.group(6));
  int minute = Integer.parseInt(matcher.group(7));
}
NomeN
Because \ is an escape character in Java string literals, you'd have to double each \: "\\d\\d[\\.\\/\\-]\\d\\d[\\.\\/\\-]\\d\\d\\d\\d \\d\\d:\\d\\d"
Jesper
You are mistaken - you would still need to write "\\d" in your Java source code, regardless if this is a regular expression or not.
Jesper
Now I had to go and check, you are absolutely right sir. That is ugly as hell, they could so easily just have used another escape character.
NomeN
A: 

Doing it yourself with a regular expression:

public class SpecialDateFormat
{
 private final static Pattern PATTERN = Pattern.compile("(\\d{2})[\\.\\/\\-](\\d{2})[\\.\\/\\-](\\d{4}) (\\d{2}):(\\d{2})");

 public static Date parse(String text) throws ParseException {
  Matcher m = PATTERN.matcher(text);
  if (m.matches()) {
   int dd = Integer.parseInt(m.group(1));
   int mm = Integer.parseInt(m.group(2));
   int yy = Integer.parseInt(m.group(3));
   int hh = Integer.parseInt(m.group(4));
   int mi = Integer.parseInt(m.group(5));

   // NOTE: Checking if values are in valid ranges omitted

   Calendar cal = Calendar.getInstance();
   cal.set(yy, mm - 1, dd, hh, mi, 0);

   return cal.getTime();
  }
  else {
   throw new ParseException("Unparseable date: " + text, 0);
  }
 }
}

Note however that this does allow mixing different separators, e.g. "17-09/2009 12:00" would be allowed.

Jesper
I call blatant stealing of concept ;-), you'll see that I've worked in the different separator checking with not much effort.
NomeN
And apparently you do not need to escape any of the characters in between the [], not even the period.
NomeN