<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Instant &#8211; Pauls Blog</title>
	<atom:link href="https://sterl.org/tag/instant/feed/" rel="self" type="application/rss+xml" />
	<link>https://sterl.org</link>
	<description></description>
	<lastBuildDate>Fri, 15 Oct 2021 10:58:08 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>Dates, DateTime, Timezone and the DB</title>
		<link>https://sterl.org/2017/10/dates-datetime-timezone-and-the-db/</link>
					<comments>https://sterl.org/2017/10/dates-datetime-timezone-and-the-db/#respond</comments>
		
		<dc:creator><![CDATA[Paul Sterl]]></dc:creator>
		<pubDate>Sun, 08 Oct 2017 12:08:00 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Pattern & Best Practice]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Best Practice]]></category>
		<category><![CDATA[Instant]]></category>
		<category><![CDATA[LocalDate]]></category>
		<category><![CDATA[LocalDateTime]]></category>
		<category><![CDATA[OffsetDateTime]]></category>
		<category><![CDATA[Pattern]]></category>
		<category><![CDATA[Timezone]]></category>
		<guid isPermaLink="false">https://sterl.org/?p=684</guid>

					<description><![CDATA[TL;DR Summary Java Type DB Type Description SAVE Long NUMBER Just using a long as milliseconds, between the current time and midnight, January 1, 1970 UTC is the most secure way. SAVE Instant NUMBER Using in Java an Instant and converting it into a NUMBER to save it in the DB will also avoid any&#8230;]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">TL;DR Summary</h2>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><th></th><th>Java Type</th><th>DB Type</th><th>Description</th></tr><tr><td><span class="badge badge-success">SAVE</span></td><td><code>Long</code></td><td><code>NUMBER</code></td><td>Just using a long as milliseconds, between the current time and midnight, January 1, 1970 UTC is the most secure way.</td></tr><tr><td><meta charset="utf-8"><span class="badge badge-success">SAVE</span></td><td><code>Instant</code></td><td><code>NUMBER</code></td><td>Using in Java an <code>Instant</code> and converting it into a <code>NUMBER</code> to save it in the DB will also avoid any issues you might encounter. APIs using the <a rel="noreferrer noopener" href="https://datatracker.ietf.org/doc/html/rfc3339" target="_blank">RFC3339</a> format usually also avoids any problems.</td></tr><tr><td><span class="badge badge-warning">CONFIGURE</span></td><td><meta charset="utf-8"><code>Instant</code></td><td><meta charset="utf-8"><code>TIMESTAMP</code></td><td>Ensure you set your JDBC driver to UTC, as it might cause issues saving and loading the date. e.g.: <code>spring.jpa.properties.hibernate.jdbc.time_zone=UTC</code></td></tr><tr><td><span class="badge badge-error">AVOID</span></td><td><code>OffsetDateTime</code></td><td><meta charset="utf-8"><code>TIMESTAMP</code></td><td>If an offset time zone is used, ensure the DB type support the time zone too.</td></tr><tr><td><meta charset="utf-8"><span class="badge badge-success">SAVE</span></td><td><meta charset="utf-8"><code>OffsetDateTime</code></td><td><meta charset="utf-8"><code>TIMESTAMP</code> <code>with timezone</code></td><td>A time stamp with zone e.g. <code>timestampz</code> in PostgreSQL combined with an <code>OffsetDateTime</code> is usually a save way to go too.</td></tr><tr><td><span class="badge badge-error">AVOID</span></td><td><code>LocalDateTime</code></td><td><meta charset="utf-8"><code>TIMESTAMP</code></td><td>Avoid the usage of local date or local date time. if this is not possible use always a time zone and set it in the jdbc driver e.g.:<br><code>ZonedDateTime.now(ZoneOffset.UTC)</code><br><code>spring.jpa.properties.hibernate.jdbc.time_zone=UTC</code><br><code>-Duser.timezone=UTC</code></td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Problem </h2>



<p>Working with dates and a DB usually tends to create issues: </p>



<ul class="wp-block-list"><li>Which type to use?</li><li>Which type to save in the DB?</li><li>How to handle time zones?</li><li>How to avoid moving times?</li><li>How to show the time in the correct time zone of the user?</li></ul>



<h2 class="wp-block-heading">Scenario</h2>



<figure class="wp-block-image size-large"><a href="https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone.png"><img fetchpriority="high" decoding="async" width="1024" height="583" src="https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-1024x583.png" alt="" class="wp-image-731" srcset="https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-1024x583.png 1024w, https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-300x171.png 300w, https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-768x437.png 768w, https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone.png 1374w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><figcaption>Times zones scenario</figcaption></figure>



<h3 class="wp-block-heading">Our test entity</h3>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text/x-java&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Java&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;java&quot;}">@Entity @Data
public class Person {
  @Id
  private Long id;
    
  private long longDate;
    
  private Instant instantDate;
    
  private LocalDateTime localDateTime;
    
  private OffsetDateTime offsetDateTime;
    
  @Column(columnDefinition = &quot;timestamptz&quot;) // we use here postgresql
  private OffsetDateTime offsetDateTimeZone;
}</pre></div>



<h3 class="wp-block-heading">Given</h3>



<ul class="wp-block-list"><li>We use the entity above</li><li>We use a PostgreSQL DB</li><li>Service instance 1 accesses the DB using the time zone GMZ / UTC</li><li>Service instance 2 accesses the DB using the time zone GMZ + 1</li><li>We have one or more users with direct DB access from their local computer using the time zone Europa/Berlin</li><li>We have currently winter time as it is october</li></ul>



<h3 class="wp-block-heading">When</h3>



<ul class="wp-block-list"><li>Service instance 1 write a record into the DB</li></ul>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;ruby&quot;,&quot;mime&quot;:&quot;text/x-ruby&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Ruby&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;ruby&quot;}">// Instance 1: GMT WRITE
Person(
  longDate           = 1507751058820, 
  instantDate        = 2017-10-11T19:44:18.820418Z, 
  localDateTime      = 2017-10-11T19:44:18.969400, 
  offsetDateTime     = 2017-10-11T19:44:18.969438Z, 
  offsetDateTimeZone = 2017-10-11T19:44:18.969455Z
)</pre></div>



<h3 class="wp-block-heading">Then</h3>



<ul class="wp-block-list"><li>Service Instance 2 reads the data</li></ul>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;ruby&quot;,&quot;mime&quot;:&quot;text/x-ruby&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Ruby&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;ruby&quot;}">-- Instance 2: GMT+01:00 READ
Person(
  longDate           = 1507751058820, // correct
  instantDate        = 2017-10-11T18:44:18.820418Z, // wrong by one hour
  localDateTime      = 2017-10-11T19:44:18.969400,  // wrong by one hour as local time zone is +1
  offsetDateTime     = 2017-10-11T19:44:18.969438+01:00, // wrong by one hour too late
  offsetDateTimeZone = 2017-10-11T20:44:18.969455+01:00  // correct
)</pre></div>



<ul class="wp-block-list"><li>A user looks into the DB</li></ul>



<figure class="wp-block-image size-large"><a href="https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-user-view.png"><img decoding="async" width="1024" height="68" src="https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-user-view-1024x68.png" alt="" class="wp-image-739" srcset="https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-user-view-1024x68.png 1024w, https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-user-view-300x20.png 300w, https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-user-view-768x51.png 768w, https://sterl.org/wp-content/uploads/2021/10/java-date-time-zone-user-view.png 1384w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Only the <code>timestampz</code>, number and instant column value looks okay. Storing an offset date into a type which doesn&#8217;t support the time zone leads here to an error.</p>



<h3 class="wp-block-heading">Change the JDBC session to UTC</h3>



<p>If the JDBC session is adjusted to use UTC instead of the JVM time zone using e.g. <meta charset="utf-8"><code>spring.jpa.properties.hibernate.jdbc.time_zone=UTC</code> we end up with the following result:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;ruby&quot;,&quot;mime&quot;:&quot;text/x-ruby&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Ruby&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;ruby&quot;}">// Instance 1: GMT WRITE
Person(
  longDate           = 1507751058820, 
  instantDate        = 2017-10-11T19:44:18.820418Z, 
  localDateTime      = 2017-10-11T19:44:18.969400, 
  offsetDateTime     = 2017-10-11T19:44:18.969438Z, 
  offsetDateTimeZone = 2017-10-11T19:44:18.969455Z
)
-- Instance 2: GMT+01:00 READ
Person(
  longDate           = 1507751058820, // correct
  instantDate        = 2017-10-11T19:44:18.820418Z, // now correct
  localDateTime      = 2017-10-11T20:44:18.969400,  // now also correct
  offsetDateTime     = 2017-10-11T20:44:18.969438+01:00, // now also correct
  offsetDateTimeZone = 2017-10-11T20:44:18.969455+01:00  // now also correct
)</pre></div>



<p>Now all dates are okay, if all our instances really work with the same JDBC session time zone.</p>



<h2 class="wp-block-heading">Avoid any time Zones in the business logic</h2>



<p>Mathematically the time zone isn&#8217;t relevant assuming any date in the backend is saved and transferred as UTC date time with now time zone at all.</p>



<div class="bs-callout bs-callout-info"><strong>As a rule of thumb:</strong> The simplest and safest method is to remove any time zone from the backend code. If we have a time zone in the code, we need it in the DB too.</div>



<p>Save the date time as UTC <code>long</code>, which is anyway the simplest method to compare dates to each other. (<em>Means as milliseconds, between the current time and midnight, January 1, 1970 UTC.</em>)</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text/x-java&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Java&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;java&quot;}">class Pojo {
  String foo;
  long dateTimeInMs = System.currentTimeMillis();
  long dateTimeInS = Instant.now().getEpochSecond();
}</pre></div>



<h2 class="wp-block-heading">Even in the DB using a long / NUMBER has several advantages</h2>



<ul class="wp-block-list"><li>Any direct usage of the DB itself is hindered</li><li>Date Time conversion by the JDBC driver is avoided</li><li>A time zone cannot be introduced or assumed</li><li>Clear also in external APIs using the Service</li><li>The precision can be selected based on the domain, in most use cases seconds is enough</li></ul>



<h2 class="wp-block-heading">Instant in Java 8 or later</h2>



<p>A valid alternative solution is to use Java <code>Instant</code>, which is in the end a wrapped UTC <code>long</code> and provides some further methods. It can be saved either as <code>NUMBER</code> or <code>TIMESTAMP</code> in the DB. But as soon as a real date type is used we have also to provide a time zone information in our JDBC URL e.g.:</p>



<ul class="wp-block-list"><li>e.g. using <code>serverTimezone=UTC</code></li><li>or <code>spring.jpa.properties.hibernate.jdbc.time_zone=UTC</code></li></ul>



<h2 class="wp-block-heading">OffsetDateTime</h2>



<p>Many projects have chosen  an offset date time type to avoid any issues with the time zone. The problem is, that it usually doesn&#8217;t address the issue if for some reason the time zone of your system changes.</p>



<h3 class="wp-block-heading">Running save and load in different Time zones</h3>



<h2 class="wp-block-heading">Avoid Local Date Time</h2>



<p>Local date time tends to surprise the people each time, even if it states, that the e.g. <code>LocalDate</code> represents a date without a time zone, same applies to <code>LocalDateTime</code>, it uses behind the scenes the system time zone:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text/x-java&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Java&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;java&quot;}">    public static LocalDate now() {
        return now(Clock.systemDefaultZone());
    }

    public static LocalDateTime now() {
        return now(Clock.systemDefaultZone());
    }</pre></div>



<p>Which leads to the problem, that we have a time which was build using a time zone which is lost as soon as we have only the object. The value depends on the OS time zone which leads to interesting problems in a hosted server environment. Imagine e.g. that one node of the cluster has a different time zone because of some configuration issue in the infrastructure.</p>



<p>Having just the following simple test let us review the results, take in mind that my local time zone would be GMT+2 but currently we have wintertime, GMT+1.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text/x-java&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Java&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;java&quot;}">    @ParameterizedTest
    @ValueSource(strings = {&quot;&quot;, &quot;GMT+1&quot;, &quot;GMT+6&quot;})
    void test(String timeZone) {
        if (timeZone.length() != 0) {
            TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
        }
        System.out.println(java.util.TimeZone.getDefault());
        System.out.println(&quot;Instant:        &quot; + Instant.now());
        System.out.println(&quot;LocalDateTime:  &quot; + LocalDateTime.now());
        System.out.println(&quot;OffsetDateTime: &quot; + OffsetDateTime.now());
    }</pre></div>



<p>Already this very simple example generates here with <code>LocalDateTime</code> three different results. Even if the offset looks correct.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;lua&quot;,&quot;mime&quot;:&quot;text/x-lua&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Lua&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;lua&quot;}">ZoneInfo[id=&quot;Europe/Berlin&quot;,offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Instant:        2017-10-10T09:54:45.370020Z
LocalDateTime:  2017-10-10T11:54:45.379338
OffsetDateTime: 2017-10-10T11:54:45.380626+02:00
ZoneInfo[id=&quot;GMT+01:00&quot;,offset=3600000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Instant:        2017-10-10T09:54:45.394314Z
LocalDateTime:  2017-10-10T10:54:45.394416
OffsetDateTime: 2021-10-10T10:54:45.394455+01:00
ZoneInfo[id=&quot;GMT+06:00&quot;,offset=21600000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Instant:        2017-10-10T09:54:45.397258Z
LocalDateTime:  2017-10-10T15:54:45.397353
OffsetDateTime: 2017-10-10T15:54:45.397406+06:00
ZoneInfo[id=&quot;GMT&quot;,offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Instant:        2017-10-10T09:54:45.399973Z
LocalDateTime:  2017-10-10T09:54:45.400040
OffsetDateTime: 2017-10-10T09:54:45.400076Z</pre></div>



<p>If the code base is already based on local date times and a refactoring isn&#8217;t possible the following actions could be applied to reduce the risk of moving dates and times:</p>



<ul class="wp-block-list"><li>Check the current time zone in production, as all dates may require an update</li><li>Change all dates to UTC/ GMT+0 in the DB</li><li>Apply a fixed time zone to the JVM: <code>-Duser.timezone=UTC</code></li><li>Apply a fixed time zone in the JDBC URL (since hibernate 5.2 a hibernate property is available)</li><li>Verify that the date isn&#8217;t displayed to the user, or a time zone conversion is applied</li></ul>



<h2 class="wp-block-heading">Links</h2>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" href="https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm" target="_blank">https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm</a></li></ul>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://sterl.org/2017/10/dates-datetime-timezone-and-the-db/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
