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><input></code> tag, as opposed to {@link DateTextField}! The corresponding tag is - * typically either a <code><div></code> or a <code><span></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("birthday"); - * </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> - * <div wicket:id="birthday"></div> - * </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"> :</span> + <input type="text" wicket:id="hours" size="2" maxlength="2" /> + <span wicket:id="hoursSeparator"> :</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>() {
