-1

I have an Android application that shows the creation date of a ticket with info I get from the server.

The thing is that I can't parse the string I'm getting I get an exception when I try to parse it with a SimpleDateFormat

This is what I get from the server:

2017-07-04 23:59:51.486559-05

This is the format I use to parse it:

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX", Locale.ENGLISH);

The exception I'm getting with this approach is:

java.lang.IllegalArgumentException: Unknown pattern character 'X'
 at java.text.SimpleDateFormat.validatePatternCharacter(SimpleDateFormat.java:323)
 at java.text.SimpleDateFormat.validatePattern(SimpleDateFormat.java:312)
 at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:365)
 at com.unipagos.app.wallet.payment.receipts.datamodels.PaymentReceipt.dateFromString(PaymentReceipt.java:320)

I've been at it for several hours and I've also tried with

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.ENGLISH);

to no avail, getting this exception:

java.text.ParseException: Unparseable date: "2016-05-30 20:41:19.934959+00" (at offset 23)
 at java.text.DateFormat.parse(DateFormat.java:579)
 at com.unipagos.app.wallet.payment.receipts.datamodels.PaymentReceipt.dateFromString(PaymentReceipt.java:326)
 at com.unipagos.app.wallet.payment.receipts.datamodels.PaymentReceipt.loadReceiptJSONObject(PaymentReceipt.java:164)
 at com.unipagos.app.history.HistoryListFragment.populateTransactionList(HistoryListFragment.java:371)
 at com.unipagos.app.history.HistoryListFragment.access$3200(HistoryListFragment.java:83)

Am I missing something?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Ranbeuer
  • 167
  • 3
  • 12
  • 2
    Despite the documentation, `X` is fairly new. – CommonsWare Sep 04 '17 at 23:43
  • 2
    I don't think the milliseconds portion is typically 6 digits – OneCricketeer Sep 04 '17 at 23:46
  • 1
    Your string, `2016-05-30 20:41:19.934959+00`, is not exactly ISO 8601, as far as I know. It’s close, though, and you obviously need it parsed anyway. – Ole V.V. Sep 05 '17 at 05:17
  • 1
    Related: [java.text.ParseException: Unparseable date: yyyy-MM-dd HH:mm:ss.SSSSSS](https://stackoverflow.com/questions/8607809/java-text-parseexception-unparseable-date-yyyy-mm-dd-hhmmss-ssssss#8607894). And this one: [Parse svn log -xml date output using SimpleDateFormat](https://stackoverflow.com/questions/23579835/parse-svn-log-xml-date-output-using-simpledateformat). – Ole V.V. Sep 05 '17 at 05:20

1 Answers1

4

SimpleDateFormat cannot parse your date-time string. The string has microseconds (6 decimals on the seconds), where SimpleDateFormat only supports milliseconds (3 decimals).

ThreeTenABP

So this seems to be a good occasion for you to abandon the long outdated classes Date and SimpleDateFormat. General experience says that the modern Java date and time API is much nicer to work with. For Android you get it in ThreeTenABP, link below. The rest is straightforward when you know how:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSX", Locale.ENGLISH);
    OffsetDateTime odt = OffsetDateTime.parse(creationDateFromServer, dtf);
    System.out.println(odt);

This prints

2017-07-04T23:59:51.486559-05:00

This agrees with your input, 2017-07-04 23:59:51.486559-05. And by the way, the printed string is in ISO 8601; this is what the modern classes’ toString() methods produce.

Oldfashioned solution

If you insist on sticking to the outdated classes, one way through is to strip off the last three decimals so there are only three, add two digits to the time zone offset so there are four, and use Z for parsing it:

    creationDateFromServer = creationDateFromServer
            .replaceFirst("(\\.\\d{3})\\d{3}((?:\\+|-)\\d{2})$", "$1$200");
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.ENGLISH);
    try {
        Date d = df.parse(creationDateFromServer);
        System.out.println(d);
    } catch (ParseException pe) {
        System.out.println(pe + " at offset " + pe.getErrorOffset());
    }

Note how hard it is to read the regular expression. Also this will break if the offset comes as for example +0530 one day. Output is:

Wed Jul 05 06:59:51 CEST 2017

The Date is printed in my local time zone, in this case at offset +0200, so the time printed agrees with 23:59:51.486-0500 on the day before. This means that internally the Date object holds the right point in time.

Link

How to use ThreeTenABP in Android Project

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 2
    I moved it to my answer, but yeah I can admit when I'm wrong :D – Barthy Sep 05 '17 at 10:30
  • 1
    This is the answer I was looking for. Even though I added the six S digits to the format, in some devices it just wouldn't get parsed. I added the ThreeTenABP API and it worked as expected. – Ranbeuer Sep 05 '17 at 21:50