Quinteger
Quinteger

Reputation: 173

Safe SimpleDateFormat parsing

I have a small block of code which parses response generation time from the response itself and turns it into a date for future purposes. It goes like this:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date responseTime = sdf.parse(RStime);

And it almost works like a charm. To be precise, it works 99.9% of the time, with the exception of one case: When the millisecond part is 000 then the Server doesn't append the .000 milliseconds at all, hence we have a problem.

Now, according to SimpleDateFormat docs if parsing fails, the function returns null. However, I probably misinterpreted it as it just throws an exception.

I am very new to Java and try-catch mechanisms, so could anyone please provide an elegant good-practice solution for handling such cases?

Thanks!

Upvotes: 2

Views: 2077

Answers (4)

Anonymous
Anonymous

Reputation: 86286

java.time

String rsTime = "2018-04-09T10:47:16.999-02:00";
OffsetDateTime responseTime = OffsetDateTime.parse(rsTime);
System.out.println("Parsed date and time: " + responseTime);

Output from this snippet is:

Parsed date and time: 2018-04-09T10:47:16.999-02:00

It works just as well for the version with the 000 milliseconds omitted:

String rsTime = "2018-04-09T10:47:16-02:00";

Parsed date and time: 2018-04-09T10:47:16-02:00

The classes you used, SimpleDateFormat and Date, are poorly designed and long outdated (the former in particular notoriously troublesome). So it is not only in this particular case I recommend using java.time, the modern Java date and time API, instead. However, the strings from your server are in ISO 8601 format, and OffsetDateTime and the other classes of java.time parse this format as their default, that is, without any explicit formatter, which already makes the task remarkably easier. Furthermore, in the standard the fractional seconds are optional, which is why both the variants of the string are parsed without any problems. OffsetDateTime also prints ISO 8601 back from it’s toString method, which is why in both cases a string identical to the parsed one is printed.

Only in case you indispensably need an old-fashioned Date object for a legacy API that you cannot change just now, convert like this:

Instant responseInstant = responseTime.toInstant();
Date oldfashionedDateObject = Date.from(responseInstant);
System.out.println("Converted to old-fashioned Date: " + oldfashionedDateObject);

Output on my computer in Europe/Copenhagen time zone is:

Converted to old-fashioned Date: Mon Apr 09 14:47:16 CEST 2018

Link: Oracle tutorial: Date Time explaining how to use java.time.

Upvotes: 1

Chaarmann
Chaarmann

Reputation: 154

You can check for a dot and then use the first or second format:

String timeString = "2018-04-09T10:47:16.999-02:00";
//String timeString = "2018-04-09T10:47:16-02:00";
String format = timeString.contains(".") ? "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" : "yyyy-MM-dd'T'HH:mm:ssXXX";
Date responseTime = new SimpleDateFormat(format).parse(timeString);
System.out.println("responseTime: " + responseTime);

If you comment-out the first line and comment-in the second and run it again, it will both print out:

responseTime: Mon Apr 09 14:47:16 CEST 2018

By the way:

  • Java 7 (the version you use obviously) returns a java.text.ParseException: Unparseable date: "2018-04-09T10:47:16-02:00"

  • Optionals are supported since Java 8.

Upvotes: 0

Amongalen
Amongalen

Reputation: 3131

Is it actually an exceptional situation? If it is not then you probably shouldn't use exceptions in that case. In my opinion it is normal that time can end with .000ms. In this case you can check if the string contains . (dot) and if not append .000 to the end.

if(!RStime.contains(".")){
    RStime+=".000";
}

Edit: I've forgot about time zone in the time String. You probably need something a little bit more complicated for that. Something like this should do it:

if(!RStime.contains(".")){
    String firstPart = RStime.substring(0, 21);
    String secondPart = RStime.substring(21);
    RStime = firstPart + ".000" + secondPart;
}

Upvotes: 0

juanlumn
juanlumn

Reputation: 7095

According to the SimpleDateFormat doc that you mentioned the parse method:

public Date parse(String text, ParsePosition pos)

Throws:

NullPointerException - if text or pos is null.

So one option is to catch that exception and do what you need inside the catch, for example:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try {
  Date responseTime = sdf.parse(RStime, position);
} catch (NullPointerException e) {
  e.printStackTrace();
  //... Do extra stuff if needed
}

Or the inherited method from DateFormat:

public Date parse(String source)

Throws:

ParseException - if the beginning of the specified string cannot be parsed.

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try {
  Date responseTime = sdf.parse(RStime);
} catch (ParseException e) {
  e.printStackTrace();
  //... Do extra stuff if needed
}

Upvotes: 0

Related Questions