Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java SimpleDateFormat.format() returns incorrect date when using miliseconds

I want to parse a date string from "2016-09-23T09:14:52.555000000" format to "23-SEP-2016" format.

Here is my code :

public class Tester {

    public static void main(String[] args) {
        System.out.println(displayDate("2016-09-23T09:14:52.555000000"));
        System.out.println(displayDate("2016-09-28T11:56:24.552000000"));
        System.out.println(displayDate("2016-09-23T09:29:12.507000000"));
        System.out.println(displayDate("2016-09-26T14:55:02.702000000"));
        System.out.println(displayDate("2016-09-26T09:50:24.880000000"));
        System.out.println(displayDate("2016-09-26T15:20:49.397000000"));
        System.out.println(displayDate("2016-09-26T15:21:21.559000000"));
    }

    public static String displayDate(String dateString) {
        String formattedDateString = "NA";

        if(dateString == null) {
            return formattedDateString;
        }

        SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");
        SimpleDateFormat dateFormatToDisplay = new SimpleDateFormat("dd-MMM-yyyy");

        try {
            Date date = oracleDateFormat.parse(dateString);
            formattedDateString = dateFormatToDisplay.format(date).toUpperCase();
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return formattedDateString;
    }

}

The problem is if I use this line

SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");

The output is (incorrect date values) :

29-SEP-2016
04-OCT-2016
29-SEP-2016
04-OCT-2016
06-OCT-2016
01-OCT-2016
03-OCT-2016

Whereas If use this line

SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss");

The output is (correct date values)

23-SEP-2016
28-SEP-2016
23-SEP-2016
26-SEP-2016
26-SEP-2016
26-SEP-2016
26-SEP-2016

I want to know why adding "S (Millisecond)" to the format string results in incorrect values.

Edit1 :

Even "yyyy-MM-dd'T'kk:mm:ss.SSS" returns incorrect values.

Edit2 :

I am using this code in Android App. It works on perfectly on emulator(API 23). For some devices it displays incorrect date. Can it be related to Java version?

like image 495
Monish Kamble Avatar asked Dec 03 '25 17:12

Monish Kamble


1 Answers

TL;DR:

In Java, it's not a fraction of seconds, it's a number of milliseconds. Don't make it larger than 999.

Change your date format in Oracle to include FF3 for the fractions of seconds to yield milliseconds.

Explanation

In Java, the S, or rather SSS, in your date format stands for milliseconds, which are thousands of a second. There are only thousand milliseconds in one second.

In Oracle, the date format doesn't specify milliseconds, but fractions of a second.

FF [1..9]
Fractional seconds; no radix character is printed. Use the X format element to add the radix character. Use the numbers 1 to 9 after FF to specify the number of digits in the fractional second portion of the datetime value returned. If you do not specify a digit, then Oracle Database uses the precision specified for the datetime datatype or the datatype's default precision. Valid in timestamp and interval formats, but not in DATE formats.

In Java, if you need a third of a second, you can't get more precise than 333 milliseconds. In Oracle however, you could express it as 333333 microseconds, or perhaps even 333333333 nanoseconds.

Oracle lets you specify the number of decimal digits you want, but if you don't, you get as much as the precision for the type allows. In your case, that seems to be 9.
Then your date format in Java interprets that as a number of milliseconds. Millions and billions of them. These are added to the rest of your date. Since there are only 86,400,000 milliseconds in a day, anything over that is another day added to your date.

Example

Let's take a look at your first test case, 2016-09-23T09:14:52.555000000.
555000000 milliseconds = 555000 seconds ≈ 154 hours ≈ 6 days and 10 hours.
Adding 6 days and 10 hours to the rest of your date, which is 2016-09-23 09:14:52, should get you to about 2016-09-29 19:00 and a bit. Change your output format (dateFormatToDisplay) to include the hours and you'll see what's happening.

Fix

Your Java date format expects no more than 3 digits for the milliseconds. Specify the number of fractional digits in Oracle. FF uses the maximal precision available for the type, FF3 only outputs 3 fractional digits — milliseconds.
If you can't alter the date format used in Oracle, trim it down to three decimal digits in Java. Note that anything less than 3 digits should be padded with zeroes to a length of three digits; 34.12 is interpreted as 34 seconds and 12 milliseconds, while you might be looking for 120 milliseconds.

like image 79
SQB Avatar answered Dec 06 '25 08:12

SQB