简体   繁体   中英

java.text.ParseException: Unparseable date "yyyy-MM-dd'T'HH:mm:ss.SSSZ" - SimpleDateFormat

I would appreciate any help with finding bug for this exception:

java.text.ParseException: Unparseable date: "2007-09-25T15:40:51.0000000Z"

and following code:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Date date = sdf.parse(timeValue);
long mills = date.getTime();
this.point.time = String.valueOf(mills);

It throws expcetion with Date date = sdf.parse(timeValue); .

timeValue = "2007-09-25T15:40:51.0000000Z"; , as in exception.

Thanks.

Z represents the timezone character. It needs to be quoted:

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

In Java 7 you can also use the X pattern to match an ISO8601 timezone, which includes the special Z (UTC) value:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSX");
Date date = sdf.parse("2007-09-25T15:40:51.0000000Z");

However, it seems to require an exact number of millisecond characters in the pattern, which is not required for the 'Z' character pattern, and is rather inconvenient. I think this is because the ISO8601 definition also includes "two-digit hours", which are just numbers, so cannot be distinguished by the parser from the preceding milliseconds.

So this version would be fine for timestamps down to second precision, less so for milliseconds.

java.time

I recommend that you use java.time, the modern Java date and time API, for your date and time work. Your string is in ISO 8601 format and can be directly parsed by the java.time.Instant class without us specifying any formatter:

    String timeValue = "2007-09-25T15:40:51.0000000Z";
    
    Instant i = Instant.parse(timeValue);
    long mills = i.toEpochMilli();
    String time = String.valueOf(mills);
    
    System.out.println(time);

Output:

1190734851000

May use a formatter for output if desired

If we know for a fact that the millisecond value will never be negative, java.time can format it into a string for us. This saves the explicit conversion to milliseconds first.

private static final DateTimeFormatter EPOCH_MILLI_FORMATTER
        = new DateTimeFormatterBuilder().appendValue(ChronoField.INSTANT_SECONDS)
                .appendValue(ChronoField.MILLI_OF_SECOND, 3)
                .toFormatter(Locale.ROOT);

Now formatting is trivial:

    assert ! i.isBefore(Instant.EPOCH) : i;
    String time = EPOCH_MILLI_FORMATTER.format(i);

And output is still the same:

1190734851000

In particular if you need to format Instant objects to strings in more places in your program, I recommend the latter approach.

What went wrong in your code?

First of all, there is no way that SimpleDateFormat can parse 7 decimals of fraction of second correctly. As long as the fraction is zero, the result will happen to come out correct anyway, but imagine a time that is just one tenth of a second after the full second, for example, 2007-09-25T15:40:51.1000000Z . In this case SimpleDateFormat would parse the fraction into a million milliseconds, and your result would be more than a quarter of an hour off. For greater fractions the error could be several hours.

Second as others have said format pattern letter Z does not match the offset of Z meaning UTC or offset zero from UTC. This caused the exception that you observed. Putting Z in quotes as suggested in the accepted answer is wrong too since it will cause you to miss this crucial information from the string, again leading to an error of several hours (in most time zones).

Link

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM