Skip to content

Fix getObject() conversion of DATETIMEOFFSET to LocalDateTime#2204

Merged
lilgreenbird merged 7 commits intomicrosoft:mainfrom
rdicroce:ldt-fix
Oct 31, 2023
Merged

Fix getObject() conversion of DATETIMEOFFSET to LocalDateTime#2204
lilgreenbird merged 7 commits intomicrosoft:mainfrom
rdicroce:ldt-fix

Conversation

@rdicroce
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
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