1

Consider the following test code (Try it here yourself on ideone.com - an online Java compiler):

class Main {
    public static void main (String[] args) throws Exception {
        Main m = new Main();
        m.test1();
        System.out.println();
        m.test2();
    }

    void test1() throws Exception {
        System.out.println("TEST 1: ");

        String strTimestamp = "1957-04-27 00:00:00.01";
        System.out.println(strTimestamp + " [Original String]");

        String format = "yyyy-MM-dd HH:mm:ss.SS";
        System.out.println(format + " [Format used]");
        java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat(format);

        // Convert String to Date:
        java.util.Date date = formatter.parse(strTimestamp);
        long time = date.getTime();
        System.out.println(formatter.format(time) + " [Date#getTime() with same format]");

        java.sql.Timestamp timestamp = new java.sql.Timestamp(time);
        System.out.println(timestamp + " [Timestamp]");
    }

    void test2() throws Exception {
        System.out.println("TEST 2: ");

        String strTimestamp = "1957-04-27 00:00:00.001";
        System.out.println(strTimestamp + " [Original String]");

        String format = "yyyy-MM-dd HH:mm:ss.SSS";
        System.out.println(format + " [Format used]");
        java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat(format);

        // Convert String to Date:
        java.util.Date date = formatter.parse(strTimestamp);
        long time = date.getTime();
        System.out.println(formatter.format(time) + " [Date#getTime() with same format]");

        java.sql.Timestamp timestamp = new java.sql.Timestamp(time);
        System.out.println(timestamp + " [Timestamp]");
    }
}

The code above gives the following output:

TEST 1: 
1957-04-27 00:00:00.01 [Original String]
yyyy-MM-dd HH:mm:ss.SS [Format used]
1957-04-27 00:00:00.01 [Date#getTime() with same format]
1957-04-27 00:00:00.001 [Timestamp]

TEST 2: 
1957-04-27 00:00:00.001 [Original String]
yyyy-MM-dd HH:mm:ss.SSS [Format used]
1957-04-27 00:00:00.001 [Date#getTime() with same format]
1957-04-27 00:00:00.001 [Timestamp]

In TEST 1 I was expecting the [Original String], [Date#getTime() with same format] AND [Timestamp] to all have the same output just like at TEST 2.

Why does the [Timestamp] in TEST 1 have an extra zero compared to the Date?

Kevin Cruijssen
  • 9,153
  • 9
  • 61
  • 135
  • 3
    `S` means *millisecond*, not fractional second. Anything other than 3 of them makes no sense. Always use `SSS`. Parsing the original value of `00:00:00.01` will not result in 1/100'th of a second (i.e 10 ms), but in 1 ms. – Andreas Feb 21 '17 at 16:14
  • 2
    Related to: [java.util.Date format SSSSSS: if not microseconds what are the last 3 digits?](http://stackoverflow.com/q/19223171/5221149) – Andreas Feb 21 '17 at 16:27
  • @Andreas Thanks for the answer and link. As you correctly pointed out in the comments of Jonathan's answer, I'm trying to save this date in a `TIMESTAMP(2)` (length 16) column in my database, so this conversion is important. Currently it saves it in the format of `yyyyMMddHHmmssSS` in the database, so the `1957-04-27 00:00:00.001` in `TEST 1` is saved as `1957042700000000` instead of the expected `1957042700000001`. Do you perhaps know how to fix this? Should I manually correct the time in the Date object somehow after the conversion when the format only contains 1 or 2 capital `S`? – Kevin Cruijssen Feb 22 '17 at 08:23
  • 2
    What do you mean you *"save it in the format of `yyyyMMddHHmmssSS` in the database"*? You should be giving it to the database using `setTimestamp()` on a `PreparedStatement`. It has no format. If your string is `1957-04-27 00:00:00.01`, you add an extra `0` to make it 10 milliseconds, i.e. `1957-04-27 00:00:00.010`, then parse that with `SSS`, and save the result to the database using `Timestamp`. – Andreas Feb 22 '17 at 08:46
  • @Andreas Well, our database column has a `TIMESTAMP(2)` precision, meaning it will only save up to 16 in length (up to a precision of 10 ms). We also receive dates in our message services with a precision of 10 ms (like the `1957042700000001`). We then convert this to a Date object by using a `SimpleDateFormat("yyyyMMddHHmmssSS")` (which as correctly pointed out by you translates `010 ms` to `001 ms`). Then hibernate converts it to a `java.sql.Timestamp` object and saves that in our `TIMESTAMP(2)` database, resulting in `27-04-57 00:00:00,00` instead of the expected `27-04-57 00:00:00,01`. – Kevin Cruijssen Feb 22 '17 at 08:57
  • 1
    @Andreas Hmm.. Perhaps if the length of the incoming String (i.e. `1957042700000001`) is 16, I should manually add a zero (i.e. `19570427000000010`) so it is correctly converted? – Kevin Cruijssen Feb 22 '17 at 08:58
  • 1
    You got it. Append `0` and parse with `SSS`. Just like I said in my previous comment. – Andreas Feb 22 '17 at 16:52
  • @Andreas Yep, I did just that and it works like a charm. If you want you can make an answer with a summary of your comments so I can accept it. Thanks again, especially for those first two comments you've made. – Kevin Cruijssen Feb 22 '17 at 19:44

2 Answers2

1

tl;dr

Your old code and Question are now moot. Use java.time classes instead.

LocalDateTime.parse( 
    "1957-04-27 00:00:00.01".replace( " " , "T" ) 
).toString()

java.time

The troublesome java.util.Date and java.sql.Timestamp classes you are using are now legacy, supplanted by the java.time classes.

Your input string is close to standard ISO 8601 format. Comply by replacing the SPACE in the middle with a T. The java.time classes use ISO 8601 formats by default when parsing/generating strings.

String input = "1957-04-27 00:00:00.01".replace( " " , "T" ) ;

Parse as a LocalDateTime object given that your input lacks any indicator of time zone or offset-from-UTC.

LocalDateTime ldt = LocalDateTime.parse( input ) ;

To generate a string in ISO 8601 format, simply call toString(). The default formatter uses 0-3 groups of three digits as needed to display the fractional second, up to the nine digits of decimal fraction for nanoseconds.

String output = ldt.toString() ;

1957-04-27T00:00:00.010

You can exchange your LocalDateTime object directly with your database column of type TIMESTAMP WITHOUT TIME ZONE using JDBC 4.2 and later.

myPreparedStatement.setObject( … , ldt ) ;

And retrieval.

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

You're forgetting to format the timestamp values when printing them in both of your test methods - the fact that your first test succeeds at all is just a coincidence.

Change:

System.out.println(timestamp + " [Timestamp]");

To:

System.out.println(formatter.format(timestamp) + " [Timestamp]");

And you should get the result you expect.

jonny
  • 3,022
  • 1
  • 17
  • 30
  • Doing that will give the text result expected, but OP will still have the wrong time value, since `0.01` is 10 millisecond, but it got parsed as 1 ms. As such, simply reformatting the value incorrectly, is not the right answer, since you're just hiding the problem, rather than fixing it. – Andreas Feb 21 '17 at 16:25
  • @Andreas Is it our purpose to assume the context within which OP needs to format the datetime string? Sure it might be a loss of precision, but depending on the context a loss of precision might make next to no difference to the function of his code (formatting for aesthetics, for example) – jonny Feb 21 '17 at 16:33
  • 1
    What is the point of parsing a string, just to format it back the exactly the same string? None. So the text string is not the important part, it's just the mechanism to see the result of the Date/Timestamp values, which are the important parts. If you hide the fact that you parsed the value wrong, you're just lying to yourself. If you care about the accuracy of you code, e.g. making sure the parsed value gets inserted into the database correctly, then you need to fix the parsing, not hide the problem. – Andreas Feb 21 '17 at 16:52
  • ... Remember: Other code reading that same database will not know the millisecond value is wrong. It's a parsing error, and the parser should be fixed. – Andreas Feb 21 '17 at 16:55