Repository: wicket
Updated Branches:
  refs/heads/WICKET-6105-java.time 9ab8f47a9 -> c25e05e31


Initial refactoring of *Date* related converters


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/c25e05e3
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/c25e05e3
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/c25e05e3

Branch: refs/heads/WICKET-6105-java.time
Commit: c25e05e31fccb44d9ac73313daff6a1a0ce8ac32
Parents: 9ab8f47
Author: Maxim Solodovnik <[email protected]>
Authored: Tue Sep 26 20:15:34 2017 +0700
Committer: Maxim Solodovnik <[email protected]>
Committed: Tue Sep 26 20:15:34 2017 +0700

----------------------------------------------------------------------
 .../bean/validation/BeanValidationPage.java     |   8 +-
 .../html/form/datetime/DateConverter.java       | 203 --------------
 .../markup/html/form/datetime/DateField.java    | 235 ++++++++++++++---
 .../markup/html/form/datetime/DateLabel.java    |  14 +-
 .../html/form/datetime/DateTextField.java       | 262 -------------------
 .../html/form/datetime/DateTimeField.html       |   6 +-
 .../html/form/datetime/DateTimeField.java       |  14 +-
 .../html/form/datetime/IDateConverter.java      |  53 ++++
 .../html/form/datetime/LocalDateConverter.java  | 103 ++++++++
 .../form/datetime/PatternDateConverter.java     |   6 +-
 .../html/form/datetime/StyleDateConverter.java  |  24 +-
 .../datetime/StyleZonedDateTimeConverter.java   | 168 ++++++++++++
 .../form/datetime/ZonedDateTimeConverter.java   | 205 +++++++++++++++
 .../html/form/datetime/DateConverterTest.java   |  10 +-
 14 files changed, 769 insertions(+), 542 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-examples/src/main/java/org/apache/wicket/examples/bean/validation/BeanValidationPage.java
----------------------------------------------------------------------
diff --git 
a/wicket-examples/src/main/java/org/apache/wicket/examples/bean/validation/BeanValidationPage.java
 
b/wicket-examples/src/main/java/org/apache/wicket/examples/bean/validation/BeanValidationPage.java
index 01a67a9..b69b019 100644
--- 
a/wicket-examples/src/main/java/org/apache/wicket/examples/bean/validation/BeanValidationPage.java
+++ 
b/wicket-examples/src/main/java/org/apache/wicket/examples/bean/validation/BeanValidationPage.java
@@ -17,8 +17,8 @@
 package org.apache.wicket.examples.bean.validation;
 
 import org.apache.wicket.bean.validation.PropertyValidator;
-import org.apache.wicket.extensions.markup.html.form.datetime.DateTextField;
-import 
org.apache.wicket.extensions.markup.html.form.datetime.StyleDateConverter;
+import org.apache.wicket.extensions.markup.html.form.datetime.DateField;
+import 
org.apache.wicket.extensions.markup.html.form.datetime.StyleZonedDateTimeConverter;
 import org.apache.wicket.examples.WicketExamplePage;
 import org.apache.wicket.feedback.ExactLevelFeedbackMessageFilter;
 import org.apache.wicket.feedback.FeedbackMessage;
@@ -52,8 +52,8 @@ public class BeanValidationPage extends WicketExamplePage
                form.add(new TextField<>("name", new 
PropertyModel<String>(this, "person.name")).add(new PropertyValidator<>()));
                form.add(new TextField<>("phone", new 
PropertyModel<String>(this, "person.phone")).add(new PropertyValidator<>()));
                form.add(new TextField<>("email", new 
PropertyModel<String>(this, "person.email")).add(new PropertyValidator<>()));
-               form.add(new DateTextField("birthdate", new 
PropertyModel<>(this, "person.birthdate"),
-                       new StyleDateConverter("S-", true)).add(new 
PropertyValidator<>()));
+               form.add(new DateField("birthdate", new PropertyModel<>(this, 
"person.birthdate"),
+                       new StyleZonedDateTimeConverter("S-", true)).add(new 
PropertyValidator<>()));
                form.add(new TextField<>("password", new 
PropertyModel<String>(this, "person.password")).add(new PropertyValidator<>()));
                
                add(new FeedbackPanel("feedbackSuccess", new 
ExactLevelFeedbackMessageFilter(FeedbackMessage.INFO)));

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverter.java
deleted file mode 100644
index e667b9e..0000000
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverter.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.extensions.markup.html.form.datetime;
-
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import org.apache.wicket.Session;
-import org.apache.wicket.core.request.ClientInfo;
-import org.apache.wicket.protocol.http.request.WebClientInfo;
-import org.apache.wicket.util.convert.ConversionException;
-import org.apache.wicket.util.convert.IConverter;
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.string.Strings;
-
-
-/**
- * Base class for Joda Time based date converters. It contains the logic to 
parse and format,
- * optionally taking the time zone difference between clients and the server 
into account.
- * <p>
- * Converters of this class are best suited for per-component use.
- * </p>
- * 
- * @author eelcohillenius
- */
-public abstract class DateConverter implements IConverter<ZonedDateTime>
-{
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Whether to apply the time zone difference when interpreting dates.
-        */
-       private final boolean applyTimeZoneDifference;
-
-       /**
-        * Construct. </p> When applyTimeZoneDifference is true, the current 
time is applied on the
-        * parsed date, and the date will be corrected for the time zone 
difference between the server
-        * and the client. For instance, if I'm in Seattle and the server I'm 
working on is in
-        * Amsterdam, the server is 9 hours ahead. So, if I'm inputting say 
12/24 at a couple of hours
-        * before midnight, at the server it is already 12/25. If this boolean 
is true, it will be
-        * transformed to 12/25, while the client sees 12/24. </p>
-        * 
-        * @param applyTimeZoneDifference
-        *            whether to apply the difference in time zones between 
client and server
-        */
-       public DateConverter(boolean applyTimeZoneDifference)
-       {
-               this.applyTimeZoneDifference = applyTimeZoneDifference;
-       }
-
-       protected ZonedDateTime convertToObject(String value, DateTimeFormatter 
format, Locale locale) {
-               try
-               {
-                       // parse date retaining the time of the submission
-                       return ZonedDateTime.parse(value, format);
-               }
-               catch (RuntimeException e)
-               {
-                       throw newConversionException(e, locale);
-               }
-       }
-
-       @Override
-       public ZonedDateTime convertToObject(String value, Locale locale)
-       {
-               if (Strings.isEmpty(value))
-               {
-                       return null;
-               }
-
-               DateTimeFormatter format = getFormat(locale);
-               Args.notNull(format, "format");
-
-               if (applyTimeZoneDifference)
-               {
-                       ZoneId zoneId = getClientTimeZone();
-
-                       // set time zone for client
-                       format = format.withZone(getTimeZone());
-
-                       ZonedDateTime dateTime = convertToObject(value, format, 
locale);
-                       // apply the server time zone to the parsed value
-                       if (zoneId != null)
-                       {
-                               dateTime = dateTime.withZoneSameInstant(zoneId);
-                       }
-
-                       return dateTime;
-               }
-               else
-               {
-                       return convertToObject(value, format, locale);
-               }
-       }
-
-       /**
-        * Creates a ConversionException and sets additional context 
information to it.
-        *
-        * @param cause
-        *            - {@link RuntimeException} cause
-        * @param locale
-        *            - {@link Locale} used to set 'format' variable with 
localized pattern
-        * @return {@link ConversionException}
-        */
-       ConversionException newConversionException(RuntimeException cause, 
Locale locale)
-       {
-               return new ConversionException(cause)
-                               .setVariable("format", getDatePattern(locale));
-       }
-
-       @Override
-       public String convertToString(ZonedDateTime dateTime, Locale locale)
-       {
-               DateTimeFormatter format = getFormat(locale);
-
-               if (applyTimeZoneDifference)
-               {
-                       ZoneId zoneId = getClientTimeZone();
-                       if (zoneId != null)
-                       {
-                               // apply time zone to formatter
-                               format = format.withZone(zoneId);
-                       }
-               }
-               return format.format(dateTime);
-       }
-
-       /**
-        * Gets whether to apply the time zone difference when interpreting 
dates.
-        * 
-        * </p> When true, the current time is applied on the parsed date, and 
the date will be
-        * corrected for the time zone difference between the server and the 
client. For instance, if
-        * I'm in Seattle and the server I'm working on is in Amsterdam, the 
server is 9 hours ahead.
-        * So, if I'm inputting say 12/24 at a couple of hours before midnight, 
at the server it is
-        * already 12/25. If this boolean is true, it will be transformed to 
12/25, while the client
-        * sees 12/24. </p>
-        * 
-        * @return whether to apply the difference in time zones between client 
and server
-        */
-       public final boolean getApplyTimeZoneDifference()
-       {
-               return applyTimeZoneDifference;
-       }
-
-       /**
-        * @param locale
-        *            The locale used to convert the value
-        * @return Gets the pattern that is used for printing and parsing
-        */
-       public abstract String getDatePattern(Locale locale);
-
-       /**
-        * Gets the client's time zone.
-        * 
-        * @return The client's time zone or null
-        */
-       protected ZoneId getClientTimeZone()
-       {
-               ClientInfo info = Session.get().getClientInfo();
-               if (info instanceof WebClientInfo)
-               {
-                       TimeZone timeZone = ((WebClientInfo) 
info).getProperties().getTimeZone();
-                       return timeZone.toZoneId();
-               }
-               return null;
-       }
-
-       /**
-        * @param locale
-        *            The locale used to convert the value
-        * 
-        * @return formatter The formatter for the current conversion
-        */
-       protected abstract DateTimeFormatter getFormat(Locale locale);
-
-       /**
-        * Gets the server time zone. Override this method if you want to fix 
to a certain time zone,
-        * regardless of what actual time zone the server is in.
-        * 
-        * @return The server time zone
-        */
-       protected ZoneId getTimeZone()
-       {
-               return ZoneId.systemDefault();
-       }
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateField.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateField.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateField.java
index 4038378..d4b5ef4 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateField.java
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateField.java
@@ -16,65 +16,238 @@
  */
 package org.apache.wicket.extensions.markup.html.form.datetime;
 
-import java.time.ZonedDateTime;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.format.FormatStyle;
 
+import 
org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider;
+import org.apache.wicket.markup.html.form.TextField;
 import org.apache.wicket.model.IModel;
+import org.apache.wicket.util.convert.IConverter;
+import org.apache.wicket.util.lang.Args;
 
 /**
- * Works on a {@link java.time.ZonedDateTime} object. Displays a {@link 
DateTextField} and a
- * {@link DatePicker calendar popup}.<br/>
+ * A TextField that is mapped to a <code>java.time.LocalDate</code> object and 
that uses java.time time to
+ * parse and format values.
  * <p>
- * Note: {@link DateField} must <strong>not</strong> be associated with an
- * <code>&lt;input&gt;</code> tag, as opposed to {@link DateTextField}! The 
corresponding tag is
- * typically either a <code>&lt;div&gt;</code> or a <code>&lt;span&gt;</code> 
tag.
+ * You should use on of the factory methods to construct the kind you want or 
use the public
+ * constructor and pass in the converter to use.
  * </p>
- * 
- * Example:
  * <p>
- * <u>Java:</u>
- * 
- * <pre>
- * DateField dateField = new DateField(&quot;birthday&quot;);
- * </pre>
- * 
+ * This component tries to apply the time zone difference between the client 
and server. See the
+ * {@link ZonedDateTimeConverter#getApplyTimeZoneDifference() date converter} 
of this package for more
+ * information on that.
  * </p>
- * <p>
- * <u>Markup:</u>
- * 
- * <pre>
- * &lt;div wicket:id=&quot;birthday&quot;&gt;&lt;/div&gt;
- * </pre>
  * 
- * </p>
+ * @see StyleZonedDateTimeConverter
+ * @see java.time.ZonedDateTime
+ * @see java.time.format.DateTimeFormatter
+ * @see java.time.ZoneId
  * 
  * @author eelcohillenius
  */
-public class DateField extends DateTimeField
+public class DateField extends TextField<LocalDate> implements 
ITextFormatProvider
 {
        private static final long serialVersionUID = 1L;
 
        /**
-        * Construct.
+        * Creates a new DateTextField defaulting to using a short date pattern
+        * 
+        * @param id
+        *            The id of the text field
+        * @param model
+        *            The model
+        * @param datePattern
+        *            The pattern to use. Must be not null. See {@link 
SimpleDateFormat} for available
+        *            patterns.
+        * @return DateTextField
+        */
+       public static DateField forDatePattern(String id, IModel<LocalDate> 
model, String datePattern)
+       {
+               return new DateField(id, model, new 
PatternDateConverter(datePattern, true));
+       }
+
+       /**
+        * Creates a new DateTextField defaulting to using a short date pattern
+        * 
+        * @param id
+        *            The id of the text field
+        * @param datePattern
+        *            The pattern to use. Must be not null. See {@link 
SimpleDateFormat} for available
+        *            patterns.
+        * @return DateTextField
+        */
+       public static DateField forDatePattern(String id, String datePattern)
+       {
+               return forDatePattern(id, null, datePattern);
+       }
+
+       /**
+        * Creates a new DateTextField using the provided date style.
+        * 
+        * @param id
+        *            The id of the text field
+        * @param model
+        *            The model
+        * @param dateStyle
+        *            Date style to use. The first character is the date style, 
and the second character
+        *            is the time style. Specify a character of 'S' for short 
style, 'M' for medium, 'L'
+        *            for long, and 'F' for full. A date or time may be 
ommitted by specifying a style
+        *            character '-'. See {@link 
org.joda.time.DateTimeFormat#forStyle(String)}.
+        * @return DateTextField
+        */
+       public static DateField forDateStyle(String id, IModel<LocalDate> 
model, String dateStyle)
+       {
+               FormatStyle dateFormatStyle = 
parseFormatStyle(dateStyle.charAt(0));
+               return new DateField(id, model, new 
StyleZonedDateTimeConverter(dateStyle, null, false));
+       }
+
+       /**
+        * Creates a new DateTextField using the provided date style.
+        * 
+        * @param id
+        *            The id of the text field
+        * @param dateStyle
+        *            Date style to use. The first character is the date style, 
and the second character
+        *            is the time style. Specify a character of 'S' for short 
style, 'M' for medium, 'L'
+        *            for long, and 'F' for full. A date or time may be 
ommitted by specifying a style
+        *            character '-'. See {@link 
org.joda.time.DateTimeFormat#forStyle(String)}.
+        * @return DateTextField
+        */
+       public static DateField forDateStyle(String id, String dateStyle)
+       {
+               return forDateStyle(id, null, dateStyle);
+       }
+
+       /**
+        * Creates a new DateTextField defaulting to using a short date pattern
+        * 
+        * @param id
+        *            The id of the text field
+        * @return DateTextField
+        */
+       public static DateField forShortStyle(String id)
+       {
+               return forShortStyle(id, null);
+       }
+
+       /**
+        * Creates a new DateTextField defaulting to using a short date pattern
         * 
         * @param id
+        *            The id of the text field
+        * @param model
+        *            The model
+        * @return DateTextField
         */
-       public DateField(String id)
+       public static DateField forShortStyle(String id, IModel<LocalDate> 
model)
        {
-               this(id, null);
+               return new DateField(id, model, new 
StyleZonedDateTimeConverter(false));
        }
 
        /**
-        * Construct.
+        * Creates a new DateTextField using the provided converter.
+        * 
+        * @param id
+        *            The id of the text field
+        * @param converter
+        *            the date converter
+        * @return DateTextField
+        */
+       public static DateField withConverter(String id, 
IDateConverter<LocalDate> converter)
+       {
+               return withConverter(id, null, converter);
+       }
+
+       /**
+        * Creates a new DateTextField using the provided converter.
+        * 
+        * @param id
+        *            The id of the text field
+        * @param model
+        *            The model
+        * @param converter
+        *            the date converter
+        * @return DateTextField
+        */
+       public static DateField withConverter(String id, IModel<LocalDate> 
model, IDateConverter<LocalDate> converter)
+       {
+               return new DateField(id, model, converter);
+       }
+
+       /**
+        * The converter for the TextField
+        */
+       private final IDateConverter<LocalDate> converter;
+
+       /**
+        * Construct with a converter.
         * 
         * @param id
+        *            The component id
         * @param model
+        *            The model
+        * @param converter
+        *            The converter to use
         */
-       public DateField(String id, IModel<ZonedDateTime> model)
+       public DateField(String id, IModel<LocalDate> model, 
IDateConverter<LocalDate> converter)
        {
-               super(id, model);
+               super(id, model, LocalDate.class);
 
-               get(HOURS).setVisibilityAllowed(false);
-               get(MINUTES).setVisibilityAllowed(false);
-               get(AM_OR_PM_CHOICE).setVisibilityAllowed(false);
+               Args.notNull(converter, "converter");
+               this.converter = converter;
+       }
+
+       /**
+        * Construct with a converter, and a null model.
+        * 
+        * @param id
+        *            The component id
+        * @param converter
+        *            The converter to use
+        */
+       public DateField(String id, IDateConverter<LocalDate> converter)
+       {
+               this(id, null, converter);
+       }
+
+       /**
+        * @return The specialized converter.
+        * @see org.apache.wicket.Component#createConverter(java.lang.Class)
+        */
+       @Override
+       protected IConverter<?> createConverter(Class<?> clazz)
+       {
+               if (LocalDate.class.isAssignableFrom(clazz))
+               {
+                       return converter;
+               }
+               return null;
+       }
+
+       /**
+        * @see 
org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider#getTextFormat()
+        */
+       @Override
+       public final String getTextFormat()
+       {
+               return converter.getPattern(getLocale());
+       }
+
+       public static FormatStyle parseFormatStyle(char style)
+       {
+               switch (style)
+               {
+                       case 'S':
+                               return FormatStyle.SHORT;
+                       case 'M':
+                               return FormatStyle.MEDIUM;
+                       case 'L':
+                               return FormatStyle.LONG;
+                       case 'F':
+                               return FormatStyle.FULL;
+                       default:
+                               return null;
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateLabel.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateLabel.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateLabel.java
index 8bbf578..957e53e 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateLabel.java
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateLabel.java
@@ -99,7 +99,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
         */
        public static DateLabel forDateStyle(String id, IModel<LocalDate> 
model, FormatStyle dateStyle)
        {
-               return new DateLabel(id, model, new 
StyleDateConverter(dateStyle, null, true));
+               return new DateLabel(id, model, new 
StyleZonedDateTimeConverter(dateStyle, null, true));
        }
 
        /**
@@ -145,7 +145,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
         */
        public static DateLabel forShortStyle(String id, IModel<LocalDate> 
model)
        {
-               return new DateLabel(id, model, new StyleDateConverter(true));
+               return new DateLabel(id, model, new 
StyleZonedDateTimeConverter(true));
        }
 
        /**
@@ -159,7 +159,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
         * 
         * @see org.apache.wicket.markup.html.form.TextField
         */
-       public static DateLabel withConverter(String id, DateConverter 
converter)
+       public static DateLabel withConverter(String id, ZonedDateTimeConverter 
converter)
        {
                return withConverter(id, null, converter);
        }
@@ -177,7 +177,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
         * 
         * @see org.apache.wicket.markup.html.form.TextField
         */
-       public static DateLabel withConverter(String id, IModel<LocalDate> 
model, DateConverter converter)
+       public static DateLabel withConverter(String id, IModel<LocalDate> 
model, ZonedDateTimeConverter converter)
        {
                return new DateLabel(id, model, converter);
        }
@@ -191,7 +191,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
        /**
         * The converter for the Label
         */
-       private final DateConverter converter;
+       private final ZonedDateTimeConverter converter;
 
        /**
         * Construct with a converter.
@@ -201,7 +201,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
         * @param converter
         *            The converter to use
         */
-       public DateLabel(String id, DateConverter converter)
+       public DateLabel(String id, ZonedDateTimeConverter converter)
        {
                this(id, null, converter);
        }
@@ -216,7 +216,7 @@ public class DateLabel extends Label implements 
IGenericComponent<LocalDate, Dat
         * @param converter
         *            The converter to use
         */
-       public DateLabel(String id, IModel<LocalDate> model, DateConverter 
converter)
+       public DateLabel(String id, IModel<LocalDate> model, 
ZonedDateTimeConverter converter)
        {
                super(id, model);
                this.converter = Args.notNull(converter, "converter");

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTextField.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTextField.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTextField.java
deleted file mode 100644
index 1e86ea5..0000000
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTextField.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.extensions.markup.html.form.datetime;
-
-import java.text.SimpleDateFormat;
-import java.time.LocalDate;
-import java.time.format.FormatStyle;
-import java.util.Date;
-
-import 
org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider;
-import org.apache.wicket.markup.html.form.TextField;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.util.convert.IConverter;
-import org.apache.wicket.util.lang.Args;
-
-/**
- * A TextField that is mapped to a <code>java.time.LocalDate</code> object and 
that uses java.time time to
- * parse and format values.
- * <p>
- * You should use on of the factory methods to construct the kind you want or 
use the public
- * constructor and pass in the converter to use.
- * </p>
- * <p>
- * This component tries to apply the time zone difference between the client 
and server. See the
- * {@link DateConverter#getApplyTimeZoneDifference() date converter} of this 
package for more
- * information on that.
- * </p>
- * 
- * @see StyleDateConverter
- * @see java.time.ZonedDateTime
- * @see java.time.format.DateTimeFormatter
- * @see java.time.ZoneId
- * 
- * @author eelcohillenius
- */
-public class DateTextField extends TextField<LocalDate> implements 
ITextFormatProvider
-{
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Creates a new DateTextField defaulting to using a short date pattern
-        * 
-        * @param id
-        *            The id of the text field
-        * @param model
-        *            The model
-        * @param datePattern
-        *            The pattern to use. Must be not null. See {@link 
SimpleDateFormat} for available
-        *            patterns.
-        * @return DateTextField
-        */
-       public static DateTextField forDatePattern(String id, IModel<LocalDate> 
model, String datePattern)
-       {
-               return new DateTextField(id, model, new 
PatternDateConverter(datePattern, true));
-       }
-
-       /**
-        * Creates a new DateTextField defaulting to using a short date pattern
-        * 
-        * @param id
-        *            The id of the text field
-        * @param datePattern
-        *            The pattern to use. Must be not null. See {@link 
SimpleDateFormat} for available
-        *            patterns.
-        * @return DateTextField
-        */
-       public static DateTextField forDatePattern(String id, String 
datePattern)
-       {
-               return forDatePattern(id, null, datePattern);
-       }
-
-       /**
-        * Creates a new DateTextField using the provided date style.
-        * 
-        * @param id
-        *            The id of the text field
-        * @param model
-        *            The model
-        * @param dateStyle
-        *            Date style to use. The first character is the date style, 
and the second character
-        *            is the time style. Specify a character of 'S' for short 
style, 'M' for medium, 'L'
-        *            for long, and 'F' for full. A date or time may be 
ommitted by specifying a style
-        *            character '-'. See {@link 
org.joda.time.DateTimeFormat#forStyle(String)}.
-        * @return DateTextField
-        */
-       public static DateTextField forDateStyle(String id, IModel<LocalDate> 
model, String dateStyle)
-       {
-               FormatStyle dateFormatStyle = 
parseFormatStyle(dateStyle.charAt(0));
-               return forDateStyle(id, model, dateFormatStyle, null);
-       }
-
-       public static DateTextField forDateStyle(String id, IModel<LocalDate> 
model, FormatStyle dateStyle, FormatStyle timeStyle)
-       {
-               return new DateTextField(id, model, new 
StyleDateConverter(dateStyle, timeStyle, true));
-       }
-
-       /**
-        * Creates a new DateTextField using the provided date style.
-        * 
-        * @param id
-        *            The id of the text field
-        * @param dateStyle
-        *            Date style to use. The first character is the date style, 
and the second character
-        *            is the time style. Specify a character of 'S' for short 
style, 'M' for medium, 'L'
-        *            for long, and 'F' for full. A date or time may be 
ommitted by specifying a style
-        *            character '-'. See {@link 
org.joda.time.DateTimeFormat#forStyle(String)}.
-        * @return DateTextField
-        */
-       public static DateTextField forDateStyle(String id, String dateStyle)
-       {
-               return forDateStyle(id, null, dateStyle);
-       }
-
-       /**
-        * Creates a new DateTextField defaulting to using a short date pattern
-        * 
-        * @param id
-        *            The id of the text field
-        * @return DateTextField
-        */
-       public static DateTextField forShortStyle(String id)
-       {
-               return forShortStyle(id, null, true);
-       }
-
-       /**
-        * Creates a new DateTextField defaulting to using a short date pattern
-        * 
-        * @param id
-        *            The id of the text field
-        * @param model
-        *            The model
-        * @param applyTimeZoneDifference
-        *            Whether to apply the time zone difference between client 
and server
-        * @return DateTextField
-        */
-       public static DateTextField forShortStyle(String id, IModel<LocalDate> 
model,
-               boolean applyTimeZoneDifference)
-       {
-               return new DateTextField(id, model, new 
StyleDateConverter(applyTimeZoneDifference));
-       }
-
-       /**
-        * Creates a new DateTextField using the provided converter.
-        * 
-        * @param id
-        *            The id of the text field
-        * @param converter
-        *            the date converter
-        * @return DateTextField
-        */
-       public static DateTextField withConverter(String id, DateConverter 
converter)
-       {
-               return withConverter(id, null, converter);
-       }
-
-       /**
-        * Creates a new DateTextField using the provided converter.
-        * 
-        * @param id
-        *            The id of the text field
-        * @param model
-        *            The model
-        * @param converter
-        *            the date converter
-        * @return DateTextField
-        */
-       public static DateTextField withConverter(String id, IModel<LocalDate> 
model, DateConverter converter)
-       {
-               return new DateTextField(id, model, converter);
-       }
-
-       /**
-        * The converter for the TextField
-        */
-       private final DateConverter converter;
-
-       /**
-        * Construct with a converter.
-        * 
-        * @param id
-        *            The component id
-        * @param model
-        *            The model
-        * @param converter
-        *            The converter to use
-        */
-       public DateTextField(String id, IModel<LocalDate> model, DateConverter 
converter)
-       {
-               super(id, model, LocalDate.class);
-
-               Args.notNull(converter, "converter");
-               this.converter = converter;
-       }
-
-       /**
-        * Construct with a converter, and a null model.
-        * 
-        * @param id
-        *            The component id
-        * @param converter
-        *            The converter to use
-        */
-       public DateTextField(String id, DateConverter converter)
-       {
-               this(id, null, converter);
-       }
-
-       /**
-        * @return The specialized converter.
-        * @see org.apache.wicket.Component#createConverter(java.lang.Class)
-        */
-       @Override
-       protected IConverter<?> createConverter(Class<?> clazz)
-       {
-               if (Date.class.isAssignableFrom(clazz))
-               {
-                       return converter;
-               }
-               return null;
-       }
-
-       /**
-        * @see 
org.apache.wicket.markup.html.form.AbstractTextComponent.ITextFormatProvider#getTextFormat()
-        */
-       @Override
-       public final String getTextFormat()
-       {
-               return converter.getDatePattern(getLocale());
-       }
-
-       public static FormatStyle parseFormatStyle(char style)
-       {
-               switch (style)
-               {
-                       case 'S':
-                               return FormatStyle.SHORT;
-                       case 'M':
-                               return FormatStyle.MEDIUM;
-                       case 'L':
-                               return FormatStyle.LONG;
-                       case 'F':
-                               return FormatStyle.FULL;
-                       default:
-                               return null;
-               }
-       }
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.html
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.html
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.html
index c714db3..8bcd43b 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.html
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.html
@@ -18,9 +18,9 @@
 <wicket:panel xmlns:wicket="http://wicket.apache.org";>
   <span style="white-space: nowrap;">
     <input type="text" wicket:id="date" size="12" />
-       <input type="text" wicket:id="hours" size="2" maxlength="2" />
-       <span wicket:id="hoursSeparator">&#160;:</span>
+    <input type="text" wicket:id="hours" size="2" maxlength="2" />
+    <span wicket:id="hoursSeparator">&#160;:</span>
     <input type="text" wicket:id="minutes" size="2" maxlength="2" />
     <select wicket:id="amOrPmChoice"></select>
   </span>
-</wicket:panel>
\ No newline at end of file
+</wicket:panel>

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.java
index 863a306..eff009a 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.java
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/DateTimeField.java
@@ -52,7 +52,7 @@ import org.apache.wicket.validation.validator.RangeValidator;
  * <p>
  * <strong>Ajaxifying the DateTimeField</strong>: If you want to update a 
DateTimeField with an
  * {@link AjaxFormComponentUpdatingBehavior}, you have to attach it to the 
contained
- * {@link DateTextField} by overriding {@link #newDateTextField(String, 
IModel)} and calling
+ * {@link DateField} by overriding {@link #newDateTextField(String, IModel)} 
and calling
  * {@link #processInput()}:
  * 
  * <pre>{@code
@@ -126,7 +126,7 @@ public class DateTimeField extends 
FormComponentPanel<ZonedDateTime>
 
        // The date TextField and it's associated model object
        // Note that any time information in date will be ignored
-       private DateTextField dateField;
+       private DateField dateField;
        private ZonedDateTime dateTime = ZonedDateTime.now();
 
        // The TextField for "hours" and it's associated model object
@@ -241,7 +241,7 @@ public class DateTimeField extends 
FormComponentPanel<ZonedDateTime>
         * 
         * @return The date TextField
         */
-       protected final DateTextField getDateTextField()
+       protected final DateField getDateTextField()
        {
                return dateField;
        }
@@ -379,17 +379,17 @@ public class DateTimeField extends 
FormComponentPanel<ZonedDateTime>
        }
 
        /**
-        * create a new {@link DateTextField} instance to be added to this 
panel.
+        * create a new {@link DateField} instance to be added to this panel.
         * 
         * @param id
         *            the component id
         * @param dateFieldModel
-        *            model that should be used by the {@link DateTextField}
+        *            model that should be used by the {@link DateField}
         * @return a new date text field instance
         */
-       protected DateTextField newDateTextField(String id, IModel<LocalDate> 
dateFieldModel)
+       protected DateField newDateTextField(String id, IModel<LocalDate> 
dateFieldModel)
        {
-               return DateTextField.forShortStyle(id, dateFieldModel, false);
+               return DateField.forShortStyle(id, dateFieldModel);
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/IDateConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/IDateConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/IDateConverter.java
new file mode 100644
index 0000000..6312716
--- /dev/null
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/IDateConverter.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.extensions.markup.html.form.datetime;
+
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.apache.wicket.util.convert.IConverter;
+
+
+/**
+ * Base class for Joda Time based date converters. It contains the logic to 
parse and format,
+ * optionally taking the time zone difference between clients and the server 
into account.
+ * <p>
+ * Converters of this class are best suited for per-component use.
+ * </p>
+ * 
+ * @author eelcohillenius
+ */
+public interface IDateConverter<T> extends IConverter<T>
+{
+
+       T convertToObject(String value, DateTimeFormatter format, Locale 
locale);
+
+       /**
+        * @param locale
+        *            The locale used to convert the value
+        * @return Gets the pattern that is used for printing and parsing
+        */
+       String getPattern(Locale locale);
+
+       /**
+        * @param locale
+        *            The locale used to convert the value
+        * 
+        * @return formatter The formatter for the current conversion
+        */
+       DateTimeFormatter getFormat(Locale locale);
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/LocalDateConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/LocalDateConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/LocalDateConverter.java
new file mode 100644
index 0000000..9306d8d
--- /dev/null
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/LocalDateConverter.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.extensions.markup.html.form.datetime;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.apache.wicket.util.convert.ConversionException;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.string.Strings;
+
+
+/**
+ * Base class for Joda Time based date converters. It contains the logic to 
parse and format,
+ * optionally taking the time zone difference between clients and the server 
into account.
+ * <p>
+ * Converters of this class are best suited for per-component use.
+ * </p>
+ * 
+ * @author eelcohillenius
+ */
+public abstract class LocalDateConverter implements IDateConverter<LocalDate>
+{
+       private static final long serialVersionUID = 1L;
+
+       public LocalDate convertToObject(String value, DateTimeFormatter 
format, Locale locale) {
+               try
+               {
+                       // parse date retaining the time of the submission
+                       return LocalDate.parse(value, format);
+               }
+               catch (RuntimeException e)
+               {
+                       throw newConversionException(e, locale);
+               }
+       }
+
+       @Override
+       public LocalDate convertToObject(String value, Locale locale)
+       {
+               if (Strings.isEmpty(value))
+               {
+                       return null;
+               }
+
+               DateTimeFormatter format = getFormat(locale);
+               Args.notNull(format, "format");
+
+               return convertToObject(value, format, locale);
+       }
+
+       /**
+        * Creates a ConversionException and sets additional context 
information to it.
+        *
+        * @param cause
+        *            - {@link RuntimeException} cause
+        * @param locale
+        *            - {@link Locale} used to set 'format' variable with 
localized pattern
+        * @return {@link ConversionException}
+        */
+       ConversionException newConversionException(RuntimeException cause, 
Locale locale)
+       {
+               return new ConversionException(cause)
+                               .setVariable("format", getPattern(locale));
+       }
+
+       @Override
+       public String convertToString(LocalDate dateTime, Locale locale)
+       {
+               DateTimeFormatter format = getFormat(locale);
+               return format.format(dateTime);
+       }
+
+       /**
+        * @param locale
+        *            The locale used to convert the value
+        * @return Gets the pattern that is used for printing and parsing
+        */
+       public abstract String getPattern(Locale locale);
+
+       /**
+        * @param locale
+        *            The locale used to convert the value
+        * 
+        * @return formatter The formatter for the current conversion
+        */
+       public abstract DateTimeFormatter getFormat(Locale locale);
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/PatternDateConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/PatternDateConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/PatternDateConverter.java
index 8358186..e2b714f 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/PatternDateConverter.java
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/PatternDateConverter.java
@@ -33,7 +33,7 @@ import org.apache.wicket.util.lang.Args;
  * </p>
  * 
  * @see SimpleDateFormat
- * @see StyleDateConverter
+ * @see StyleZonedDateTimeConverter
  * @see org.apache.wicket.extensions.markup.html.form.DateTextField
  * @see java.time.ZonedDateTime
  * @see DateTimeFormatter
@@ -41,7 +41,7 @@ import org.apache.wicket.util.lang.Args;
  * 
  * @author eelcohillenius
  */
-public class PatternDateConverter extends DateConverter
+public class PatternDateConverter extends ZonedDateTimeConverter
 {
 
        private static final long serialVersionUID = 1L;
@@ -81,7 +81,7 @@ public class PatternDateConverter extends DateConverter
         * @return datePattern
         */
        @Override
-       public final String getDatePattern(Locale locale)
+       public final String getPattern(Locale locale)
        {
                return datePattern;
        }

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleDateConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleDateConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleDateConverter.java
index 574eaf2..14e6d22 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleDateConverter.java
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleDateConverter.java
@@ -39,7 +39,7 @@ import java.util.Locale;
  * 
  * @author eelcohillenius
  */
-public class StyleDateConverter extends DateConverter
+public class StyleDateConverter extends LocalDateConverter
 {
        private static final long serialVersionUID = 1L;
 
@@ -48,24 +48,14 @@ public class StyleDateConverter extends DateConverter
         */
        private final FormatStyle dateStyle;
 
-       private final FormatStyle timeStyle;
-
        /**
         * Construct. The dateStyle 'S-' (which is the same as {@link 
DateTimeFormatter#ofLocalizedDate(FormatStyle)}) will
-        * be used for constructing the date format for the current locale. 
</p> When
-        * applyTimeZoneDifference is true, the current time is applied on the 
parsed date, and the date
-        * will be corrected for the time zone difference between the server 
and the client. For
-        * instance, if I'm in Seattle and the server I'm working on is in 
Amsterdam, the server is 9
-        * hours ahead. So, if I'm inputting say 12/24 at a couple of hours 
before midnight, at the
-        * server it is already 12/25. If this boolean is true, it will be 
transformed to 12/25, while
-        * the client sees 12/24. </p>
+        * be used for constructing the date format for the current locale.
         * 
-        * @param applyTimeZoneDifference
-        *            whether to apply the difference in time zones between 
client and server
         */
-       public StyleDateConverter(boolean applyTimeZoneDifference)
+       public StyleDateConverter()
        {
-               this(FormatStyle.SHORT, null, applyTimeZoneDifference);
+               this(FormatStyle.SHORT);
        }
 
        /**
@@ -105,7 +95,7 @@ public class StyleDateConverter extends DateConverter
         * @return datePattern
         */
        @Override
-       public final String getDatePattern(Locale locale)
+       public final String getPattern(Locale locale)
        {
                String localizedDateTimePattern = 
DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, 
IsoChronology.INSTANCE, locale);
                return localizedDateTimePattern;
@@ -138,11 +128,11 @@ public class StyleDateConverter extends DateConverter
 
        public static FormatStyle parseFormatStyle(char style)
        {
-               return DateTextField.parseFormatStyle(style);
+               return DateField.parseFormatStyle(style);
        }
 
        @Override
-       protected ZonedDateTime convertToObject(String value, DateTimeFormatter 
format, Locale locale) {
+       public ZonedDateTime convertToObject(String value, DateTimeFormatter 
format, Locale locale) {
                if (format == null) {
                        return null;
                }

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleZonedDateTimeConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleZonedDateTimeConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleZonedDateTimeConverter.java
new file mode 100644
index 0000000..cb0f5be
--- /dev/null
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/StyleZonedDateTimeConverter.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.extensions.markup.html.form.datetime;
+
+import java.time.LocalDate;
+import java.time.ZonedDateTime;
+import java.time.chrono.IsoChronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.FormatStyle;
+import java.util.Locale;
+
+/**
+ * Date converter that uses Joda Time and can be configured to take the time 
zone difference between
+ * clients and server into account, and that is configured for a certain date 
style. The pattern
+ * will always be locale specific.
+ * <p>
+ * This converter is especially suited on a per-component base.
+ * </p>
+ * 
+ * @see org.apache.wicket.extensions.markup.html.form.DateTextField
+ * @see java.time.ZonedDateTime
+ * @see DateTimeFormatter
+ * @see java.time.ZoneId
+ * 
+ * @author eelcohillenius
+ */
+public class StyleZonedDateTimeConverter extends ZonedDateTimeConverter
+{
+       private static final long serialVersionUID = 1L;
+
+       /**
+        * Date style to use. See {@link 
DateTimeFormatter#ofLocalizedDate(FormatStyle)}.
+        */
+       private final FormatStyle dateStyle;
+
+       private final FormatStyle timeStyle;
+
+       /**
+        * Construct. The dateStyle 'S-' (which is the same as {@link 
DateTimeFormatter#ofLocalizedDate(FormatStyle)}) will
+        * be used for constructing the date format for the current locale. 
</p> When
+        * applyTimeZoneDifference is true, the current time is applied on the 
parsed date, and the date
+        * will be corrected for the time zone difference between the server 
and the client. For
+        * instance, if I'm in Seattle and the server I'm working on is in 
Amsterdam, the server is 9
+        * hours ahead. So, if I'm inputting say 12/24 at a couple of hours 
before midnight, at the
+        * server it is already 12/25. If this boolean is true, it will be 
transformed to 12/25, while
+        * the client sees 12/24. </p>
+        * 
+        * @param applyTimeZoneDifference
+        *            whether to apply the difference in time zones between 
client and server
+        */
+       public StyleZonedDateTimeConverter(boolean applyTimeZoneDifference)
+       {
+               this(FormatStyle.SHORT, null, applyTimeZoneDifference);
+       }
+
+       /**
+        * Construct. The provided pattern will be used as the base format (but 
they will be localized
+        * for the current locale) and if null, {@link 
DateTimeFormatter#ofLocalizedDate(FormatStyle)} will be used. </p>
+        * When applyTimeZoneDifference is true, the current time is applied on 
the parsed date, and the
+        * date will be corrected for the time zone difference between the 
server and the client. For
+        * instance, if I'm in Seattle and the server I'm working on is in 
Amsterdam, the server is 9
+        * hours ahead. So, if I'm inputting say 12/24 at a couple of hours 
before midnight, at the
+        * server it is already 12/25. If this boolean is true, it will be 
transformed to 12/25, while
+        * the client sees 12/24. </p>
+        * 
+        * @param dateStyle
+        *            Date style to use. See {@link 
DateTimeFormatter#ofLocalizedDate(FormatStyle)}.
+        * @param timeStyle
+        *            Time style to use. See {@link 
DateTimeFormatter#ofLocalizedTime(FormatStyle)}
+        * @param applyTimeZoneDifference
+        *            whether to apply the difference in time zones between 
client and server
+        * @throws IllegalArgumentException
+        *             in case dateStyle is null
+        */
+       public StyleZonedDateTimeConverter(FormatStyle dateStyle, FormatStyle 
timeStyle, boolean applyTimeZoneDifference)
+       {
+               super(applyTimeZoneDifference);
+               this.dateStyle = dateStyle;
+               this.timeStyle = timeStyle;
+       }
+
+       public StyleZonedDateTimeConverter(String dateTimeStyle, boolean 
applyTimeZoneDifference)
+       {
+               this(parseFormatStyle(dateTimeStyle.charAt(0)), 
parseFormatStyle(dateTimeStyle.charAt(1)), applyTimeZoneDifference);
+       }
+
+       /**
+        * Gets the optional date pattern.
+        * 
+        * @return datePattern
+        */
+       @Override
+       public final String getPattern(Locale locale)
+       {
+               String localizedDateTimePattern = 
DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, 
IsoChronology.INSTANCE, locale);
+               return localizedDateTimePattern;
+       }
+
+       /**
+        * @return formatter The formatter for the current conversion
+        */
+       @Override
+       public DateTimeFormatter getFormat(Locale locale)
+       {
+               DateTimeFormatter df = null;
+               if (dateStyle == null && timeStyle == null) {
+                       return df;
+               }
+               if (timeStyle == null)
+               {
+                       df = DateTimeFormatter.ofLocalizedDate(dateStyle);
+               }
+               else if (dateStyle == null)
+               {
+                       df = DateTimeFormatter.ofLocalizedTime(timeStyle);
+               }
+               else
+               {
+                       df = DateTimeFormatter.ofLocalizedDateTime(dateStyle, 
timeStyle);
+               }
+               return df.withLocale(locale);
+       }
+
+       public static FormatStyle parseFormatStyle(char style)
+       {
+               return DateField.parseFormatStyle(style);
+       }
+
+       @Override
+       public ZonedDateTime convertToObject(String value, DateTimeFormatter 
format, Locale locale) {
+               if (format == null) {
+                       return null;
+               }
+               try
+               {
+                       if (timeStyle == null)
+                       {
+                               LocalDate d = LocalDate.parse(value, format);
+                               return ZonedDateTime.of(d.atStartOfDay(), 
getTimeZone());
+                       }
+                       else if (dateStyle == null)
+                       {
+                               // not sure how we can get ZonedDateTime from 
time
+                               return null;
+                       }
+                       return super.convertToObject(value, format, locale);
+               }
+               catch (RuntimeException e)
+               {
+                       throw newConversionException(e, locale);
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/ZonedDateTimeConverter.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/ZonedDateTimeConverter.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/ZonedDateTimeConverter.java
new file mode 100644
index 0000000..9d0f430
--- /dev/null
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/form/datetime/ZonedDateTimeConverter.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.extensions.markup.html.form.datetime;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.wicket.Session;
+import org.apache.wicket.core.request.ClientInfo;
+import org.apache.wicket.protocol.http.request.WebClientInfo;
+import org.apache.wicket.util.convert.ConversionException;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.string.Strings;
+
+
+/**
+ * Base class for Joda Time based date converters. It contains the logic to 
parse and format,
+ * optionally taking the time zone difference between clients and the server 
into account.
+ * <p>
+ * Converters of this class are best suited for per-component use.
+ * </p>
+ * 
+ * @author eelcohillenius
+ */
+public abstract class ZonedDateTimeConverter implements 
IDateConverter<ZonedDateTime>
+{
+       private static final long serialVersionUID = 1L;
+
+       /**
+        * Whether to apply the time zone difference when interpreting dates.
+        */
+       private final boolean applyTimeZoneDifference;
+
+       /**
+        * Construct. <p> When applyTimeZoneDifference is true, the current 
time is applied on the
+        * parsed date, and the date will be corrected for the time zone 
difference between the server
+        * and the client. For instance, if I'm in Seattle and the server I'm 
working on is in
+        * Amsterdam, the server is 9 hours ahead. So, if I'm inputting say 
12/24 at a couple of hours
+        * before midnight, at the server it is already 12/25. If this boolean 
is true, it will be
+        * transformed to 12/25, while the client sees 12/24. </p>
+        * 
+        * @param applyTimeZoneDifference
+        *            whether to apply the difference in time zones between 
client and server
+        */
+       public ZonedDateTimeConverter(boolean applyTimeZoneDifference)
+       {
+               this.applyTimeZoneDifference = applyTimeZoneDifference;
+       }
+
+       @Override
+       public ZonedDateTime convertToObject(String value, DateTimeFormatter 
format, Locale locale) {
+               try
+               {
+                       // parse date retaining the time of the submission
+                       return ZonedDateTime.parse(value, format);
+               }
+               catch (RuntimeException e)
+               {
+                       throw newConversionException(e, locale);
+               }
+       }
+
+       @Override
+       public ZonedDateTime convertToObject(String value, Locale locale)
+       {
+               if (Strings.isEmpty(value))
+               {
+                       return null;
+               }
+
+               DateTimeFormatter format = getFormat(locale);
+               Args.notNull(format, "format");
+
+               if (applyTimeZoneDifference)
+               {
+                       ZoneId zoneId = getClientTimeZone();
+
+                       // set time zone for client
+                       format = format.withZone(getTimeZone());
+
+                       ZonedDateTime dateTime = convertToObject(value, format, 
locale);
+                       // apply the server time zone to the parsed value
+                       if (zoneId != null)
+                       {
+                               dateTime = dateTime.withZoneSameInstant(zoneId);
+                       }
+
+                       return dateTime;
+               }
+               else
+               {
+                       return convertToObject(value, format, locale);
+               }
+       }
+
+       /**
+        * Creates a ConversionException and sets additional context 
information to it.
+        *
+        * @param cause
+        *            - {@link RuntimeException} cause
+        * @param locale
+        *            - {@link Locale} used to set 'format' variable with 
localized pattern
+        * @return {@link ConversionException}
+        */
+       ConversionException newConversionException(RuntimeException cause, 
Locale locale)
+       {
+               return new ConversionException(cause)
+                               .setVariable("format", getPattern(locale));
+       }
+
+       @Override
+       public String convertToString(ZonedDateTime dateTime, Locale locale)
+       {
+               DateTimeFormatter format = getFormat(locale);
+
+               if (applyTimeZoneDifference)
+               {
+                       ZoneId zoneId = getClientTimeZone();
+                       if (zoneId != null)
+                       {
+                               // apply time zone to formatter
+                               format = format.withZone(zoneId);
+                       }
+               }
+               return format.format(dateTime);
+       }
+
+       /**
+        * Gets whether to apply the time zone difference when interpreting 
dates.
+        * 
+        * </p> When true, the current time is applied on the parsed date, and 
the date will be
+        * corrected for the time zone difference between the server and the 
client. For instance, if
+        * I'm in Seattle and the server I'm working on is in Amsterdam, the 
server is 9 hours ahead.
+        * So, if I'm inputting say 12/24 at a couple of hours before midnight, 
at the server it is
+        * already 12/25. If this boolean is true, it will be transformed to 
12/25, while the client
+        * sees 12/24. </p>
+        * 
+        * @return whether to apply the difference in time zones between client 
and server
+        */
+       public final boolean getApplyTimeZoneDifference()
+       {
+               return applyTimeZoneDifference;
+       }
+
+       /**
+        * @param locale
+        *            The locale used to convert the value
+        * @return Gets the pattern that is used for printing and parsing
+        */
+       @Override
+       public abstract String getPattern(Locale locale);
+
+       /**
+        * Gets the client's time zone.
+        * 
+        * @return The client's time zone or null
+        */
+       protected ZoneId getClientTimeZone()
+       {
+               ClientInfo info = Session.get().getClientInfo();
+               if (info instanceof WebClientInfo)
+               {
+                       TimeZone timeZone = ((WebClientInfo) 
info).getProperties().getTimeZone();
+                       return timeZone.toZoneId();
+               }
+               return null;
+       }
+
+       /**
+        * @param locale
+        *            The locale used to convert the value
+        * 
+        * @return formatter The formatter for the current conversion
+        */
+       @Override
+       public abstract DateTimeFormatter getFormat(Locale locale);
+
+       /**
+        * Gets the server time zone. Override this method if you want to fix 
to a certain time zone,
+        * regardless of what actual time zone the server is in.
+        * 
+        * @return The server time zone
+        */
+       protected ZoneId getTimeZone()
+       {
+               return ZoneId.systemDefault();
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c25e05e3/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverterTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverterTest.java
 
b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverterTest.java
index 6e609b1..73c5035 100644
--- 
a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverterTest.java
+++ 
b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/form/datetime/DateConverterTest.java
@@ -24,7 +24,7 @@ import java.util.Date;
 import java.util.Locale;
 
 import 
org.apache.wicket.extensions.markup.html.form.datetime.PatternDateConverter;
-import 
org.apache.wicket.extensions.markup.html.form.datetime.StyleDateConverter;
+import 
org.apache.wicket.extensions.markup.html.form.datetime.StyleZonedDateTimeConverter;
 import org.apache.wicket.util.convert.ConversionException;
 import org.apache.wicket.util.convert.IConverter;
 import org.apache.wicket.util.convert.converter.CalendarConverter;
@@ -32,7 +32,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 /**
- * Tests for {@link DateConverter} and subclasses.
+ * Tests for {@link ZonedDateTimeConverter} and subclasses.
  * 
  * @author akiraly
  */
@@ -46,13 +46,13 @@ public class DateConverterTest
        {
                Locale locale = Locale.GERMAN;
 
-               StyleDateConverter styleDateConverter = new 
StyleDateConverter("F-", false);
+               StyleZonedDateTimeConverter styleDateConverter = new 
StyleZonedDateTimeConverter("F-", false);
                DateTimeFormatter styleFormatter = 
styleDateConverter.getFormat(locale);
 
                Assert.assertEquals(locale, styleFormatter.getLocale());
 
                PatternDateConverter patternDateConverter = new 
PatternDateConverter(
-                       styleDateConverter.getDatePattern(locale), false);
+                       styleDateConverter.getPattern(locale), false);
                DateTimeFormatter patternFormatter = 
patternDateConverter.getFormat(locale);
 
                Assert.assertEquals(locale, patternFormatter.getLocale());
@@ -78,7 +78,7 @@ public class DateConverterTest
                input.clear();
                input.set(2011, Calendar.MAY, 7);
 
-               final StyleDateConverter styleDateConverter = new 
StyleDateConverter("F-", false);
+               final StyleZonedDateTimeConverter styleDateConverter = new 
StyleZonedDateTimeConverter("F-", false);
 
                CalendarConverter calendarConverter = new CalendarConverter(new 
IConverter<Date>()
                {

Reply via email to