Skip to content
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

Fix getObject() conversion of DATETIMEOFFSET to LocalDateTime #2204

Merged
merged 7 commits into from
Oct 31, 2023

Conversation

rdicroce
Copy link
Contributor

Currently, if you have a DATETIMEOFFSET column and you call ResultSet#getObject() with LocalDate/LocalTime/LocalDateTime as the class argument, the JDBC driver converts the value into the local time zone.

One could argue that the current behavior is correct, but IMO it is a bug because it does not align with what java.time.OffsetDateTime#toLocalDateTime() and CAST(date_time_offset_column AS DATETIME2) do, which is to ignore the offset.

So this PR fixes the behavior for local types to ignore the offset.

@David-Engel
Copy link
Collaborator

As is, this would be a breaking change that would never be merged.

That said, before you spend more time on the code, if you want to push for this change, ways forward would include a connection property that maintained existing behavior and made this behavior opt-in. Additionally, justifying this change by comparing behavior with other JDBC drivers, like jTDS, MySQL's JDBC driver, PostgreSQL, and Oracle. If the MS JDBC driver is the outlier and all others behave as you are proposing, that gives your position more strength.

Regards,
David

@lilgreenbird lilgreenbird added the Enhancement An enhancement to the driver. Lower priority than bugs. label Aug 28, 2023
@rdicroce
Copy link
Contributor Author

I've done a brief survey and determined the following:

  • The JDBC spec says nothing about this.
  • A lot of databases do not support TIMESTAMP WITH TIME ZONE or equivalent. MySQL, SQLite, and Derby fall into this category.
  • jTDS is ancient and hasn't been updated in eons. The code doesn't appear to support Java 8 types at all.
  • Postgres throws an exception, as determined by looking at their tests.
  • H2 does what mssql-jdbc currently does, as determined by experimenting with it.
  • HSQLDB does what I'm proposing to make mssql-jdbc do, as determined by experimenting with it.

I'd be fine with saying the current behavior is the default, and my proposed behavior requires a connection property. If I update this PR to do that, do you think it would get merged?

@David-Engel
Copy link
Collaborator

Just to be transparent, adding public surface area (public APIs, connection properties, etc.) has a non-zero maintenance cost. So we try to get justification for adding those and see if there are others who desire the same functionality. (Of course, submitting the functionality yourself certainly makes it more likely to be accepted. 😄)

That said, what is the use case for using the driver this way?

When calling setObject(LocalDateTime) against a DATETIMEOFFSET column, the driver assumes the local offset, so rather than simply ignoring and truncating the offset from the database value, the current behavior of returning the time in the local offset seems perfectly reasonable.

If you are using DATETIMEOFFSET in the database, how come you aren't using OffsetDateTime on the Java side?

If you want to ignore the offset for some reason, why not just call getObject(col, OffsetDateTime.class) then OffsetDateTime#toLocalDate()?

If your app doesn't need offset at all, why not use a DATETIME2 column?

Regards,
David

@rdicroce
Copy link
Contributor Author

Just to be transparent, adding public surface area (public APIs, connection properties, etc.) has a non-zero maintenance cost. So we try to get justification for adding those and see if there are others who desire the same functionality. (Of course, submitting the functionality yourself certainly makes it more likely to be accepted. 😄)

That said, what is the use case for using the driver this way?

When calling setObject(LocalDateTime) against a DATETIMEOFFSET column, the driver assumes the local offset, so rather than simply ignoring and truncating the offset from the database value, the current behavior of returning the time in the local offset seems perfectly reasonable.

The current behavior might be reasonable, but it also violates the principle of least surprise. It's the only context I've seen where this behavior exists. As I said when I opened this PR, both OffsetDateTime#toLocalDateTime() and CASTing a DATETIMEOFFSET to DATETIME2 have the behavior of simply ignoring the offset and not adjusting anything. I expected this to work the same way, and it doesn't.

If you are using DATETIMEOFFSET in the database, how come you aren't using OffsetDateTime on the Java side?

If you want to ignore the offset for some reason, why not just call getObject(col, OffsetDateTime.class) then OffsetDateTime#toLocalDate()?

I can, at least in some code where I have direct control of this. There are other cases where a third-party library is interrogating the ResultSet and I can't do that. And, at the risk of repeating myself: as a Java developer, I expected that getObject(col, LocalDateTime.class) would yield the same result as getObject(col, OffsetDateTime.class).toLocalDateTime().

If your app doesn't need offset at all, why not use a DATETIME2 column?

There are some cases in the application where the time zone matters, and other cases where we want the local time.

I opened this PR mainly because the current behavior is surprising to me. But if nobody else agrees, I'll stop arguing and close it.

@David-Engel
Copy link
Collaborator

We discussed it and we are okay with adding this feature. To summarize the changes required:

  • Add a new connection property that makes the new behavior opt-in. I suggest IgnoreOffsetOnDateTimeOffsetConversion, default = false. Feel free to propose something better. (I dislike the long name, but can't think of anything better.)
  • Add tests that verify current and new behaviors when setting the connection property.

CC: @lilgreenbird, @Jeffery-Wasty, @tkyc to chime in if I'm forgetting anything.

@rdicroce
Copy link
Contributor Author

rdicroce commented Sep 6, 2023

Okay, sounds good. I'm about to go on vacation so it'll be a few weeks before I get back to this.

@rdicroce
Copy link
Contributor Author

@microsoft-github-policy-service agree company="Scientific Games"

I named the connection property javaCompatibleTimeConversion which is a little shorter and IMO explains it a little better. If you don't like it, I can change it.

Copy link
Contributor

@lilgreenbird lilgreenbird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we discussed with product mgmt your suggestion however IgnoreOffsetOnDateTimeOffsetConversion is preferred.

@rdicroce
Copy link
Contributor Author

This is ready to be reviewed.

tkyc
tkyc previously approved these changes Sep 27, 2023
Jeffery-Wasty
Jeffery-Wasty previously approved these changes Oct 13, 2023
tkyc
tkyc previously approved these changes Oct 13, 2023
@tkyc tkyc dismissed stale reviews from Jeffery-Wasty and themself via 04d7b85 October 18, 2023 22:29
@lilgreenbird lilgreenbird added this to the 12.5.0 milestone Oct 18, 2023
@lilgreenbird lilgreenbird merged commit 974e4a1 into microsoft:main Oct 31, 2023
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement An enhancement to the driver. Lower priority than bugs.
Projects
Status: Closed/Merged PRs
Development

Successfully merging this pull request may close these issues.

5 participants