title: How to use java.time.ZonedDateTime / LocalDateTime in p:calendar
body: <p>I had been using Joda Time for date-time manipulation in a Java EE application in which a string representation of date-time submitted by the associated client had been converted using the following conversion routine before submitting it to a database i.e. in the <code>getAsObject()</code> method in a JSF converter.</p>
  
  <pre><code>org.joda.time.format.DateTimeFormatter formatter = org.joda.time.format.DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss a Z").withZone(DateTimeZone.UTC);
  DateTime dateTime = formatter.parseDateTime("05-Jan-2016 03:04:44 PM +0530");
  
  System.out.println(formatter.print(dateTime));
  </code></pre>
  
  <p>The local time zone given is 5 hours and 30 minutes ahead of <code>UTC</code> / <code>GMT</code>. Therefore, the conversion to <code>UTC</code> should deduct 5 hours and 30 minutes from the date-time given which happens correctly using Joda Time. It displays the following output as expected.</p>
  
  <pre><code>05-Jan-2016 09:34:44 AM +0000
  </code></pre>
  
  <p>&#9658; The time zone offset <code>+0530</code> in place of <code>+05:30</code> has been taken because it is dependent upon <code>&lt;p:calendar&gt;</code> which submits a zone offset in this format. It does not seem possible to change this behaviour of <code>&lt;p:calendar&gt;</code> (This question itself would not have been needed otherwise).</p>
  
  <hr>
  
  <p>The same thing is however broken, if attempted using the Java Time API in Java 8.</p>
  
  <pre><code>java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a Z").withZone(ZoneOffset.UTC);
  ZonedDateTime dateTime = ZonedDateTime.parse("05-Jan-2016 03:04:44 PM +0530", formatter);
  
  System.out.println(formatter.format(dateTime));
  </code></pre>
  
  <p>It unexpectedly displays the following incorrect output.</p>
  
  <pre><code>05-Jan-2016 03:04:44 PM +0000
  </code></pre>
  
  <p>Obviously, the date-time converted is not according to <code>UTC</code> in which it is supposed to convert.</p>
  
  <p>It requires the following changes to be adopted for it to work correctly.</p>
  
  <pre><code>java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a z").withZone(ZoneOffset.UTC);
  ZonedDateTime dateTime = ZonedDateTime.parse("05-Jan-2016 03:04:44 PM +05:30", formatter);
  
  System.out.println(formatter.format(dateTime));
  </code></pre>
  
  <p>Which in turn displays the following.</p>
  
  <pre><code>05-Jan-2016 09:34:44 AM Z
  </code></pre>
  
  <p><sub><code>Z</code> has been replaced with <code>z</code> and <code>+0530</code> has been replaced with <code>+05:30</code>.</sub></p>
  
  <p>Why these two APIs have a different behaviour in this regard has been ignored wholeheartedly in this question.</p>
  
  <p>What middle way approach can be considered for <code>&lt;p:calendar&gt;</code> and Java Time in Java 8 to work consistently and coherently though <code>&lt;p:calendar&gt;</code> internally uses <code>SimpleDateFormat</code> along with <code>java.util.Date</code>?</p>
  
  <hr>
  
  <p>The unsuccessful test scenario in JSF.</p>
  
  <p>The converter :</p>
  
  <pre><code>@FacesConverter("dateTimeConverter")
  public class DateTimeConverter implements Converter {
  
      @Override
      public Object getAsObject(FacesContext context, UIComponent component, String value) {
          if (value == null || value.isEmpty()) {
              return null;
          }
  
          try {
              return ZonedDateTime.parse(value, DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a Z").withZone(ZoneOffset.UTC));
          } catch (IllegalArgumentException | DateTimeException e) {
              throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Message"), e);
          }
      }
  
      @Override
      public String getAsString(FacesContext context, UIComponent component, Object value) {
          if (value == null) {
              return "";
          }
  
          if (!(value instanceof ZonedDateTime)) {
              throw new ConverterException("Message");
          }
  
          return DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a z").withZone(ZoneId.of("Asia/Kolkata")).format(((ZonedDateTime) value));
          // According to a time zone of a specific user.
      }
  }
  </code></pre>
  
  <p>XHTML having <code>&lt;p:calendar&gt;</code>.</p>
  
  <pre><code>&lt;p:calendar  id="dateTime"
               timeZone="Asia/Kolkata"
               pattern="dd-MMM-yyyy hh:mm:ss a Z"
               value="#{bean.dateTime}"
               showOn="button"
               required="true"
               showButtonPanel="true"
               navigator="true"&gt;
      &lt;f:converter converterId="dateTimeConverter"/&gt;
  &lt;/p:calendar&gt;
  
  &lt;p:message for="dateTime"/&gt;
  
  &lt;p:commandButton value="Submit" update="display" actionListener="#{bean.action}"/&gt;&lt;br/&gt;&lt;br/&gt;
  
  &lt;h:outputText id="display" value="#{bean.dateTime}"&gt;
      &lt;f:converter converterId="dateTimeConverter"/&gt;
  &lt;/h:outputText&gt;
  </code></pre>
  
  <p><sub>The time zone is fully transparently dependent upon the user's current time zone.</sub></p>
  
  <p>The bean having nothing other than a single property.</p>
  
  <pre><code>@ManagedBean
  @ViewScoped
  public class Bean implements Serializable {
  
      private ZonedDateTime dateTime; // Getter and setter.
      private static final long serialVersionUID = 1L;
  
      public Bean() {}
  
      public void action() {
          // Do something.
      }
  }
  </code></pre>
  
  <p>This will work in an unexpected way as demonstrated in the second last example / middle in the first three code snippets.</p>
  
  <p>Specifically, if you enter <code>05-Jan-2016 12:00:00 AM +0530</code>, it will redisplay <code>05-Jan-2016 05:30:00 AM IST</code> because the original conversion of <code>05-Jan-2016 12:00:00 AM +0530</code> to <code>UTC</code> in the converter fails.</p>
  
  <p>Conversion from a local time zone whose offset is <code>+05:30</code> to <code>UTC</code> and then conversion from <code>UTC</code> back to that time zone must obviously redisplay the same date-time as inputted through the calendar component which is the rudimentary functionality of the converter given.</p>
  
  <hr>
  
  <p><strong>Update:</strong></p>
  
  <p>The JPA converter converting to and from <code>java.sql.Timestamp</code> and <code>java.time.ZonedDateTime</code>.</p>
  
  <pre><code>import java.sql.Timestamp;
  import java.time.ZoneOffset;
  import java.time.ZonedDateTime;
  import javax.persistence.AttributeConverter;
  import javax.persistence.Converter;
  
  @Converter(autoApply = true)
  public final class JodaDateTimeConverter implements AttributeConverter&lt;ZonedDateTime, Timestamp&gt; {
  
      @Override
      public Timestamp convertToDatabaseColumn(ZonedDateTime dateTime) {
          return dateTime == null ? null : Timestamp.from(dateTime.toInstant());
      }
  
      @Override
      public ZonedDateTime convertToEntityAttribute(Timestamp timestamp) {
          return timestamp == null ? null : ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.UTC);
      }
  }
  </code></pre>