Dateformatter in java

Viewed 7724

I am using the below code to format millisecond resolution date strings. It works for 2018-09-14T13:05:21.329Z but not 2018-09-14T13:05:21.3Z. Can anybody suggest the reason and how to correct it?

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
SimpleDateFormat sdfDestination = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
    Date parsedDate = formatter.parse(date);
    String destDate = sdfDestination.format(parsedDate);
    return destDate;
} catch (java.text.ParseException parseException) {
    logger.error("Parse Exception occured while converting publication time to date "
            + "format 'yyyy-MM-dd HH:mm:ss'", parseException);
}

I get below exception:

java.text.ParseException: Unparseable date: "2018-09-14T13:05:21.3Z"
    at java.text.DateFormat.parse(Unknown Source) ~[na:1.8.0_181]
    at com.noordpool.api.implementation.utility.Utility.parseDate(Utility.java:136) [classes/:na]
    at com.noordpool.api.implementation.utility.Utility.parseMessage(Utility.java:77) [classes/:na]
4 Answers

Your only problem is that you are using a wrong pattern for SimpleDateFormat, you need to change:

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");

To:

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

Because the Z used in the date string means "zero hour offset" so you just need to pass it as 'Z' in your pattern.

This is a working demo with the right pattern.

Edit:

And to make things work with different Locales and Timezones, you need to use the appropriate Locale when you are creating the SimpleDateFormat instance, this is how should be the code:

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);

The only possible issue I can see is that you're passing in milliseconds incorrectly and the program doesn't know what to do about it.

So the last part of the formatter indicates with milliseconds and a timezone as .SSSX

But how does it evaluate 3Z for the input into this? I mean, do you say it's 300 timezone Z, or say it's 003 timezone Z, or worse, try and parse it as 3Z, which hopefully you see that you cannot turn '3Z' into a number.

To remedy this, I'd validate your input 'date' and ensure the milliseconds part is always 3 digits long, this removes the ambiguity and the program always knows that you mean '300 milliseconds, timezone Z'.

There is a problem in java 8 where the number of characters that you specified with the formatter should be an exact match (which is not specified in the documentation). You can use three different Formatters and use nested exception as follows:

DateFormat format1 = new SimpleDateFormat("y-M-d'T'H:m:s.SX");
DateFormat format2 = new SimpleDateFormat("y-M-d'T'H:m:s.SSX");
DateFormat format3 = new SimpleDateFormat("y-M-d'T'H:m:s.SSSX");
Date parsedDate;

try {
    // Parsing for the case - 2018-09-14T13:05:21.3Z 
    parsedDate  = format1.parse(date); 
} catch (ParseException e1) {

    try {
         // Parsing for the case - 2018-09-14T13:05:21.32Z 
         parsedDate = format2.parse(date); 
    } catch (ParseException e2) {

          try {
              // Parsing for the case - 2018-09-14T13:05:21.329Z 
              parsedDate = format3.parse(date);
          } catch (ParseException e2) {
             //The input date format is wrong
             logger.error("Wrong format for date - " + date);      
          }

    }
}

java.time

    DateTimeFormatter dtfDestination
            = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
    String date = "2018-09-14T13:05:21.3Z";
    String destDate = Instant.parse(date)
            .atZone(ZoneId.of("Indian/Comoro"))
            .format(dtfDestination);
    System.out.println(destDate);

Output from this snippet is:

2018-09-14 16:05:21

Please substitute your correct time zone if it didn’t happen to be Indian/Comoro, since correct output depends on using the correct time zone. If you want to use your JVM’s default time zone, specify ZoneId.systemDefault(), but be aware that the default can be changed at any time from other parts of your program or other programs running in the same JVM.

I am exploiting the fact that your string, "2018-09-14T13:05:21.3Z", is in ISO 8601 format, the format that the classes of java.time parse as their default, that is, without any explicit formatter. Instant.parse accepts anything from 0 through 9 decimals on the seconds, so there is no problem giving it a string with just 1 decimal, as you did. In comparison there is no way that an old-fashioned SimpleDateFormat can parse 1 decimal on the seconds with full precision since it takes pattern letter (uppercase) S to mean milliseconds, so .3 will be parsed as 3 milliseconds, not 3 tenths of a second, as it means.

Jahnavi Paliwal has already correctly diagnosed and explained the reason for the exception you got.

The date-time classes that you used, DateFormat, SimpleDateFormat and Date, are all long outdated and SimpleDateFormat in particular is notoriously troublesome. Since you seem to be using Java 8 (and even if you didn’t), I suggest you avoid those classes completely and use java.time instead.

Links

Related