java.text.ParseException: Unparseable date in JAVA Android

7.7k Views Asked by At

I am getting datetime Default CURRENT_TIMESTAMP result from MSSQL server database, Now i need to parse this date,time etc. Database result is like 2016-05-14 12:54:01.363

All i need is to traverse this datetime format. As i am doing it below.

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     try
    {
        Date date = format.parse(dateStr);
        SimpleDateFormat todayFormat = new SimpleDateFormat("dd");
        String dateToday = todayFormat.format(date);
        format = dateToday.equals(today) ? new SimpleDateFormat("hh:mm a") : new SimpleDateFormat("dd LLL, hh:mm a");
        String date1 = format.format(date);
        timestamp = date1.toString();
        //
    }
    catch (ParseException e)
    {
        e.printStackTrace();
    }

But I am getting error of:

    java.text.ParseException: Unparseable date: "{"date":"2016-05-14 12:54:01.363000","timezone":"UTC","timezone_type":3}" (at offset 0)

at line 3 .i.e Date date = format.parse(dateStr);

Need help!

2

There are 2 best solutions below

4
On BEST ANSWER

Microseconds

Your Question’s text quotes the input string as 2016-05-14 12:54:01.363 but your error message says otherwise, 2016-05-14 12:54:01.363000. Those extra three digits are microseconds.

The old date-time classes such as java.util.Date resolve only to milliseconds (3 digits after the decimal point for fraction of second). So SimpleDateFormat cannot handle such input. Either truncate the input string, or better, use modern date-time classes instead.

java.time

The bigger problem is that you are using old outmoded date-time classes from the earliest versions of Java. Those classes have been supplanted by the java.time framework built into Java 8 and later. Much of that java.time functionality has been back-ported to Java 6 & 7 by the ThreeTen-Backport project, and further adapted to Android by ThreeTenABP. So no reason to wrestle with those old and notoriously troublesome classes.

The java.time classes have much finer granularity, resolving to nanoseconds (9 digits of decimal fraction of second).

In java.time, the Instant class is a moment on the timeline in UTC with resolution of nanoseconds. An Instant is the equivalent of a java.util.Date except with finer resolution.

For more info on converting between old types and java.time types, see my Answer with diagram to another question.

Retrieve date-time values as date-time type

You should be retrieving date-time values from your database as date-time types, not as Strings. If your JDBC driver is complies with JDBC 4.2 (an update to JSR 221, and described here), you may be able to retrieve directly into an OffsetDateTime. You can think of an OffsetDateTime as an Instant plus an offset-from-UTC (a number hours, minutes, and seconds).

java.time.OffsetDateTime odt = resultSet.getObject( 1 );

If not, fall back to using the old java.sql types. For date-time, that means java.sql.Timestamp.

java.sql.Timestamp ts = resultSet.getTimestamp( 1 );

Minimize your use of the old java.sql types as they are part of the mess that is java.util.Date/.Calendar and so on. Immediately convert to java.time. To convert, look for new methods added to the old classes, such as toInstant.

Instant instant = ts.toInstant();

Parsing

If you must parse that input string, the easiest way is to make it comply with the ISO 8601 standard formats. The java.time classes use ISO 8601 formats by default when parsing/generating Strings.

To comply, replace the SPACE in the middle with a T. And, assuming this string does indeed represent a moment in UTC, append a Z. The Z is short for Zulu which means UTC.

String input = "2016-05-14 12:54:01.363000";
String inputModified = input.replace( " " , "T" );
Instant instant = Instant.parse( inputModified );

Time Zone

The Question ignores the crucial issue of time zones.

The best practice is to store date-time values in UTC. The better databases will adjust incoming data into UTC. When retrieved as a java.sql.Timestamp your value is in UTC, and so too when converted to an Instant.

A date and a time-of-day depends on the time zone. Apply a time zone (ZoneId) to your Instant to get a ZonedDateTime.

ZoneId zoneId = zoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Then interrogate to get the day of month and the time that you require.

int dayOfMonth = zdt.getDayOfMonth();

If you want the time-of-day as an object in its own right, use the LocalTime class.

LocalTime localTime = zdt.toLocalTime();

If you really want a String of the time-of-day, use the java.time.format package.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "hh:mm a" );
String output = zdt.format( formatter );

Even better, let java.time do the work of automatically formatting. Note the use of Locale, which determines (a) human language of name of month and such, and (b) cultural norms such as the ordering of the elements like year-month-day. Better to explicitly specify the Locale than implicitly rely on the JVM’s current default Locale which can change at runtime.

DateTimeFormatter timeOfDayFormatter = DateTimeFormatter.ofLocalizedTime( FormatStyle.SHORT );
timeOfDayFormatter = timeOfDayFormatter.withLocale( Locale.CANADA_FRENCH );
String output = zdt.toLocalTime().format( timeOfDayFormatter );
7
On

The problem depends on how you construct the format variable, and it's good practice to use passed-in pattern rather than default one. In your case (the argument is like "2016-05-14 12:54:01.363", then the format could be like that:

DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date date = format.parse(dateStr);

If your dataStr is returned by a json web service call with the format as indicated in the exception, then you can map the dateStr to the response type, for instance assumed it is called MyDate class.

ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
MyDate myDate = mapper.readValue(dateStr, MyDate.class);
Date date = myDate.getDate();

If you don't know the return type, you can simply map it to a Map class:

Map dateMap = new ObjectMapper().readValue(dateStr, Map.class);
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date date = format.parse(dateMap.get("date"));