-
Notifications
You must be signed in to change notification settings - Fork 1.2k
surprises with Java 9 Instant now having microsecond resolution #1178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Both JDK-8068730 has nothing to do with it, in only changes resolution of methods like H2 strictly follows the SQL standard for I do not understand what timestamps are compared in your tests and why you have a problem with it. There are too many things that can potentially be done wrong. Perhaps a simple test case that shows your problem is needed. |
please re-open if you have a test case |
Sure, @grandinj . I'll add a test case when I get time. But @katzyn doesn't seemed to have followed my explanation, so let me explain a bit more. Let's say I'm using Java 8 and I use Instant bar = Instant.now();
System.out.println(); Now let's say I have a INSERT INTO foo(id, bar) VALUES (?, ?) Using JDBC I plug in "foo-id" for the ROW ID, and preparedStatement.setTimestamp(2, Timestamp.from(bar)); Then I do a Instant bar2 = resultSet.getTimestamp("bar").toInstant(); If I print out assertThat(bar, is(bar2)); //passes under Java 8 Now suppose I upgrade to Java 9/10 and go back in time and do all this over. Instant bar = Instant.now();
System.out.println(); Now because Java 9/10 has microsecond precision, it prints out something different!
Nice! More precision is always better. So I stored it in H2 in Oracle mode, and then query it again—just like I did above. I can print out
Oops, that's not the same! Sure enough, if I do a JUnit test of the two assertThat(bar, is(bar2)); //fails under Java 9/10 I can get around this by truncating to milliseconds: assertThat(bar.truncatedTo(MILLIS), is(bar2.truncatedTo(MILLIS))); As I mentioned at the first of this ticket, this isn't a showstopper, but I do wonder why H2 isn't storing the microseconds. Or maybe the problem is that Oracle's default precision for |
I can't reproduce this. I tried the latest released version of H2 (1.4.197) and the following code sample: import java.sql.*;
import java.time.Instant;
public class TestTS {
public static void main(String[] args) throws SQLException {
try (Connection c = DriverManager.getConnection("jdbc:h2:mem:1;MODE=Oracle")) {
Statement s = c.createStatement();
s.execute("CREATE TABLE TEST(T TIMESTAMP NOT NULL)");
PreparedStatement ps = c.prepareStatement("INSERT INTO TEST VALUES (?)");
Instant i1 = Instant.now();
ps.setTimestamp(1, Timestamp.from(i1));
ps.executeUpdate();
ResultSet rs = s.executeQuery("SELECT T FROM TEST");
rs.next();
Instant i2 = rs.getTimestamp(1).toInstant();
System.out.printf("%s %s %s%n", i1, i2, i1.equals(i2));
}
}
} On Java 8 it prints
On Java 9 it prints
On Java 10 it prints
|
Please, provide a simple complete test case for it. Do not use |
@katzyn , thanks for the test case. That is very odd.
Yes, I will do one as soon as I have time. Now I'm even more intrigued to get to the bottom of this.
I don't understand your reasoning here, or how it is relevant. Care to explain? At the root of things they both use But anyway, I'll be happy to simply use |
Harmcrest does not exist in H2 build path, any H2 developer who will try to test it in own development enviroment will be required to resolve this problem somehow. It takes some time, nobody wants to spend it. Your test case with third-party dependencies most likely will be ignored. Also |
Good morning. @katzyn I just stuck your test case above, exactly, into a JUnit
I am running H2 1.4.197 on Windows 10 Professional 64-bit. My Java version is:
I haven't had a chance yet to try the code using In the meantime @katzyn could you verify the version of Java 10 you're using and the platform? Running |
Here's another output of the same test that's even more interesting!! Look how the value we got back from H2 is actually rounded up!!
This relates to something I saw with Oracle as well. Somehow the extra precision is getting rounded up, making the timestamp value I get back from the database later than the one I put in! |
If you need more than 6 fractional digits you should always specify fractional seconds precision for Both Oracle and H2 support precision from 0 to 9 fractional digits. Some other databases do not support precision above 6. Windows provides 7 digits by itself. Linux provides 9 digits. Java on Windows returns 7 digits, I didn't know it before because I never tested it. Java on Linux returns only 6 digits, because Java uses an old system function that provides only microseconds instead of better one. |
I was cleaning up my inbox and wanted to follow up on this. Thanks for providing me the information about the default precision for fractional digits; that's not something I had thought of, so I went and researched it. So the default timestamp precision is probably the cause of the problem. I'm not saying that I need more precision, but when Java 9+ enhanced the precision of the timestamps across the JVM on Windows, it causes a problem, so that's something everyone should be aware of. And it's not just that extra precision is truncated—the way H2 and Oracle store things, apparently they can round up at times! This completely surprised me and complicated the unit tests, which were trying to work around the round-trip problem by truncating at a certain number of fractional digits. I would urge you not to dismiss it so quickly. I'm not saying there's anything specifically you need to do; I'm just saying I think it's not a trivial problem, and could bite people trying to represent a normal Anyway thanks for the discussion and insights. |
Yes, they can. SQL standard clearly indicates that implementation-defined rounding or truncation is performed when necessary, so each database system may choose own behavior here. Many databases round these values in the same way as numeric values. In H2 half-up rounding is used. If your time zone uses DST and your timestamps may be near transitions, I suggest you not to use |
@garretwilson came across this while looking up a probably different timestamp precision issue, but you might be interested in this bug - https://bugs.openjdk.java.net/browse/JDK-8135055 Instants aren't directly involved but java.sql.Timestamp was doing some unexpected stuff under the hood in how it represented the millis/nano precision. |
Uh oh!
There was an error while loading. Please reload this page.
This is less of a bug report than a comment and request for discussion.
We had some unit tests that verified we got objects we saved in the H2 database were the same
java.time.Instant
that we requested. The timestamp was saved usingjava.sql.Timestamp.from(Instant)
using theInstant
we started out with. We're using Oracle mode with thetimestamp
type in the table schema.This worked fine in Java 8, but apparently Java 9 increased the clock resolution to microseconds; see https://bugs.openjdk.java.net/browse/JDK-8068730 . This means that now when we compare the timestamps, they don't match, because H2 is still using millisecond resolution. (I frankly haven't looked yet to see what precision Oracle uses by default.)
This isn't a big deal now that we know about it; we can simply update our unit tests to compare with only millisecond precision. But I thought I'd raise the issue so you could be aware of it. Maybe you would want to increase the H2 timestamp resolution to support microseconds—but only if Oracle supports that, if H2 is run in Oracle mode.
#365 and #389 may be related.
I welcome any comments
The text was updated successfully, but these errors were encountered: