You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2021/04/22 06:24:49 UTC

[myfaces-tobago] 01/02: feat: new datepicker

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 0019dafccc1e54ef0c23e0747e885b6a04c7c883
Author: Udo Schnurpfeil <ud...@irian.eu>
AuthorDate: Wed Apr 21 18:10:24 2021 +0200

    feat: new datepicker
    
    Using HTML5 conform way
    
    issue: TOBAGO-2071
---
 .../myfaces/tobago/convert/DateTimeConverter.java  |   4 +
 .../tobago/facelets/ConvertDateTimeHandler.java    |   4 +
 .../tobago/internal/component/AbstractUIDate.java  |  40 ++-
 .../tobago/internal/component/AbstractUIIn.java    |   1 +
 .../internal/component/AbstractUITextarea.java     |   4 +-
 .../internal/renderkit/renderer/DateRenderer.java  | 313 ++++++++++++++++++---
 .../internal/renderkit/renderer/InRenderer.java    |   6 +-
 .../component/ConvertDateTimeTagDeclaration.java   |   3 +
 .../taglib/component/DateTagDeclaration.java       |   1 +
 .../tobago/internal/util/DateFormatUtils.java      | 156 ++++++----
 .../myfaces/tobago/renderkit/RendererBase.java     |   6 +-
 .../tobago/renderkit/html/CustomAttributes.java    |   2 +
 .../tobago/renderkit/html/HtmlInputTypes.java      |  51 +++-
 .../apache/myfaces/tobago/util/ComponentUtils.java |   3 +
 .../internal/config/AbstractTobagoTestBase.java    |   5 +
 .../renderkit/renderer/DateRendererUnitTest.java   | 178 ++++++++++--
 .../internal/util/DateFormatUtilsUnitTest.java     |  68 ++++-
 .../renderer/date/{date.html => dateAuto.html}     |   4 +-
 .../date/{date-label.html => dateBoth.html}        |   6 +-
 .../renderer/date/{date.html => dateDate.html}     |   4 +-
 .../renderer/date/{date.html => dateTime.html}     |   6 +-
 .../date/{date.html => localDateAuto.html}         |   4 +-
 .../{date-label.html => localDateTimeAuto.html}    |   6 +-
 .../date/{date.html => localTimeAuto.html}         |   6 +-
 .../date/{date-label.html => testLabel.html}       |   4 +-
 ...date-today-button.html => testTodayButton.html} |   4 +-
 .../date/{date-today-button.html => text.html}     |   4 +-
 .../{date-label.html => zonedDateTimeAuto.html}    |   6 +-
 .../tobago/example/demo/DateController.java        |  28 +-
 .../20-component/010-input/40-date/Date.xhtml      |  26 +-
 .../tobago-theme-standard/src/main/js/tobago.js    |  17 +-
 .../src/main/ts/tobago-date.ts                     |  20 +-
 32 files changed, 807 insertions(+), 183 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java
index 85f0573..d4fe8bf 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java
@@ -44,6 +44,10 @@ import java.util.TimeZone;
 
 import static org.apache.myfaces.tobago.convert.DateTimeConverter.CONVERTER_ID;
 
+/**
+ * @deprecated Since 5.0.0. Should work with &lt;f:convertDateTime> since JSF 2.3.
+ */
+@Deprecated
 @org.apache.myfaces.tobago.apt.annotation.Converter(id = CONVERTER_ID)
 public class DateTimeConverter extends javax.faces.convert.DateTimeConverter {
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java
index 3012204..5c2dec1 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java
@@ -39,6 +39,10 @@ import java.lang.invoke.MethodHandles;
 import java.util.Locale;
 import java.util.TimeZone;
 
+/**
+ * @deprecated Since 5.0.0. Should work with &lt;f:convertDateTime> since JSF 2.3.
+ */
+@Deprecated
 public class ConvertDateTimeHandler extends ConverterHandler {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java
index b0141fe..ea7fb6c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java
@@ -19,36 +19,30 @@
 
 package org.apache.myfaces.tobago.internal.component;
 
-import org.apache.myfaces.tobago.internal.util.DateFormatUtils;
-import org.apache.myfaces.tobago.util.ComponentUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.faces.context.FacesContext;
-import javax.faces.convert.Converter;
-import javax.faces.convert.DateTimeConverter;
-import java.lang.invoke.MethodHandles;
-import java.util.Date;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
 
 /**
  * {@link org.apache.myfaces.tobago.internal.taglib.component.DateTagDeclaration}
  */
-public abstract class AbstractUIDate extends AbstractUIIn {
+public abstract class AbstractUIDate extends AbstractUIInput {
+
+  private transient HtmlInputTypes type;
+  private transient String pattern;
 
-  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+  public HtmlInputTypes getType() {
+    return type;
+  }
+
+  public void setType(HtmlInputTypes type) {
+    this.type = type;
+  }
 
   public String getPattern() {
-    final FacesContext facesContext = getFacesContext();
-    Converter converter = ComponentUtils.getConverter(facesContext, this, getSubmittedValue());
-    if (!(converter instanceof DateTimeConverter)) {
-      // hack for prototyping, if there is no value behind the component.
-      converter = facesContext.getApplication().createConverter(Date.class);
-      if (LOG.isWarnEnabled()) {
-        LOG.warn("Can't find a converter to get a pattern in component {}! Using default.",
-            getClientId(facesContext));
-      }
-    }
-    return DateFormatUtils.findPattern((DateTimeConverter) converter);
+    return pattern;
+  }
+
+  public void setPattern(String pattern) {
+    this.pattern = pattern;
   }
 
   public abstract boolean isTodayButton();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java
index d30e2d6..00bb977 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java
@@ -26,4 +26,5 @@ public abstract class AbstractUIIn extends AbstractUIInput {
 
   public abstract String getPlaceholder();
 
+  public abstract boolean isPassword();
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java
index bcdec44..47298d3 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java
@@ -24,7 +24,9 @@ import org.apache.myfaces.tobago.sanitizer.SanitizeMode;
 /**
  * {@link org.apache.myfaces.tobago.internal.taglib.component.TextareaTagDeclaration}
  */
-public abstract class AbstractUITextarea extends AbstractUIIn {
+public abstract class AbstractUITextarea extends AbstractUIInput {
+
+  public abstract String getPlaceholder();
 
   public abstract SanitizeMode getSanitize();
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java
index 65eeeac..fed828a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java
@@ -19,32 +19,73 @@
 
 package org.apache.myfaces.tobago.internal.renderkit.renderer;
 
+import org.apache.myfaces.tobago.component.Attributes;
 import org.apache.myfaces.tobago.internal.component.AbstractUIDate;
 import org.apache.myfaces.tobago.internal.context.DateTimeI18n;
+import org.apache.myfaces.tobago.internal.util.AccessKeyLogger;
 import org.apache.myfaces.tobago.internal.util.DateFormatUtils;
+import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
-import org.apache.myfaces.tobago.internal.util.StringUtils;
 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
-import org.apache.myfaces.tobago.renderkit.css.CssItem;
 import org.apache.myfaces.tobago.renderkit.css.FaIcons;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
 import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
+import org.apache.myfaces.tobago.util.ComponentUtils;
 import org.apache.myfaces.tobago.util.ResourceUtils;
 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.el.ValueExpression;
 import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.convert.DateTimeConverter;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
 
-public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
+public class DateRenderer<T extends AbstractUIDate> extends MessageLayoutRendererBase<T> {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  private static final String TYPE_DATE = "date";
+  private static final String TYPE_TIME = "time";
+  private static final String TYPE_BOTH = "both";
+  private static final String TYPE_LOCAL_DATE = "localDate";
+  private static final String TYPE_LOCAL_TIME = "localTime";
+  private static final String TYPE_LOCAL_DATE_TIME = "localDateTime";
+  private static final String TYPE_OFFSET_TIME = "offsetTime";
+  private static final String TYPE_OFFSET_DATE_TIME = "offsetDateTime";
+  private static final String TYPE_ZONED_DATE_TIME = "zonedDateTime";
+  private static final String TYPE_MONTH = "month";
+  private static final String TYPE_WEEK = "week";
+
+  private static final String PATTERN_DATE = "yyyy-MM-dd";
+  private static final String PATTERN_DATETIME = "yyyy-MM-dd'T'HH:mm:ss.SSS";
+  private static final String PATTERN_TIME = "HH:mm:ss.SSS";
+  private static final String PATTERN_MONTH = "yyyy-MM";
+  private static final String PATTERN_WEEK = "yyyy-'W'MM";
+
+  @Override
+  public void encodeBeginInternal(FacesContext facesContext, T component) throws IOException {
+    prepare(facesContext, component);
+    super.encodeBeginInternal(facesContext, component);
+  }
+
+  @Override
+  protected boolean isOutputOnly(T component) {
+    return component.isDisabled() || component.isReadonly();
+  }
+
   @Override
   public HtmlElements getComponentTag() {
     return HtmlElements.TOBAGO_DATE;
@@ -52,16 +93,26 @@ public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
 
   @Override
   protected void writeAdditionalAttributes(
-      final FacesContext facesContext, final TobagoResponseWriter writer, final T input)
+      final FacesContext facesContext, final TobagoResponseWriter writer, final T date)
       throws IOException {
 
-    super.writeAdditionalAttributes(facesContext, writer, input);
-    writer.writeAttribute(HtmlAttributes.PATTERN, DateFormatUtils.toJavaScriptPattern(input.getPattern()), true);
+    super.writeAdditionalAttributes(facesContext, writer, date);
+//    writer.writeAttribute(HtmlAttributes.PATTERN,
+//    new DateFormatUtils.DateTimeJavaScriptPattern(input.getPattern()).getDatePattern(), true);
+    final HtmlInputTypes type = date.getType();
+    if (date.getType() == HtmlInputTypes.TEXT) {
+      // todo
+      final DateFormatUtils.DateTimeJavaScriptPattern patterns
+          = new DateFormatUtils.DateTimeJavaScriptPattern(date.getPattern());
+      writer.writeAttribute(CustomAttributes.DATE_PATTERN, patterns.getDatePattern(), true);
+      writer.writeAttribute(CustomAttributes.TIME_PATTERN, patterns.getTimePattern(), true);
+    }
+    // todo: also render pattern, either yyyy-mm-dd or in case of type=text the combination of upper
 //    final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
 //    writer.writeAttribute(CustomAttributes.TODAY, sdf.format(new Date()), true); XXX seem no longer needed
     final DateTimeI18n dateTimeI18n = DateTimeI18n.valueOf(facesContext.getViewRoot().getLocale());
     writer.writeAttribute(CustomAttributes.I18N, JsonUtils.encode(dateTimeI18n), true);
-    writer.writeAttribute(CustomAttributes.TODAY_BUTTON, input.isTodayButton());
+    writer.writeAttribute(CustomAttributes.TODAY_BUTTON, date.isTodayButton());
   }
 
   @Override
@@ -74,15 +125,59 @@ public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
     writer.startElement(HtmlElements.DIV);
     writer.writeClassAttribute(BootstrapClass.INPUT_GROUP);
 
-    super.encodeBeginField(facesContext, component);
-  }
+    final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
+    final HtmlInputTypes type = component.getType();
 
-  @Override
-  public void encodeEndField(final FacesContext facesContext, final T component) throws IOException {
+    final String currentValue = getCurrentValue(facesContext, component);
+    final String clientId = component.getClientId(facesContext);
+    final String fieldId = component.getFieldId(facesContext);
+    final boolean readonly = component.isReadonly();
+    final boolean disabled = component.isDisabled();
+    final boolean required = ComponentUtils.getBooleanAttribute(component, Attributes.required);
+
+    writer.startElement(HtmlElements.INPUT);
 
-    super.encodeEndField(facesContext, component);
+    if (component.getAccessKey() != null) {
+      writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(component.getAccessKey()), false);
+      AccessKeyLogger.addAccessKey(facesContext, component.getAccessKey(), clientId);
+    }
+
+    writer.writeAttribute(HtmlAttributes.TYPE, type.getValue(), false);
+    writer.writeNameAttribute(clientId);
+    writer.writeIdAttribute(fieldId);
+    HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
+    writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
+    writer.writeAttribute(HtmlAttributes.TITLE, title, true);
+    writer.writeAttribute(HtmlAttributes.READONLY, readonly);
+    writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
+    writer.writeAttribute(HtmlAttributes.TABINDEX, component.getTabIndex());
+//    if (!disabled && !readonly) {
+//      writer.writeAttribute(HtmlAttributes.PLACEHOLDER, component.getPlaceholder(), true);
+//    }
+
+    writer.writeClassAttribute(
+        BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(component)),
+        BootstrapClass.FORM_CONTROL,
+        component.getCustomClass());
+
+    writer.writeAttribute(HtmlAttributes.REQUIRED, required);
+    renderFocus(clientId, component.isFocus(), component.isError(), facesContext, writer);
+
+    writer.endElement(HtmlElements.INPUT);
+
+    encodeBehavior(writer, facesContext, component);
+
+    if (type.supportsDate()) {
+      encodeButton(facesContext, component, FaIcons.CALENDAR);
+    }
+    if (type.supportsTime()) {
+      encodeButton(facesContext, component, FaIcons.CLOCK_O);
+    }
+  }
+
+  private void encodeButton(final FacesContext facesContext, final T component, final FaIcons icon)
+      throws IOException {
 
-    final String pattern = component.getPattern();
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
 
     writer.startElement(HtmlElements.BUTTON);
@@ -96,33 +191,185 @@ public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
     writer.writeAttribute(HtmlAttributes.DISABLED, component.isDisabled() || component.isReadonly());
     writer.writeAttribute(HtmlAttributes.TABINDEX, component.getTabIndex());
 
-    final boolean hasDate = StringUtils.containsAny(pattern, "yYMDdE");
-    final boolean hasTime = StringUtils.containsAny(pattern, "Hhms");
-
-    if (hasDate || !hasTime) { //  || !hasTime is, to have at least one icon
-      writer.startElement(HtmlElements.I);
-      writer.writeClassAttribute(FaIcons.FA, FaIcons.CALENDAR);
-      writer.endElement(HtmlElements.I);
-    }
-    if (hasTime) {
-      writer.startElement(HtmlElements.I);
-      writer.writeClassAttribute(FaIcons.FA, FaIcons.CLOCK_O);
-      writer.endElement(HtmlElements.I);
-    }
-
-    if (StringUtils.containsAny(pattern, "GWFKzX")) {
-      LOG.warn("Pattern chars 'G', 'W', 'F', 'K', 'z' and 'X' are not supported: " + pattern);
-    }
+    writer.startElement(HtmlElements.I);
+    writer.writeClassAttribute(FaIcons.FA, icon);
+    writer.endElement(HtmlElements.I);
 
     writer.endElement(HtmlElements.BUTTON);
+  }
 
+  @Override
+  public void encodeEndField(final FacesContext facesContext, final T component) throws IOException {
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
     writer.endElement(HtmlElements.DIV);
-
     writer.endElement(HtmlElements.DIV);
   }
 
   @Override
-  protected CssItem getRendererCssClass() {
-    return null;
+  protected String getFieldId(final FacesContext facesContext, final T component) {
+    return component.getFieldId(facesContext);
   }
+
+  /**
+   * Creates a converter (if not defined any) which satifies the requirements of HTML5 like described here:
+   * <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats">MDN</a>
+   */
+  @Override
+  protected Converter getConverter(FacesContext facesContext, T component, Object value) {
+    final Converter converter = component.getConverter();
+    if (converter != null) {
+      // todo: should we warn here, if the type is not "text"?
+      return converter;
+    } else {
+      Class<?> estimatedType = estimateValueType(facesContext, component);
+      if (estimatedType == null) {
+        // todo: log debug instead of warn
+        LOG.warn("Can't estimate type (clientId='{}')!", component.getClientId(facesContext));
+        return null;
+      } else if (estimatedType.isAssignableFrom(String.class)) {
+        // todo: log debug instead of warn
+        LOG.warn("No converter for java.lang.String");
+        return null;
+      } else {
+        final DateTimeConverter dateTimeConverter
+            = (DateTimeConverter) facesContext.getApplication().createConverter("javax.faces.DateTime");
+        if (estimatedType.isAssignableFrom(LocalDateTime.class)) {
+          dateTimeConverter.setType("localDateTime");
+          dateTimeConverter.setPattern(PATTERN_DATETIME);
+        } else if (estimatedType.isAssignableFrom(LocalDate.class)) {
+          dateTimeConverter.setType("localDate");
+          dateTimeConverter.setPattern(PATTERN_DATE);
+        } else if (estimatedType.isAssignableFrom(LocalTime.class)) {
+          dateTimeConverter.setType("localTime");
+          dateTimeConverter.setPattern(PATTERN_TIME);
+        } else if (estimatedType.isAssignableFrom(ZonedDateTime.class)) {
+          dateTimeConverter.setType("zonedDateTime");
+          dateTimeConverter.setPattern(PATTERN_DATETIME);
+        } else if (estimatedType.isAssignableFrom(Date.class)) {
+          dateTimeConverter.setType("date");
+          dateTimeConverter.setPattern(PATTERN_DATE);
+        } else if (estimatedType.isAssignableFrom(Calendar.class)) {
+          dateTimeConverter.setType("date");
+          dateTimeConverter.setPattern(PATTERN_DATE);
+        } else if (estimatedType.isAssignableFrom(Number.class)) {
+          LOG.error("date");
+          dateTimeConverter.setType("date");
+          dateTimeConverter.setPattern(PATTERN_DATE);
+//      } else if (estimatedType.isAssignableFrom(Month.class)) {
+//        LOG.error("month");
+//        dateTimeConverter.setType("month"); // XXX is there a type month?
+//      } else if (estimatedType.isAssignableFrom(Week.class)) {
+//        LOG.error("week");
+//        dateTimeConverter.setType("week"); // XXX is there a type week?
+          // todo: ZonedDateTime
+        } else {
+          LOG.warn("Type might not be supported (type='{}' clientId='{}')!",
+              estimatedType.getName(), component.getClientId(facesContext));
+          throw new RuntimeException();
+        }
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("type='{}' pattern='{}'", dateTimeConverter.getType(), dateTimeConverter.getPattern());
+        }
+
+        return dateTimeConverter;
+      }
+    }
+  }
+
+  private Class<?> estimateValueType(final FacesContext facesContext, final T component) {
+    Class<?> estimatedType = null;
+    final ValueExpression valueExpression = component.getValueExpression("value");
+    if (valueExpression != null) {
+      try {
+        estimatedType = valueExpression.getType(facesContext.getELContext());
+      } catch (final Exception e) {
+        // ignore, seems not to be possible, when EL is a function like #{bean.getName(item.id)}
+      }
+    }
+    if (estimatedType == null) {
+      Object submittedValue = component.getSubmittedValue();
+      if (submittedValue != null) {
+        estimatedType = submittedValue.getClass();
+      }
+    }
+    if (estimatedType == null) {
+      Object value = component.getValue();
+      if (value != null) {
+        estimatedType = value.getClass();
+      }
+    }
+    return estimatedType;
+  }
+
+  private void prepare(final FacesContext facesContext, final T component) {
+    HtmlInputTypes type = component.getType();
+    String pattern = component.getPattern();
+    if (type == null || pattern == null) {
+      Converter converter = getConverter(facesContext, component, component.getSubmittedValue()); // XXX submitted?
+      if (converter instanceof DateTimeConverter) {
+        DateTimeConverter dtConverter = (DateTimeConverter) converter;
+        String t = dtConverter.getType();
+        if (TYPE_DATE.equals(t)) {
+          type = HtmlInputTypes.DATE;
+        } else if (TYPE_BOTH.equals(t)) {
+          type = HtmlInputTypes.DATETIME_LOCAL;
+        } else if (TYPE_TIME.equals(t)) {
+          type = HtmlInputTypes.TIME;
+        } else if (TYPE_LOCAL_DATE.equals(t)) {
+          type = HtmlInputTypes.DATE;
+        } else if (TYPE_LOCAL_DATE_TIME.equals(t)) {
+          type = HtmlInputTypes.DATETIME_LOCAL;
+        } else if (TYPE_LOCAL_TIME.equals(t)) {
+          type = HtmlInputTypes.TIME;
+        } else if (TYPE_ZONED_DATE_TIME.equals(t)) {
+          type = HtmlInputTypes.DATETIME_LOCAL;
+        } else if (TYPE_OFFSET_DATE_TIME.equals(t)) {
+          type = HtmlInputTypes.DATETIME_LOCAL;
+        } else if (TYPE_OFFSET_TIME.equals(t)) {
+          type = HtmlInputTypes.TIME;
+        } else if (TYPE_MONTH.equals(t)) {
+          type = HtmlInputTypes.MONTH;
+        } else if (TYPE_WEEK.equals(t)) {
+          type = HtmlInputTypes.WEEK;
+        } else {
+          type = HtmlInputTypes.TEXT;
+        }
+        pattern = dtConverter.getPattern();
+        if (pattern == null) {
+          if (TYPE_DATE.equals(t)) {
+            pattern = PATTERN_DATE;
+          } else if (TYPE_BOTH.equals(t)) {
+            pattern = PATTERN_DATETIME;
+          } else if (TYPE_TIME.equals(t)) {
+            pattern = PATTERN_TIME;
+          } else if (TYPE_LOCAL_DATE.equals(t)) {
+            pattern = PATTERN_DATE;
+          } else if (TYPE_LOCAL_DATE_TIME.equals(t)) {
+            pattern = PATTERN_DATETIME;
+          } else if (TYPE_LOCAL_TIME.equals(t)) {
+            pattern = PATTERN_TIME;
+          } else if (TYPE_ZONED_DATE_TIME.equals(t)) {
+            pattern = PATTERN_DATETIME;
+          } else if (TYPE_OFFSET_DATE_TIME.equals(t)) {
+            pattern = PATTERN_DATETIME;
+          } else if (TYPE_OFFSET_TIME.equals(t)) {
+            pattern = PATTERN_TIME;
+          } else if (TYPE_MONTH.equals(t)) {
+            pattern = PATTERN_MONTH;
+          } else if (TYPE_WEEK.equals(t)) {
+            pattern = PATTERN_WEEK;
+          } else {
+            pattern = DateFormatUtils.findPattern(dtConverter);
+          }
+        } else {
+          pattern = PATTERN_DATETIME;
+        }
+      } else {
+        type = HtmlInputTypes.TEXT;
+      }
+    }
+    component.setPattern(pattern);
+    component.setType(type);
+  }
+
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
index 31d083e..e2ea371 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
@@ -79,7 +79,7 @@ public class InRenderer<T extends AbstractUIIn> extends MessageLayoutRendererBas
       throws IOException {
     final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
     final String currentValue = getCurrentValue(facesContext, component);
-    final boolean password = ComponentUtils.getBooleanAttribute(component, Attributes.password);
+    final boolean password = component.isPassword();
     if (LOG.isDebugEnabled()) {
       LOG.debug("currentValue = '{}'", StringUtils.toConfidentialString(currentValue, password));
     }
@@ -117,9 +117,7 @@ public class InRenderer<T extends AbstractUIIn> extends MessageLayoutRendererBas
     if (currentValue != null && !password) {
       writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
     }
-    if (title != null) {
-      writer.writeAttribute(HtmlAttributes.TITLE, title, true);
-    }
+    writer.writeAttribute(HtmlAttributes.TITLE, title, true);
     int maxLength = 0;
     int minLength = 0;
     String pattern = null;
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java
index f3b21cc..0647a11 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java
@@ -31,7 +31,10 @@ import javax.el.ValueExpression;
 /**
  * Register a DateTimeConverter instance on the UIComponent associated with the closest parent UIComponent custom
  * action.
+ *
+ * @deprecated Since 5.0.0. Should work with &lt;f:convertDateTime> since JSF 2.3.
  */
+@Deprecated
 @Tag(name = "convertDateTime")
 @ConverterTag(
     converterId = DateTimeConverter.CONVERTER_ID,
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java
index da18c25..79e9c27 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java
@@ -86,6 +86,7 @@ public interface DateTagDeclaration
   /**
    * If true, a today button is displayed on the datetimepicker.
    */
+  // todo: might be deprecated
   @TagAttribute
   @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
   void setTodayButton(String required);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java
index 5dd7933..9f9185d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java
@@ -118,69 +118,129 @@ public final class DateFormatUtils {
    * and convert it to 'vanillajs-datepicker', see https://mymth.github.io/vanillajs-datepicker/#/date-string+format
    * Attention: Not every pattern char is supported.
    */
-  public static String toJavaScriptPattern(final String originalPattern) {
-    String pattern;
-    if (originalPattern == null || originalPattern.length() > 100) {
-      LOG.warn("Pattern not supported: " + originalPattern);
-      pattern = "";
-    } else {
-      pattern = originalPattern;
-    }
-
-    StringBuilder analyzedPattern = new StringBuilder();
-    StringBuilder nextSegment = new StringBuilder();
-    boolean escMode = false;
-    for (int i = 0; i < pattern.length(); i++) {
-      final char currentChar = pattern.charAt(i);
-      if (currentChar == '\'' && !escMode) {
-        escMode = true;
-        analyzedPattern.append(toJavaScriptPatternPart(nextSegment.toString()));
-        nextSegment = new StringBuilder();
-      } else if (currentChar == '\'' && pattern.charAt(i + 1) == '\'') {
-        nextSegment.append("\\");
-        nextSegment.append("'");
-        i++;
-      } else if (currentChar == '\'') {
-        escMode = false;
-        analyzedPattern.append(nextSegment);
-        nextSegment = new StringBuilder();
+  public static class DateTimeJavaScriptPattern {
+
+    private StringBuilder buffer = new StringBuilder();
+    private StringBuilder datePattern;
+    private StringBuilder timePattern;
+    private String separator;
+
+    private boolean dateMode = false;
+    private boolean timeMode = false;
+
+    private int lastDateIndex = -1;
+    private int lastTimeIndex = -1;
+
+    public DateTimeJavaScriptPattern(final String javaPattern) {
+      String pattern;
+      if (javaPattern == null) {
+        LOG.warn("Empty pattern not supported!");
+        // XXX an missing pattern might be supporable without Datepicker, but with simple <tc:in>
+        pattern = "";
+      } else if (javaPattern.length() > 100) {
+        LOG.warn("Pattern too long (max = 100): '{}'!", javaPattern);
+        pattern = "";
+      } else if (javaPattern.indexOf('\'') > -1) {
+        LOG.warn("Pattern escape char ' is not supported: '{}'!", javaPattern);
+        pattern = "";
+      } else if (StringUtils.containsAny(javaPattern, "GWFKzX")) {
+        LOG.warn("Pattern chars 'G', 'W', 'F', 'K', 'z' and 'X' are not supported: '{}'!", javaPattern);
+        pattern = "";
       } else {
-        if (escMode) {
-          nextSegment.append("\\");
-        }
-        nextSegment.append(currentChar);
+        pattern = javaPattern;
+      }
+
+      for (int i = 0; i < pattern.length(); i++) {
+        final char c = pattern.charAt(i);
+        checkSpit(i, c);
+        append(c);
       }
     }
-    if (nextSegment.toString().length() > 0) {
-      if (escMode) {
-        analyzedPattern.append(nextSegment);
+
+    private void checkSpit(final int index, final char c) {
+
+      boolean isDate = isDate(c);
+      boolean isTime = isTime(c);
+
+      if (isDate){
+        lastDateIndex = index;
+      }
+      if (isTime){
+        lastTimeIndex = index;
+      }
+
+      if (!dateMode && !timeMode && isDate) {
+        dateMode = true;
+        datePattern = new StringBuilder(buffer);
+        buffer.setLength(0);
+      } else if (!dateMode && !timeMode && isTime) {
+        timeMode = true;
+        timePattern = new StringBuilder(buffer);
+        buffer.setLength(0);
+      } else if (!dateMode && !timeMode) {
+        LOG.warn("Prefix without date/time char currently not supported!");
+      } else if (dateMode && isTime) {
+        timeMode = true;
+        dateMode = false;
+        timePattern = new StringBuilder();
+        separator = datePattern.substring(lastDateIndex + 1);
+        datePattern.setLength(lastDateIndex + 1);
+        if (timePattern != null) {
+          LOG.warn("Pattern mixed!");
+        }
+      } else if (timeMode && isDate) {
+        timeMode = false;
+        dateMode = true;
+        datePattern = new StringBuilder();
+        separator = timePattern.substring(lastTimeIndex + 1);
+        timePattern.setLength(lastTimeIndex + 1);
+        if (datePattern != null) {
+          LOG.warn("Pattern mixed!");
+        }
       } else {
-        analyzedPattern.append(toJavaScriptPatternPart(nextSegment.toString()));
+        // nothing to switch
       }
     }
 
-    return analyzedPattern.toString();
-  }
+    private void append(final char c) {
+      final char converted = convert(c);
 
-  public static String toJavaScriptPatternPart(String originalPattern) {
+      if (dateMode) {
+        datePattern.append(converted);
+      } else if (timeMode) {
+        timePattern.append(converted);
+      } else {
+        buffer.append(converted);
+      }
+    }
 
-    String pattern = originalPattern;
+    public String getDatePattern() {
+      return datePattern != null ? datePattern.toString() : null;
+    }
 
-    if (pattern.contains("G") || pattern.contains("W") || pattern.contains("F")
-        || pattern.contains("K") || pattern.contains("z") || pattern.contains("X")) {
-      LOG.warn("Pattern chars 'G', 'W', 'F', 'K', 'z' and 'X' are not supported: " + pattern);
-      pattern = "";
+    public String getTimePattern() {
+      return timePattern != null ? timePattern.toString() : null;
     }
 
-    if (pattern.contains("M")) {
-      pattern = pattern.replaceAll("M", "m");
+    public String getSeparator() {
+      return separator;
     }
 
-    if (pattern.contains("d")) {
-      pattern = pattern.replaceAll("dd+", "dd");
+    public static boolean isDate(final char c) {
+      return c == 'y' || c == 'Y' || c == 'M' || c == 'L' || c == 'd';
     }
 
-    return pattern;
-  }
+    private static boolean isTime(final char c) {
+      return c == 'H' || c == 'm' || c == 's';
+    }
+
+    private char convert(char c) {
 
+      if (c == 'M') {
+        return 'm';
+      } else {
+        return c;
+      }
+    }
+  }
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java
index 9b5cfb8..929812e 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java
@@ -421,7 +421,7 @@ public abstract class RendererBase<T extends UIComponent> extends Renderer {
         if (itemValue instanceof String && values != null && values.length > 0 && !(values[0] instanceof String)) {
           itemValue = ComponentUtils.getConvertedValue(facesContext, component, (String) itemValue);
         }
-        final String formattedValue = getFormattedValue(facesContext, component, itemValue);
+        final String formattedValue = getFormattedValue(facesContext, (T) component, itemValue);
         final boolean contains;
         if (submittedValues == null) {
           contains = ArrayUtils.contains(values, itemValue);
@@ -473,7 +473,7 @@ public abstract class RendererBase<T extends UIComponent> extends Renderer {
   }
 
   protected String getFormattedValue(
-      final FacesContext facesContext, final UIComponent component, final Object currentValue)
+      final FacesContext facesContext, final T component, final Object currentValue)
       throws ConverterException {
 
     if (currentValue == null) {
@@ -492,7 +492,7 @@ public abstract class RendererBase<T extends UIComponent> extends Renderer {
    * May return null, if no converter can be find.
    */
   protected Converter getConverter(
-      final FacesContext facesContext, final UIComponent component, final Object value) {
+      final FacesContext facesContext, final T component, final Object value) {
 
     Converter converter = null;
     if (component instanceof ValueHolder) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
index fa33082..55682a6 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
@@ -25,6 +25,7 @@ public enum CustomAttributes implements MarkupLanguageAttributes {
   COLLAPSE_TARGET("collapse-target"),
   CONFIRMATION("confirmation"),
   CLIENT_ID("client-id"),
+  DATE_PATTERN("date-pattern"),
   DELAY("delay"),
   EVENT("event"),
   /**
@@ -84,6 +85,7 @@ public enum CustomAttributes implements MarkupLanguageAttributes {
    * The date of today
    */
 //  TODAY("today"),
+  TIME_PATTERN("time-pattern"),
   /**
    * Show the button to set the field to today.
    */
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java
index f4325e8..a86a01d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java
@@ -31,7 +31,47 @@ public enum HtmlInputTypes implements HtmlTypes {
   FILE("file"),
   HIDDEN("hidden"),
   IMAGE("image"),
-  BUTTON("button");
+  BUTTON("button"),
+  COLOR("color"),
+  DATE("date"),
+  /** @deprecated */
+  @Deprecated
+  DATETIME("datetime"),
+  DATETIME_LOCAL("datetime-local"),
+  EMAIL("email"),
+  MONTH("month"),
+  NUMBER("number"),
+  SEARCH("search"),
+  TEL("tel"),
+  TIME("time"),
+  URL("url"),
+  WEEK("week");
+
+  public static final String STRING_TEXT = "text";
+  public static final String STRING_PASSWORD = "password";
+  public static final String STRING_CHECKBOX = "checkbox";
+  public static final String STRING_RADIO = "radio";
+  public static final String STRING_RANGE = "range";
+  public static final String STRING_SUBMIT = "submit";
+  public static final String STRING_RESET = "reset";
+  public static final String STRING_FILE = "file";
+  public static final String STRING_HIDDEN = "hidden";
+  public static final String STRING_IMAGE = "image";
+  public static final String STRING_BUTTON = "button";
+  public static final String STRING_COLOR = "color";
+  public static final String STRING_DATE = "date";
+  /** @deprecated */
+  @Deprecated
+  public static final String STRING_DATETIME = "datetime";
+  public static final String STRING_DATETIME_LOCAL = "datetime-local";
+  public static final String STRING_EMAIL = "email";
+  public static final String STRING_MONTH = "month";
+  public static final String STRING_NUMBER = "number";
+  public static final String STRING_SEARCH = "search";
+  public static final String STRING_TEL = "tel";
+  public static final String STRING_TIME = "time";
+  public static final String STRING_URL = "url";
+  public static final String STRING_WEEK = "week";
 
   private final String value;
 
@@ -43,4 +83,13 @@ public enum HtmlInputTypes implements HtmlTypes {
   public String getValue() {
     return value;
   }
+
+  public final boolean supportsDate() {
+    return this == DATE || this == DATETIME_LOCAL || this == WEEK || this == MONTH;
+  }
+
+  public final boolean supportsTime() {
+    return this == TIME || this == DATETIME_LOCAL;
+  }
+
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
index 8fc4320..7bbe857 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
@@ -845,7 +845,10 @@ public final class ComponentUtils {
 
   /**
    * May return null, if no converter can be find.
+   *
+   * @deprecated since 5.0.0. Please use {@link RendererBase#getConverter}.
    */
+  @Deprecated
   public static Converter getConverter(
       final FacesContext facesContext, final UIComponent component, final Object value) {
 
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
index 15cb79f..6a27f5e 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
@@ -93,11 +93,13 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
 import javax.faces.component.behavior.AjaxBehavior;
+import javax.faces.convert.DateTimeConverter;
 import javax.faces.render.RenderKit;
 import javax.servlet.ServletContext;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.nio.charset.StandardCharsets;
+import java.util.Date;
 import java.util.Locale;
 
 import static org.apache.myfaces.tobago.config.TobagoConfig.TOBAGO_CONFIG;
@@ -176,6 +178,9 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
     application.addBehavior(AjaxBehavior.BEHAVIOR_ID, AjaxBehavior.class.getName());
     application.addBehavior(EventBehavior.BEHAVIOR_ID, EventBehavior.class.getName());
 
+    application.addConverter(Date.class, DateTimeConverter.class.getName());
+    application.addConverter("javax.faces.DateTime", DateTimeConverter.class.getName());
+
     final RenderKit renderKit = facesContext.getRenderKit();
     renderKit.addRenderer(UIBadge.COMPONENT_FAMILY, RendererTypes.BADGE, new BadgeRenderer());
     renderKit.addRenderer(UIBox.COMPONENT_FAMILY, RendererTypes.BOX, new BoxRenderer());
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java
index 7b4d960..c72c948 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java
@@ -22,64 +22,206 @@ package org.apache.myfaces.tobago.internal.renderkit.renderer;
 import org.apache.myfaces.tobago.component.RendererTypes;
 import org.apache.myfaces.tobago.component.Tags;
 import org.apache.myfaces.tobago.component.UIDate;
-import org.apache.myfaces.tobago.convert.DateTimeConverter;
 import org.apache.myfaces.tobago.util.ComponentUtils;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import javax.faces.convert.Converter;
+import javax.faces.convert.DateTimeConverter;
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.TimeZone;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
 
 public class DateRendererUnitTest extends RendererTestBase {
 
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private static final LocalDateTime SPUTNIK_LOCAL_DATE_TIME = LocalDateTime.of(1957, 10, 5, 0, 28, 34, 123456789);
+  private static final LocalDate SPUTNIK_LOCAL_DATE = SPUTNIK_LOCAL_DATE_TIME.toLocalDate();
+  private static final LocalTime SPUTNIK_LOCAL_TIME = SPUTNIK_LOCAL_DATE_TIME.toLocalTime();
+  private static final ZonedDateTime SPUTNIK_ZONED_DATE_TIME = SPUTNIK_LOCAL_DATE_TIME.atZone(ZoneId.of("+05:00"));
+  private static final Date SPUTNIK_DATE = Date.from(SPUTNIK_ZONED_DATE_TIME.toInstant());
+
+//  Naming scheme: value-type + converter-type
+
   @Test
-  public void date() throws IOException {
+  public void dateBoth() throws IOException {
 
     final UIDate d = (UIDate) ComponentUtils.createComponent(
         facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_DATE);
+
     DateTimeConverter c = new DateTimeConverter();
-    c.setPattern("dd.MM.yyyy");
+    c.setType("both");
+    c.setPattern("yyyy-MM-dd'T'HH:mm:ss");
     d.setConverter(c);
 
+    log(d);
     d.encodeAll(facesContext);
 
-    Assertions.assertEquals(loadHtml("renderer/date/date.html"), formattedResult());
+    Assertions.assertEquals(loadHtml("renderer/date/dateBoth.html"), formattedResult());
   }
 
   @Test
-  public void dateLabel() throws IOException, ParseException {
+  public void dateDate() throws IOException {
 
     final UIDate d = (UIDate) ComponentUtils.createComponent(
         facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
-    d.setLabel("Label");
+    d.setValue(SPUTNIK_DATE);
+
     DateTimeConverter c = new DateTimeConverter();
-    c.setPattern("dd.MM.yyyy");
+    c.setType("date");
+    c.setPattern("yyyy-MM-dd");
     d.setConverter(c);
-    final SimpleDateFormat sdf = new SimpleDateFormat(c.getPattern());
-    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
-    d.setValue(sdf.parse("10.12.2020"));
 
+    log(d);
     d.encodeAll(facesContext);
 
-    Assertions.assertEquals(loadHtml("renderer/date/date-label.html"), formattedResult());
+    Assertions.assertEquals(loadHtml("renderer/date/dateDate.html"), formattedResult());
   }
 
   @Test
-  public void dateTodayButton() throws IOException {
+  public void dateAuto() throws IOException {
 
     final UIDate d = (UIDate) ComponentUtils.createComponent(
         facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
-    d.setLabel("Label");
-    d.setTodayButton(true);
+    d.setValue(SPUTNIK_DATE);
+
+    log(d);
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/dateAuto.html"), formattedResult());
+  }
+
+  @Test
+  public void dateTime() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_DATE);
+
     DateTimeConverter c = new DateTimeConverter();
-    c.setPattern("dd.MM.yyyy");
+    c.setType("time");
+    c.setPattern("HH:mm:ss");
     d.setConverter(c);
 
+    log(d);
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/dateTime.html"), formattedResult());
+  }
+
+  @Test
+  public void testLabel() throws IOException, ParseException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setLabel("Label");
+    d.setValue(SPUTNIK_LOCAL_DATE);
+
+    log(d);
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/testLabel.html"), formattedResult());
+  }
+
+  @Test
+  public void localDateAuto() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_LOCAL_DATE);
+
     d.encodeAll(facesContext);
 
-    Assertions.assertEquals(loadHtml("renderer/date/date-today-button.html"), formattedResult());
+    Assertions.assertEquals(loadHtml("renderer/date/localDateAuto.html"), formattedResult());
+  }
+
+  @Test
+  public void localDateTimeAuto() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_LOCAL_DATE_TIME);
+
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/localDateTimeAuto.html"), formattedResult());
+  }
+
+  // old
+
+  @Test
+  public void zonedDateTimeAuto() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_ZONED_DATE_TIME);
+
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/zonedDateTimeAuto.html"), formattedResult());
+  }
+
+  // todo: might be removed
+  @Test
+  public void testTodayButton() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setLabel("Label");
+    d.setValue(SPUTNIK_LOCAL_DATE);
+    d.setTodayButton(true);
+
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/testTodayButton.html"), formattedResult());
+  }
+
+  @Test
+  @Disabled
+  public void text() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_LOCAL_DATE_TIME);
+
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/text.html"), formattedResult());
+  }
+
+  @Test
+  public void localTimeAuto() throws IOException {
+
+    final UIDate d = (UIDate) ComponentUtils.createComponent(
+        facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+    d.setValue(SPUTNIK_LOCAL_TIME);
+
+    d.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/date/localTimeAuto.html"), formattedResult());
+  }
+
+  private void log(UIDate d) {
+    Converter<?> converter = d.getConverter();
+    String pattern = converter instanceof DateTimeConverter ? ((DateTimeConverter) converter).getPattern() : "-";
+    String type = converter instanceof DateTimeConverter ? ((DateTimeConverter) converter).getType() : "-";
+    LOG.info(
+        "type-of-value='{}' with converter='{}', pattern='{}', type='{}'",
+        d.getValue().getClass().getName(),
+        converter != null ? converter.getClass().getName() : "-",
+        pattern,
+        type);
   }
 
 }
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java
index e323434..81e6b6b 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java
@@ -26,10 +26,72 @@ import org.junit.jupiter.api.Test;
 public class DateFormatUtilsUnitTest extends AbstractTobagoTestBase {
 
   @Test
-  public void simple() {
+  public void date() {
     final String p1 = "yyyy-MM-dd";
-    final String js1 = DateFormatUtils.toJavaScriptPattern(DateFormatUtils.toJavaScriptPattern(p1));
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
 
-    Assertions.assertEquals("yyyy-mm-dd", js1);
+    Assertions.assertEquals("yyyy-mm-dd", js1.getDatePattern());
+    Assertions.assertNull(js1.getTimePattern());
+    Assertions.assertNull(js1.getSeparator());
+  }
+
+  @Test
+  public void time() {
+    final String p1 = "HH:mm";  // hour 0-23 : minute 0-59
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+    Assertions.assertNull(js1.getDatePattern());
+    Assertions.assertEquals("HH:mm", js1.getTimePattern());
+    Assertions.assertNull(js1.getSeparator());
+  }
+
+  @Test
+  public void bothDateTime() {
+    final String p1 = "yyyy-MM-dd HH:mm";
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+    Assertions.assertEquals("yyyy-mm-dd", js1.getDatePattern());
+    Assertions.assertEquals("HH:mm", js1.getTimePattern());
+    Assertions.assertEquals(" ", js1.getSeparator());
+  }
+
+  @Test
+  public void bothTimeDate() {
+    final String p1 = "HH:mm yyyy-MM-dd";
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+    Assertions.assertEquals("yyyy-mm-dd", js1.getDatePattern());
+    Assertions.assertEquals("HH:mm", js1.getTimePattern());
+    Assertions.assertEquals(" ", js1.getSeparator());
+  }
+
+  @Test
+  public void both1() {
+    final String p1 = "HH----yyyy";
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+    Assertions.assertEquals("yyyy", js1.getDatePattern());
+    Assertions.assertEquals("HH", js1.getTimePattern());
+    Assertions.assertEquals("----", js1.getSeparator());
+  }
+
+  @Test
+  public void both2() {
+    final String p1 = "::yy--ss::";
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+    Assertions.assertEquals("::yy", js1.getDatePattern());
+    Assertions.assertEquals("ss::", js1.getTimePattern());
+    Assertions.assertEquals("--", js1.getSeparator());
+  }
+
+  @Test
+  public void both3() {
+    final String p1 = "::MMmm::";
+    final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+    Assertions.assertEquals("::mm", js1.getDatePattern());
+    Assertions.assertEquals("mm::", js1.getTimePattern());
+    Assertions.assertEquals("", js1.getSeparator());
   }
 }
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/dateAuto.html
similarity index 61%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/dateAuto.html
index 1c80ca2..42e10ab 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/dateAuto.html
@@ -15,10 +15,10 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' class='form-control'>
+      <input type='date' name='id' id='id::field' value='1957-10-04' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
     </div>
   </div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/dateBoth.html
similarity index 56%
copy from tobago-core/src/test/resources/renderer/date/date-label.html
copy to tobago-core/src/test/resources/renderer/date/dateBoth.html
index 56531f5..d03ddcc 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/dateBoth.html
@@ -15,12 +15,12 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
-  <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+      <input type='datetime-local' name='id' id='id::field' value='1957-10-04T19:28:34' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
     </div>
   </div>
 </tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/dateDate.html
similarity index 61%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/dateDate.html
index 1c80ca2..42e10ab 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/dateDate.html
@@ -15,10 +15,10 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' class='form-control'>
+      <input type='date' name='id' id='id::field' value='1957-10-04' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
     </div>
   </div>
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/dateTime.html
similarity index 57%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/dateTime.html
index 1c80ca2..b78b348 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/dateTime.html
@@ -15,11 +15,11 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' class='form-control'>
-      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+      <input type='time' name='id' id='id::field' value='19:28:34' class='form-control'>
+      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
     </div>
   </div>
 </tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/localDateAuto.html
similarity index 61%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/localDateAuto.html
index 1c80ca2..03f7296 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/localDateAuto.html
@@ -15,10 +15,10 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' class='form-control'>
+      <input type='date' name='id' id='id::field' value='1957-10-05' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
     </div>
   </div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/localDateTimeAuto.html
similarity index 56%
copy from tobago-core/src/test/resources/renderer/date/date-label.html
copy to tobago-core/src/test/resources/renderer/date/localDateTimeAuto.html
index 56531f5..1aa041b 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/localDateTimeAuto.html
@@ -15,12 +15,12 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
-  <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+      <input type='datetime-local' name='id' id='id::field' value='1957-10-05T00:28:34.123' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
     </div>
   </div>
 </tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/localTimeAuto.html
similarity index 57%
rename from tobago-core/src/test/resources/renderer/date/date.html
rename to tobago-core/src/test/resources/renderer/date/localTimeAuto.html
index 1c80ca2..fca219c 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/localTimeAuto.html
@@ -15,11 +15,11 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' class='form-control'>
-      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+      <input type='time' name='id' id='id::field' value='00:28:34.123' class='form-control'>
+      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
     </div>
   </div>
 </tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/testLabel.html
similarity index 64%
copy from tobago-core/src/test/resources/renderer/date/date-label.html
copy to tobago-core/src/test/resources/renderer/date/testLabel.html
index 56531f5..78d7767 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/testLabel.html
@@ -15,11 +15,11 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
+<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0," [...]
   <label for='id::field' class='col-form-label'>Label</label>
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+      <input type='date' name='id' id='id::field' value='1957-10-05' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
     </div>
   </div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-today-button.html b/tobago-core/src/test/resources/renderer/date/testTodayButton.html
similarity index 64%
copy from tobago-core/src/test/resources/renderer/date/date-today-button.html
copy to tobago-core/src/test/resources/renderer/date/testTodayButton.html
index 2f3b21d..508299c 100644
--- a/tobago-core/src/test/resources/renderer/date/date-today-button.html
+++ b/tobago-core/src/test/resources/renderer/date/testTodayButton.html
@@ -15,11 +15,11 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
+<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0," [...]
   <label for='id::field' class='col-form-label'>Label</label>
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' class='form-control'>
+      <input type='date' name='id' id='id::field' value='1957-10-05' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
     </div>
   </div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-today-button.html b/tobago-core/src/test/resources/renderer/date/text.html
similarity index 59%
rename from tobago-core/src/test/resources/renderer/date/date-today-button.html
rename to tobago-core/src/test/resources/renderer/date/text.html
index 2f3b21d..4a57a46 100644
--- a/tobago-core/src/test/resources/renderer/date/date-today-button.html
+++ b/tobago-core/src/test/resources/renderer/date/text.html
@@ -15,12 +15,12 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
-  <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' date-pattern='dd.mm.yyyy' time-pattern='HH:mm' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fr [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
       <input type='text' name='id' id='id::field' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
     </div>
   </div>
 </tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/zonedDateTimeAuto.html
similarity index 56%
rename from tobago-core/src/test/resources/renderer/date/date-label.html
rename to tobago-core/src/test/resources/renderer/date/zonedDateTimeAuto.html
index 56531f5..1aa041b 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/zonedDateTimeAuto.html
@@ -15,12 +15,12 @@
  * limitations under the License.
 -->
 
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
-  <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
   <div class='tobago-input-group-outer'>
     <div class='input-group'>
-      <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+      <input type='datetime-local' name='id' id='id::field' value='1957-10-05T00:28:34.123' class='form-control'>
       <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+      <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
     </div>
   </div>
 </tobago-date>
\ No newline at end of file
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java
index f29f808..38740cf 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java
@@ -28,6 +28,8 @@ import java.io.Serializable;
 import java.lang.invoke.MethodHandles;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Date;
 
 @RequestScoped
@@ -36,17 +38,25 @@ public class DateController implements Serializable {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  private static final LocalDateTime SPUTNIK_LOCAL_DATE_TIME
+      = LocalDateTime.of(1957, 10, 5, 0, 28, 34, 123456789);
+  private static final LocalDateTime APOLLO11_LOCAL_DATE_TIME
+      = LocalDateTime.of(1969, 7, 20, 20, 17, 40, 123456789);
+
   private Date once;
   private Date onchange;
   private Date submitDate;
 
+  private LocalDateTime sputnikLdt = SPUTNIK_LOCAL_DATE_TIME;
+  private LocalDate sputnikLd = APOLLO11_LOCAL_DATE_TIME.toLocalDate();
+
   public DateController() {
     once = new Date();
     final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
     try {
       submitDate = sdf.parse("2016-05-22");
     } catch (final ParseException e) {
-      LOG.error("", e);
+      LOG.error("Unexpected parse exception", e);
     }
   }
 
@@ -77,4 +87,20 @@ public class DateController implements Serializable {
   public void setSubmitDate(final Date submitDate) {
     this.submitDate = submitDate;
   }
+
+  public LocalDateTime getSputnikLdt() {
+    return sputnikLdt;
+  }
+
+  public void setSputnikLdt(LocalDateTime sputnikLdt) {
+    this.sputnikLdt = sputnikLdt;
+  }
+
+  public LocalDate getSputnikLd() {
+    return sputnikLd;
+  }
+
+  public void setSputnikLd(LocalDate sputnikLd) {
+    this.sputnikLd = sputnikLd;
+  }
 }
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml
index 946a2f9..df69b7e 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml
@@ -42,18 +42,11 @@
         <demo-highlight language="markup">&lt;tc:date label="Date" value="\#{dateController.now}"&gt;
   &lt;f:convertDateTime pattern="dd.MM.yyyy"/&gt;
 &lt;/tc:date&gt;</demo-highlight>
-    <tc:date id="dNormal" label="Date" value="#{dateController.now}">
-      <f:convertDateTime pattern="dd.MM.yyyy"/>
-    </tc:date>
-    <tc:date id="dReadonly" label="Read Only" readonly="true" value="#{dateController.now}">
-      <f:convertDateTime pattern="dd.MM.yyyy"/>
-    </tc:date>
-    <tc:date id="d3" label="Disabled" disabled="true" value="#{dateController.now}">
-      <f:convertDateTime pattern="dd.MM.yyyy"/>
-    </tc:date>
-    <tc:date id="d4" value="#{dateController.now}">
-      <f:convertDateTime pattern="dd.MM.yyyy"/>
-    </tc:date>
+    <tc:date id="dNormal" label="Date" value="#{dateController.sputnikLd}"/>
+    <tc:date id="dReadonly" label="Read Only" readonly="true" value="#{dateController.now}"/>
+    <tc:date id="d3" label="Disabled" disabled="true" value="#{dateController.now}"/>
+      Without a label:
+    <tc:date id="d4" value="#{dateController.now}"/>
   </tc:section>
   <tc:section label="Focus">
     <p>The following date should be selected after reloading the page. This can be done with the attribute
@@ -61,9 +54,7 @@
         <demo-highlight language="markup">&lt;tc:date label="Date (focus)" focus="true"&gt;
   &lt;f:convertDateTime pattern="dd.MM.yyyy"/&gt;
 &lt;/tc:date&gt;</demo-highlight>
-    <tc:date id="d5" label="Date (focus)" focus="true">
-      <f:convertDateTime pattern="dd.MM.yyyy"/>
-    </tc:date>
+    <tc:date id="d5" label="Date (focus)" focus="true"/>
   </tc:section>
 
   <tc:section label="Required">
@@ -73,9 +64,7 @@
     <demo-highlight language="markup">&lt;tc:date label="Date (required)" required="true"
     value="\#{dateController.once}"></demo-highlight>
     <tc:form>
-      <tc:date id="dreq" label="Date (required)" required="true" value="#{dateController.once}">
-        <f:convertDateTime pattern="dd.MM.yyyy"/>
-      </tc:date>
+      <tc:date id="dreq" label="Date (required)" required="true" value="#{dateController.once}"/>
       <tc:button label="Submit"/>
     </tc:form>
   </tc:section>
@@ -195,7 +184,6 @@
       The date can be changed by button or by entering a valid date in the textfield. If the date is not valid,
       it won't be adopted.</p>
     <tc:date id="ajaxinput" label="On Change" value="#{dateController.onchange}">
-      <f:convertDateTime pattern="dd.MM.yyyy"/>
       <f:ajax render="outputfield"/>
     </tc:date>
     <tc:out id="outputfield" label="On Server" value="#{dateController.onchange}">
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
index 082edd7..4838d97 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
@@ -10321,6 +10321,13 @@
           super();
       }
       connectedCallback() {
+          console.debug("input type=date support", DatePicker.SUPPORTS_INPUT_TYPE_DATE);
+          if (!DatePicker.SUPPORTS_INPUT_TYPE_DATE) {
+              this.setAttribute("type", "text");
+              this.initVanillaDatePicker();
+          }
+      }
+      initVanillaDatePicker() {
           var _a;
           const field = this.field;
           const locale = Page.page(this).locale;
@@ -10390,7 +10397,8 @@
           }
       }
       get pattern() {
-          return this.getAttribute("pattern");
+          let pattern = this.getAttribute("pattern");
+          return pattern ? pattern : "yyyy-mm-dd";
       }
       get i18n() {
           const i18n = this.getAttribute("i18n");
@@ -10401,6 +10409,13 @@
           return rootNode.getElementById(this.id + "::field");
       }
   }
+  DatePicker.SUPPORTS_INPUT_TYPE_DATE = (() => {
+      let input = document.createElement('input');
+      input.setAttribute('type', 'date');
+      let thisIsNoDate = 'this is not a date';
+      input.setAttribute('value', thisIsNoDate);
+      return (input.value !== thisIsNoDate);
+  })();
   document.addEventListener("tobago.init", function (event) {
       if (window.customElements.get("tobago-date") == null) {
           window.customElements.define("tobago-date", DatePicker);
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts
index f62aea0..83edcd9 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts
@@ -44,6 +44,14 @@ interface DatePickerOptions {
 
 class DatePicker extends HTMLElement {
 
+  static readonly SUPPORTS_INPUT_TYPE_DATE : boolean = (() => {
+    let input = document.createElement('input');
+    input.setAttribute('type','date');
+    let thisIsNoDate = 'this is not a date';
+    input.setAttribute('value', thisIsNoDate);
+    return (input.value !== thisIsNoDate);
+  })();
+
   lastValue: string;
 
   constructor() {
@@ -51,6 +59,15 @@ class DatePicker extends HTMLElement {
   }
 
   connectedCallback(): void {
+    console.debug("input type=date support", DatePicker.SUPPORTS_INPUT_TYPE_DATE);
+
+    if (!DatePicker.SUPPORTS_INPUT_TYPE_DATE) {
+      this.setAttribute("type", "text");
+      this.initVanillaDatePicker();
+    }
+  }
+
+  initVanillaDatePicker(): void {
     const field = this.field;
     const locale: string = Page.page(this).locale;
 
@@ -132,7 +149,8 @@ class DatePicker extends HTMLElement {
   }
 
   get pattern(): string {
-    return this.getAttribute("pattern");
+    let pattern = this.getAttribute("pattern");
+    return pattern ? pattern : "yyyy-mm-dd";
   }
 
   get i18n(): DatePickerI18n {