<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<base href="https://hibernate.atlassian.net">
<title>Message Title</title>
</head>
<body class="jira" style="color: #333333; font-family: Arial, sans-serif; font-size: 14px; line-height: 1.429">
<table id="background-table" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0; background-color: #f5f5f5; border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0" bgcolor="#f5f5f5">
<!-- header here -->
<tbody>
<tr>
<td id="header-pattern-container" style="padding: 0; border-collapse: collapse; padding: 10px 20px">
<table id="header-pattern" cellspacing="0" cellpadding="0" border="0" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0">
<tbody>
<tr>
<td id="header-avatar-image-container" valign="top" style="padding: 0; border-collapse: collapse; vertical-align: top; width: 32px; padding-right: 8px" width="32"> <img id="header-avatar-image" class="image_fix" src="https://avatar-cdn.atlassian.com/2a8bdd4ffd282b7185c74b52ab452617?s=48&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F2a8bdd4ffd282b7185c74b52ab452617%3Fd%3Dmm%26s%3D48%26noRedirect%3Dtrue" height="32" width="32" border="0" style="border-radius: 3px; vertical-align: top"> </td>
<td id="header-text-container" valign="middle" style="padding: 0; border-collapse: collapse; vertical-align: middle; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 1px"> <a class="user-hover" rel="yrodiere" style="color:#6c797f;; color: #3b73af; text-decoration: none" id="email_yrodiere" href="https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%3A58fa1ced-171a-4c00-97e8-5d70d442cc4b"> Yoann Rodière </a> <strong>commented</strong> on <a href="https://hibernate.atlassian.net/browse/HHH-13266?atlOrigin=eyJpIjoiOTY1ZTVjZWJmZDEwNDY2YWJjYjc0ZTA0NjZkZWU3OGYiLCJwIjoiaiJ9" style="color: #3b73af; text-decoration: none"><img src="cid:jira-generated-image-avatar-bcab93c3-275b-48d6-a7c0-e37823a58af3" height="16" width="16" border="0" align="absmiddle" alt="Bug"> HHH-13266</a> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td id="email-content-container" style="padding: 0; border-collapse: collapse; padding: 0 20px">
<table id="email-content-table" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0; border-spacing: 0; border-collapse: separate">
<tbody>
<tr>
<!-- there needs to be content in the cell for it to render in some clients -->
<td class="email-content-rounded-top mobile-expand" style="padding: 0; border-collapse: collapse; color: #ffffff; padding: 0 15px 0 16px; height: 15px; background-color: #ffffff; border-left: 1px solid #cccccc; border-top: 1px solid #cccccc; border-right: 1px solid #cccccc; border-bottom: 0; border-top-right-radius: 5px; border-top-left-radius: 5px; height: 10px; line-height: 10px; padding: 0 15px 0 16px; mso-line-height-rule: exactly" height="10" bgcolor="#ffffff"> </td>
</tr>
<tr>
<td class="email-content-main mobile-expand " style="padding: 0; border-collapse: collapse; border-left: 1px solid #cccccc; border-right: 1px solid #cccccc; border-top: 0; border-bottom: 0; padding: 0 15px 0 16px; background-color: #ffffff" bgcolor="#ffffff">
<table class="page-title-pattern" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0">
<tbody>
<tr>
<td style="vertical-align: top;; padding: 0; border-collapse: collapse; padding-right: 5px; font-size: 20px; line-height: 30px; mso-line-height-rule: exactly" class="page-title-pattern-header-container"> <span class="page-title-pattern-header" style="font-family: Arial, sans-serif; padding: 0; font-size: 20px; line-height: 30px; mso-text-raise: 2px; mso-line-height-rule: exactly; vertical-align: middle"> <a href="https://hibernate.atlassian.net/browse/HHH-13266?atlOrigin=eyJpIjoiOTY1ZTVjZWJmZDEwNDY2YWJjYjc0ZTA0NjZkZWU3OGYiLCJwIjoiaiJ9" style="color: #3b73af; text-decoration: none">Re: LocalDateTime values are wrong</a> </span> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td id="text-paragraph-pattern-top" class="email-content-main mobile-expand comment-top-pattern" style="padding: 0; border-collapse: collapse; border-left: 1px solid #cccccc; border-right: 1px solid #cccccc; border-top: 0; border-bottom: 0; padding: 0 15px 0 16px; background-color: #ffffff; border-bottom: none; padding-bottom: 0" bgcolor="#ffffff">
<table class="text-paragraph-pattern" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 2px">
<tbody>
<tr>
<td class="text-paragraph-pattern-container mobile-resize-text " style="padding: 0; border-collapse: collapse; padding: 0 0 10px"> <p style="margin: 10px 0 0; margin-top: 0">There seems to be a problem for some date/times around 1900, indeed.</p> <p style="margin: 10px 0 0">And it also seems to disappear if we change the conversion strategy in <tt>LocalDateTimeJavaDescriptor</tt> to rely on <tt>Timestamp.valueOf(LocalDate)</tt>/<tt>Timestamp.toLocalDate()</tt> instead of using an intermediary instant.</p> <p style="margin: 10px 0 0">It's not as simple as the old strategy being simply incorrect, however. I investigated for quite some time, and I'm still struggling to make sense of this, but I will try to explain the best I can.</p> <p style="margin: 10px 0 0">I created two test cases in a GitHub repository: <a href="https://github.com/yrodiere/jdk-bugs" class="external-link" rel="nofollow" style="color: #3b73af; text-decoration: none">https://github.com/yrodiere/jdk-bugs</a></p>
<ul>
<li><tt>TimestampWriteThenReadTest</tt> reproduces what happens in Hibernate and the JDBC drivers when LocalDateTimes are written to/read from the database. It uses the JDK exclusively (no Hibernate, no JDBC), and we get unexpected behavior too.</li>
<li><tt>JdkChronoFieldsToEpochMillisConversionTest</tt> explores the bug in more details by testing the conversion from chrono fields (year/month/etc.) to milliseconds since the epoch in both Timestamp and Calendar. Surprise: it also fails. At the very least the results are inconsistent with the java.time APIs (hence the problems when converting from the date/time APIs).</li>
</ul> <h3 style="margin: 10px 0 0; font-size: 16px; line-height: 1.563; margin: 30px 0 0"><a name="What%27stherootoftheproblem%3F" style="color: #3b73af; text-decoration: none"></a>What's the root of the problem?</h3> <p style="margin: 10px 0 0">So the first conclusion is the problem really seems to lie in the JDK. I am not sure if it's a bug or just yet another quirk of the legacy date/time APIs, but it's definitely in the JDK.</p> <p style="margin: 10px 0 0">The second conclusion is that the problem seems to be about converting chrono fields to/from milliseconds since the epoch. When you set a Calendar to a number of milliseconds, then get that number back, you're fine; when you set its chrono fields then get back these chrono fields, you're fine; but as soon as you mix them, the bug shallows. Same idea for the timestamp.<br> Since JDBC drivers rely on these mechanics to build a string representation of the timestamp, ultimately they are all affected by the problem.</p> <p style="margin: 10px 0 0">In more details, it seems the mechanics responsible for translating a number of millisecond since the epoch into a year/month/day/hours/minutes/etc. (and back) in java.util produce different results from java.time. Some examples:</p>
<ul>
<li>Converting 1900-01-01T00:59:59 to milliseconds since the epoch with Europe/Amsterdam as default timezone will give -2208986373000 (consistent with java.time)</li>
<li>Converting -2208986373000 to chrono fields with Europe/Amsterdam as default timezone will give 1900-01-01T00:59:59 (consistent with java.time)</li>
<li>Converting 1900-01-01T00:19:31 to milliseconds since the epoch with Europe/Amsterdam as default timezone will give -2208991229000 (<b>inconsistent</b> with java.time, expected -2208988801000)</li>
<li>Converting -2209027229000 to chrono fields with Europe/Amsterdam as default timezone will give 1900-01-31T00:19:31 (<b>inconsistent</b> with java.time, expected 1899-12-31T23:39:03)</li>
<li>Converting -2208988801000 to chrono fields with Europe/Amsterdam as default timezone will give 1900-01-01T00:59:59 (<b>inconsistent</b> with java.time, expected 1900-01-01T00:19:31)</li>
</ul> <h3 style="margin: 10px 0 0; font-size: 16px; line-height: 1.563; margin: 30px 0 0"><a name="Howdoesthisaffectus%3F" style="color: #3b73af; text-decoration: none"></a>How does this affect us?</h3> <p style="margin: 10px 0 0">As you can see, on top of being inconsistent with java.time, it's sometimes inconsistent with itself:</p>
<ol>
<li>1900-01-01T00:19:31 is converted to -2208991229000 (wrong locally), which is converted back to 1900-01-01T00:19:31 (wrong locally, correct overall)</li>
<li>-2208988801000 is converted to 1900-01-01T00:59:59 (wrong locally), which is converted back to -2208986373000 (correct locally, wrong overall)</li>
</ol> <p style="margin: 10px 0 0">This internal inconsistency explains why our "old" strategy for LocalDateTime conversion did not work, while the new one does.<br> Conversion inconsistencies happen this way:</p>
<ul>
<li>They only appear before a certain moment (which depends on the time zone). Let's call this moment "breaking point".</li>
<li>When converting from chrono fields to millisecond since the epoch, the result is a number <b>before</b> the one expected (drift backward in time)</li>
<li>When converting from milliseconds since the epoch to chrono fields, the result is a date <b>after</b> the one expected (drift forward in time)</li>
</ul> <p style="margin: 10px 0 0">As a result, if you convert <b>first</b> from milliseconds to chrono fields, <b>then</b> from chrono fields to milliseconds, you run the risk of the original value being affected by the drift, but not the "drifted", intermediary value, in which case the first conversion will drift the value, but not the second conversion, and the final result will wrong overall. That is what used to happen, and what is demonstrated it the example number 2 above.</p> <p style="margin: 10px 0 0">On the other hand, if you convert first from chrono fields to milliseconds, then from milliseconds to chrono fields, then the intermediary value will be wrong too, but conversions will lead to opposing drifts, the drifts will compensate and the final result will be correct overall. This is what will happen with the new strategy for LocalDateTime conversion, and what is demonstrated it the example number 1 above.</p> <h3 style="margin: 10px 0 0; font-size: 16px; line-height: 1.563; margin: 30px 0 0"><a name="Misc" style="color: #3b73af; text-decoration: none"></a>Misc</h3> <p style="margin: 10px 0 0">Some more information below that I collected while investigating. Not necessarily useful if you read the information above, but maybe I'll need it later, so I'll just leave it here.</p> <p style="margin: 10px 0 0">The data flow of the "new" strategy looks like this:</p>
<div class="preformatted panel" style="border-width: 1px;; border: 1px solid #cccccc; background: #f5f5f5; font-size: 12px; line-height: 1.333; font-family: monospace; border: 1px solid #cccccc; -moz-border-radius: 3px; border-radius: 3px; margin: 9px 0">
<div class="preformattedContent panelContent" style="padding: 9px 12px">
<pre style="margin: 10px 0 0; margin-top: 0; max-height: 30em; overflow: auto; white-space: pre-wrap; word-wrap: normal; white-space: pre; word-break: normal; word-wrap: break-word; word-break: break-all; white-space: pre-wrap"> LocalDateTime (expected value before breaking point)
=[java.time]=> year, month, etc. (expected value before breaking point)
=[Timestamp contstructor, using Calendar, drift]=> Millis since epoch (unexpected value before breaking point)
=[JDBC driver, using Calendar, drift]=> year, month, etc. (expected value before breaking point)
=[SQL]=> Database
</pre>
</div>
</div> <p style="margin: 10px 0 0">And back from the database:</p>
<div class="preformatted panel" style="border-width: 1px;; border: 1px solid #cccccc; background: #f5f5f5; font-size: 12px; line-height: 1.333; font-family: monospace; border: 1px solid #cccccc; -moz-border-radius: 3px; border-radius: 3px; margin: 9px 0">
<div class="preformattedContent panelContent" style="padding: 9px 12px">
<pre style="margin: 10px 0 0; margin-top: 0; max-height: 30em; overflow: auto; white-space: pre-wrap; word-wrap: normal; white-space: pre; word-break: normal; word-wrap: break-word; word-break: break-all; white-space: pre-wrap"> Database
=[SQL]=> year, month, etc. (expected value before breaking point)
=[JDBC driver, using Calendar, drift]=> Millis since epoch (unexpected value before breaking point)
=[Timestamp contstructor, using Calendar, drift]=> year, month, etc. (expected value before breaking point)
=[java.time]=> LocalDateTime
</pre>
</div>
</div> <p style="margin: 10px 0 0">The data flow of the "old" method looks like this:</p>
<div class="preformatted panel" style="border-width: 1px;; border: 1px solid #cccccc; background: #f5f5f5; font-size: 12px; line-height: 1.333; font-family: monospace; border: 1px solid #cccccc; -moz-border-radius: 3px; border-radius: 3px; margin: 9px 0">
<div class="preformattedContent panelContent" style="padding: 9px 12px">
<pre style="margin: 10px 0 0; margin-top: 0; max-height: 30em; overflow: auto; white-space: pre-wrap; word-wrap: normal; white-space: pre; word-break: normal; word-wrap: break-word; word-break: break-all; white-space: pre-wrap"> LocalDateTime (expected value before breaking point)
=[java.time]=> Millis since epoch (expected value before breaking point)
=[Timestamp contstructor, not using Calendar]=> Millis since epoch (expected value before breaking point)
=[JDBC driver, using Calendar, drift]=> year, month, etc. (unexpected value after breaking point)
=[SQL]=> Database
</pre>
</div>
</div> <p style="margin: 10px 0 0">And back from the database:</p>
<div class="preformatted panel" style="border-width: 1px;; border: 1px solid #cccccc; background: #f5f5f5; font-size: 12px; line-height: 1.333; font-family: monospace; border: 1px solid #cccccc; -moz-border-radius: 3px; border-radius: 3px; margin: 9px 0">
<div class="preformattedContent panelContent" style="padding: 9px 12px">
<pre style="margin: 10px 0 0; margin-top: 0; max-height: 30em; overflow: auto; white-space: pre-wrap; word-wrap: normal; white-space: pre; word-break: normal; word-wrap: break-word; word-break: break-all; white-space: pre-wrap"> Database
=[SQL]=> year, month, etc. (unexpected value after breaking point)
=[JDBC driver, using Calendar, no drift]=> Millis since epoch (unexpected value after breaking point)
=[Timestamp contstructor, not using Calendar]=> Millis since epoch (unexpected value after breaking point)
=[java.time]=> LocalDateTime
</pre>
</div>
</div> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td class="email-content-main mobile-expand " style="padding: 0; border-collapse: collapse; border-left: 1px solid #cccccc; border-right: 1px solid #cccccc; border-top: 0; border-bottom: 0; padding: 0 15px 0 16px; background-color: #ffffff" bgcolor="#ffffff"> <script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "EmailMessage",
"description": "View Issue",
"potentialAction": {
"@type": "ViewAction",
"target": "https://hibernate.atlassian.net/browse/HHH-13266?inbox=true&focusedCommentId=104778&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-104778",
"name": "View Comment"
},
"publisher": {
"@type": "Organization",
"name": "Atlassian",
"url": "https://www.atlassian.com"
}
}
</script>
<table id="actions-pattern" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 1px">
<tbody>
<tr>
<td id="actions-pattern-container" valign="middle" style="padding: 0; border-collapse: collapse; padding: 10px 0 10px 24px; vertical-align: middle; padding-left: 0">
<table align="left" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0">
<tbody>
<tr>
<td class="actions-pattern-action-icon-container" style="padding: 0; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 0; vertical-align: middle"> <a href="https://hibernate.atlassian.net/browse/HHH-13266#add-comment?atlOrigin=eyJpIjoiOTY1ZTVjZWJmZDEwNDY2YWJjYjc0ZTA0NjZkZWU3OGYiLCJwIjoiaiJ9" target="_blank" title="Add Comment" style="color: #3b73af; text-decoration: none"> <img class="actions-pattern-action-icon-image" src="cid:jira-generated-image-static-comment-icon-a38e9073-a698-4515-a944-e97053ed79a8" alt="Add Comment" title="Add Comment" height="16" width="16" border="0" style="vertical-align: middle"> </a> </td>
<td class="actions-pattern-action-text-container" style="padding: 0; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 4px; padding-left: 5px"> <a href="https://hibernate.atlassian.net/browse/HHH-13266#add-comment?atlOrigin=eyJpIjoiOTY1ZTVjZWJmZDEwNDY2YWJjYjc0ZTA0NjZkZWU3OGYiLCJwIjoiaiJ9" target="_blank" title="Add Comment" style="color: #3b73af; text-decoration: none">Add Comment</a> </td>
</tr>
</tbody>
</table> </td>
</tr>
</tbody>
</table> </td>
</tr>
<!-- there needs to be content in the cell for it to render in some clients -->
<tr>
<td class="email-content-rounded-bottom mobile-expand" style="padding: 0; border-collapse: collapse; color: #ffffff; padding: 0 15px 0 16px; height: 5px; line-height: 5px; background-color: #ffffff; border-top: 0; border-left: 1px solid #cccccc; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; mso-line-height-rule: exactly" height="5" bgcolor="#ffffff"> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td id="footer-pattern" style="padding: 0; border-collapse: collapse; padding: 12px 20px">
<table id="footer-pattern-container" cellspacing="0" cellpadding="0" border="0" style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0">
<tbody>
<tr>
<td id="footer-pattern-mobile-separated-links" class="mobile-resize-text" width="100%" colspan="2" style="padding: 0; border-collapse: collapse; color: #999999; font-size: 12px; line-height: 18px; font-family: Arial, sans-serif; mso-line-height-rule: exactly; mso-text-raise: 2px"> Get Jira notifications on your phone! Download the Jira Cloud app for <a href="https://play.google.com/store/apps/details?id=com.atlassian.android.jira.core&referrer=utm_source%3DNotificationLink%26utm_medium%3DEmail" style="color: #3b73af; text-decoration: none">Android</a> or <a href="https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailNotificationLink&mt=8" style="color: #3b73af; text-decoration: none">iOS</a>
<hr> </td>
</tr>
<tr>
<td id="footer-pattern-text" class="mobile-resize-text" width="100%" style="padding: 0; border-collapse: collapse; color: #999999; font-size: 12px; line-height: 18px; font-family: Arial, sans-serif; mso-line-height-rule: exactly; mso-text-raise: 2px"> This message was sent by Atlassian Jira <span id="footer-build-information">(v1001.0.0-SNAPSHOT#100098-<span title="ec9318169c8a5f085e6d16833bc88c3162636f3d" data-commit-id="ec9318169c8a5f085e6d16833bc88c3162636f3d}">sha1:ec93181</span>)</span> </td>
<td id="footer-pattern-logo-desktop-container" valign="top" style="padding: 0; border-collapse: collapse; padding-left: 20px; vertical-align: top">
<table style="border-collapse: collapse; mso-table-lspace: 0; mso-table-rspace: 0">
<tbody>
<tr>
<td id="footer-pattern-logo-desktop-padding" style="padding: 0; border-collapse: collapse; padding-top: 3px; opacity: 0.150"> <img id="footer-pattern-logo-desktop" src="cid:jira-generated-image-static-footer-desktop-logo-04e10ff4-02cb-4560-87cb-a06a77162222" alt="Atlassian logo" title="Atlassian logo" width="192" height="24" class="image_fix"> </td>
</tr>
</tbody>
</table> </td>
</tr>
</tbody>
</table> </td>
</tr>
</tbody>
</table>
<img border="0" width="1" height="1" alt="" src="http://atlassian.et.e.sparkpost.com/q/K2wDRl8qVCJmfpVK2-T_mw~~/AAAAAQA~/RgReV5qpPlcLYXRsYXNzaWFudXNCCgAAKWd2XC--MGFSIGhpYmVybmF0ZS1pc3N1ZXNAbGlzdHMuamJvc3Mub3JnWAQAAAAE">
</body>
</html>