-3

I receive the date from API in this format:

yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'

and convert it within this way:

String rawDate = "2017-05-11T15:46:48.2226756Z";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'", Locale.getDefault());
Date date = simpleDateFormat.parse(rawDate);
System.out.println(date); //Thu May 11 16:23:54 PDT 2017

However the date output is like that:

Thu May 11 16:23:54 PDT 2017

Output supposed to be:

Thu May 11 15:46:48 PDT 2017

How to convert the raw date properly?

bkm
  • 225
  • 2
  • 4
  • 14
  • You should use DateFormat. You can find samples here: https://stackoverflow.com/questions/454315/how-do-you-format-date-and-time-in-android – EtherPaul May 26 '17 at 17:47
  • Try setting the rawData just to "yyyy-MM-dd'T'HH:mm:ss". –  May 26 '17 at 17:47
  • 2
    Why do you have `'Z'` as a literal? – bradimus May 26 '17 at 17:49
  • 2
    Somebody misunderstood something here. Z means Zulu time zone, also known as UTC. So the output definitely is *not* supposed to be Thu May 11 15:46:48 PDT 2017, but 15:46:48 UTC, equal to 8:46:48 Pacific Daylight Time. Whether the error was in writing the string or in interpreting it, I dare not say. – Ole V.V. May 26 '17 at 18:14
  • Possible duplicate of [Parsing ISO\_INSTANT and similar Date Time Strings](https://stackoverflow.com/questions/32826077/parsing-iso-instant-and-similar-date-time-strings) – Ole V.V. May 26 '17 at 18:19
  • (1) You are ignoring vital information: the `Z`. (2) You trying to put nanoseconds into a legacy class that supports only milliseconds. Both issues have been covered *many* times already. **Search before posting** on Stack Overflow. – Basil Bourque May 27 '17 at 00:52

3 Answers3

3

The problem is that 'S' stands for milliseconds. So, in your case, you are telling it that the time is 15 hours, 46 minutes, 48 seconds and 2226756 milliseconds. If you add 2226756 milliseconds, i.e. 2226 seconds and 756 milliseconds to 15:46:48, you indeed get 16:23:54.

The easiest solution is probably to just find the period in your string, and truncate the string three places, later, i.e. convert it to:

2017-05-11T15:46:48.222

You can achieve this with the following line:

rawDate = rawDate.substring(0, rawDate.indexOf('.') + 4);

And then parse it with

SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault());

Note that this does not correctly round the microseconds. In your case, for example, 222.6756ms should be rounded up to 223ms, not down to 222ms. If this matters, you can do this manually by examining the first dropped digit to see if it's 5 or above and adding a millisecond to date.

Update (re: Basil Bourque):

If you would like to actually respect the time-zone identifier in your time-string (which indicates UTC as explained below by Ole V.V.), you can simply add 'UTC' to the end of the string and parse it with that timezone in older versions of Java without using any additional libraries:

rawDate = rawDate.substring(0, rawDate.indexOf('.') + 4) + "UTC";
SimpleDateFormat sDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz",
                                            Locale.getDefault());
Date date = sDF.parse(rawDate);
Markus A.
  • 12,349
  • 8
  • 52
  • 116
  • **Incorrect** You ignore the time zone, the `Z`. Your JVM’s current default time zone will be applied implicitly, changing the meaning of the original data. See the [correct Answer by Ole V.V.](https://stackoverflow.com/a/44208346/642706). – Basil Bourque May 27 '17 at 01:02
  • @BasilBourque Thanks! Learned something today! I didn't even think of 'Z' being a time-zone indicator. I somehow assumed they were always 3-letter (PDT, EST, CET, GMT, UTC, ...). Especially since the OP specifically stated that he wanted the timezone to be understood as PDT (see expected output sample). – Markus A. May 27 '17 at 16:38
  • 2
    Actually, those 3-4 letter abbreviations are *not* real time zones. They are not standardized. They are not even unique! CST is central time in US/Canada *and* China Standard Time. IST is India Standard Time *and* Ireland Standard Time, and so on. **Avoid these codes**. Use [real time zone names](https://en.m.wikipedia.org/wiki/List_of_tz_database_time_zones) in the format of `continent/region` such as `America/Montreal` and `Asia/Kolkata` and `Pacific/Auckland`. This is one of many reasons to avoid the awful legacy date-time classes. Use only the java.time classes. – Basil Bourque May 27 '17 at 17:06
3

SimpleDateFormat cannot handle any other number of decimals on the seconds than three (milliseconds), so there is no way to have it parse your string correctly. Furhermore the newer Java date and time classes are generally much more programmer-friendly and convenient. And they come with nanosecond precision (9 decimals on the seconds). So I am suggesting that you consider moving on to them.

As already commented Z means Zulu time zone, also known as UTC. So 2017-05-11T15:46:48.2226756Z means 15:46:48 UTC, equal to 8:46:48 Pacific Daylight Time. Your format is the ISO 8601 format for an instant, which the Instant class understand as its default, so parsing is easy:

    Instant instant = Instant.parse(rawDate);

The result is

2017-05-11T15:46:48.222675600Z

Only thing to note about this is the two added zeroes. The toString method prints decimals in groups of three, enough groups to render the full precision. So with 7 decimals it prints 9.

To get the date in the Pacific time zone:

    ZonedDateTime dateTime = instant.atZone(ZoneId.of("America/Los_Angeles"));

The result is what I predicted:

2017-05-11T08:46:48.222675600-07:00[America/Los_Angeles]

Now assume you got your raw date-time string from someone who misunderstood and really meant Thu May 11 15:46:48 PDT 2017 (it wouldn’t be the first time in history). Then you need to convert it to that. Again, while this would be cumbersome with the oldfashioned classes, it goes smoothly with the newer ones:

    ZonedDateTime dateTime = instant.atOffset(ZoneOffset.UTC)
            .atZoneSimilarLocal(ZoneId.of("America/Los_Angeles"));

The result is the one you asked for (except I am giving you all the decimals too):

2017-05-11T15:46:48.222675600-07:00[America/Los_Angeles]

For Android you get the newer date and time classes from the ThreeTenABP library.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Great answer. This is for Java 8 tho isn't it? – bkm May 26 '17 at 19:01
  • Thanks. It’s for Java 8 or 9 or later, or for Android Java 7 using the ThreeTenABP I mention, or for Java 6 and 7 using the ThreeTen Backport. You are sort of correct. :-) – Ole V.V. May 26 '17 at 19:02
  • @bkm, the answer itself uses classes that were added in Java8 and up. However, whole `java.time` package was derived from another library, [Joda-Time](http://www.joda.org/joda-time/) (the author of which was in expert group for JSR-310, which became the `java.time` package.). To cut it short, you can use this answer natively in Java 8 and up, or by using similar classes from Joda-Time library in Java 7 or lower. – M. Prokhorov May 29 '17 at 11:59
-1

You can just use below to parse.

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
yogidilip
  • 790
  • 6
  • 21
  • 4
    Note that this rounds down the milliseconds from your timestamp to 0, in case it matters... – Markus A. May 26 '17 at 18:02
  • 1
    **Incorrect** (a) You ignore the time zone. (b) You ignore the fractional second as another mentioned. (c) You will encounter an exception as this formatting pattern fails to match the input string. – Basil Bourque May 27 '17 at 00:59
  • @BasilBourque then how to make it without library and within Java 7? @ Ole V.V. answer is using library or within Java 8 – bkm May 27 '17 at 03:54
  • 1
    @bkm The wise solution is to add the back-port of java.time to your project, the ThreeTen-Backport project which is further adapted to Android in the ThreeTenABP project. Yes, see the [Answer by Ole V.V.](https://stackoverflow.com/a/44208346/642706). The legacy date-time classes are an awful mess, now entirely supplanted by java.time. Never type “SimpleDateFormat” again! – Basil Bourque May 27 '17 at 06:21
  • I did see the android tag before writing my answer. I know the newer classes are not yet built into Android Java. I still think I advised a good solution with ThreeTenABP. And futureproof. – Ole V.V. May 27 '17 at 08:40