2

I am having problem parsing dates in Java. Below is the code.

 String dateString = "2017-12-13T16:49:20.730555904Z";
 List<String> formatStrings = Arrays.asList("yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'");

    for (String formatString : formatStrings)
    {
        try
        {
            SimpleDateFormat formatter = new SimpleDateFormat(formatString);
            formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date d = formatter.parse(dateString);
            System.out.println("Format is:" + formatString);
            System.out.println("Orignal Date: " + d);
            System.out.println("Orignal MS: " + d.getTime());

            return d;
        }
        catch (ParseException e) {}
    }

    return null;
}

When I run this program, I get the following output.

Format is:yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'
Orignal Date: Fri Dec 22 03:45:15 UTC 2017
Orignal MS: 1513914315904

I am not sure why it is giving me Dec 22 when it should be Dec 13. But if I change my input date to this.

String dateString = "2017-12-13T16:49:20.7Z";

i.e only one character before the Z. then I get the correct output.

Format is:yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'
Orignal Date: Wed Dec 13 16:49:20 UTC 2017
Orignal MS: 1513183760007

It gives me correct output till 3 numbers before the Z. If more than 3 numbers than I got the wrong output.

It would be great if someone can point me out what I am missing here.

PS: I am parsing these dates in android. I have set min API level to 16 and java.time is not available for API level below 26.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Waqar Ahmed
  • 5,005
  • 2
  • 23
  • 45
  • Related: [java.util.Date format SSSSSS: if not microseconds what are the last 3 digits?](https://stackoverflow.com/questions/19223171/java-util-date-format-ssssss-if-not-microseconds-what-are-the-last-3-digits) – Ole V.V. Dec 13 '17 at 19:23

2 Answers2

12

S in SimpleDateFormat specifies a number of milliseconds, not a fraction of a second. You've specified 730555904 milliseconds, which is ~8.45 days - hence the date change.

java.util.Date only has a precision of milliseconds. I would recommend using the java.time package, which has a precision of nanoseconds, like your input. Use DateTimeFormatter for parsing. Note that in DateTimeFormatter, the S specifier is for fraction-of-a-second.

Even better, Instant.parse uses the right format anyway:

import java.time.*;

public class Test {
    public static void main(String... args) throws Exception {
        String text = "2017-12-13T16:49:20.730555904Z";
        Instant instant = Instant.parse(text);
        System.out.println(instant);
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. Since the release of Java-8 (Mar 2014), it is highly recommended to stop using them completely and switch to the modern Date-Time API.

I am not sure why it is giving me Dec 22 when it should be Dec 13.

This is a great example of how much error-prone SimpleDateFormat can be. SimpleDateFormat considers the fraction-of-second part as the number of milliseconds. Let's see the amount of duration by which it causes deviation from the correct date-time in your case.

import java.time.Duration;

class Main {
    public static void main(String[] args) {
        Duration duration = Duration.ofMillis(730555904);
        System.out.println(String.format("%d days %d hours %d minutes %d seconds %d milliseconds",
                duration.toDaysPart(), duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(),
                duration.toMillisPart()));
    }
}

Output:

8 days 10 hours 55 minutes 55 seconds 904 milliseconds

Now, you understand why you got Dec 22 instead of Dec 13. Let's see how clean and correct solution java.time provides you with.

You do not need a DateTimeFormatter

java.time API is based on ISO 8601 and therefore you do not need a DateTimeFormatter to parse a date-time string which is already in ISO 8601 format (e.g. your date-time string, 2017-12-13T16:49:20.730555904Z).

Demo:

import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;

class Main {
    public static void main(String[] args) {
        Instant instant = Instant.parse("2017-12-13T16:49:20.730555904Z");
        System.out.println(instant);

        // or parse it to a ZonedDateTime
        ZonedDateTime zdt = ZonedDateTime.parse("2017-12-13T16:49:20.730555904Z");
        System.out.println(zdt);

        // or parse it to an OffsetDateTime
        OffsetDateTime odt = OffsetDateTime.parse("2017-12-13T16:49:20.730555904Z");
        System.out.println(odt);
    }
}

Output:

2017-12-13T16:49:20.730555904Z
2017-12-13T16:49:20.730555904Z
2017-12-13T16:49:20.730555904Z

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

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