Search code examples
javadatetimesimpledateformatunparseable

java.text.ParseException: Unparseable date in JAVA Android


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!


Solution

  • 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 );