You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2021/06/15 16:21:04 UTC

[openmeetings] 01/01: [OPENMEETINGS-2624] datetimes should work with custom locales/formats

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

solomax pushed a commit to branch OPENMEETINGS-2624-datetime
in repository https://gitbox.apache.org/repos/asf/openmeetings.git

commit 8688850272965d7d9ac7ad37de80abe9b6be7bc8
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Tue Jun 15 23:19:52 2021 +0700

    [OPENMEETINGS-2624] datetimes should work with custom locales/formats
---
 .../web/common/AbstractOmDateTimePicker.java       |  89 ------------
 .../openmeetings/web/common/GeneralUserForm.java   |   1 +
 .../openmeetings/web/common/InvitationForm.java    |   1 +
 .../common/datetime/AbstractOmDateTimePicker.html  |  27 ++++
 .../common/datetime/AbstractOmDateTimePicker.java  | 153 +++++++++++++++++++++
 .../common/{ => datetime}/AjaxOmDatePicker.java    |  41 ++++--
 .../common/{ => datetime}/OmDateTimePicker.java    |  32 ++---
 .../web/common/datetime/datepicker-functions.js    |  16 +++
 .../openmeetings/web/user/MessageDialog.java       |   2 +-
 .../web/user/calendar/AppointmentDialog.java       |   2 +-
 .../org/apache/openmeetings/util/TestDateTime.java |   2 +-
 pom.xml                                            |   4 +-
 12 files changed, 248 insertions(+), 122 deletions(-)

diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AbstractOmDateTimePicker.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AbstractOmDateTimePicker.java
deleted file mode 100644
index 2d0bd5a..0000000
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AbstractOmDateTimePicker.java
+++ /dev/null
@@ -1,89 +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.openmeetings.web.common;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
-
-import org.apache.openmeetings.web.app.WebSession;
-import org.apache.wicket.model.IModel;
-
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.AbstractDateTimePickerWithIcon;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.DatetimePickerConfig;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.DatetimePickerIconConfig;
-import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
-
-public abstract class AbstractOmDateTimePicker<T> extends AbstractDateTimePickerWithIcon<T> {
-	private static final long serialVersionUID = 1L;
-	private static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd";
-	private static final String DEFAULT_DATE_TIME_FORMAT = DEFAULT_DATE_FORMAT + " HH:mm:ss";
-
-	protected AbstractOmDateTimePicker(String id, IModel<T> model) {
-		this(id, model, getDateTimeFormat());
-	}
-
-	protected AbstractOmDateTimePicker(String id, IModel<T> model, String format) {
-		super(id, model, new DatetimePickerConfig()
-				//.useLocale(WebSession.get().getLocale().toLanguageTag())
-				.withFormat(patch(format))
-				.with(new DatetimePickerIconConfig()
-						.useDateIcon(FontAwesome5IconType.calendar_s)
-						.useTimeIcon(FontAwesome5IconType.clock_s)
-						.useUpIcon(FontAwesome5IconType.arrow_up_s)
-						.useDownIcon(FontAwesome5IconType.arrow_down_s)
-						.usePreviousIcon(FontAwesome5IconType.arrow_left_s)
-						.useNextIcon(FontAwesome5IconType.arrow_right_s)
-						.useTodayIcon(FontAwesome5IconType.calendar_check_s)
-						.useClearIcon(FontAwesome5IconType.eraser_s)
-						.useCloseIcon(FontAwesome5IconType.times_s))
-				);
-		setRenderBodyOnly(false);
-	}
-
-	public static String patch(String format) {
-		// in Java free text is escaped with single-quotes
-		// moment.js uses []
-		return format.replaceAll("[']{1}([^']*)[']{1}", "\\[$1\\]");
-	}
-
-	public static String getDateTimeFormat() {
-		return getDateTimeFormat(WebSession.get().getLocale());
-	}
-
-	public static String getDateTimeFormat(Locale loc) {
-		DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, loc);
-		if (fmt instanceof SimpleDateFormat) {
-			return ((SimpleDateFormat)fmt).toPattern();
-		}
-		return DEFAULT_DATE_TIME_FORMAT;
-	}
-
-	public static String getDateFormat() {
-		return getDateFormat(WebSession.get().getLocale());
-	}
-
-	public static String getDateFormat(Locale loc) {
-		DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT, loc);
-		if (fmt instanceof SimpleDateFormat) {
-			return ((SimpleDateFormat)fmt).toPattern();
-		}
-		return DEFAULT_DATE_FORMAT;
-	}
-}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/GeneralUserForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/GeneralUserForm.java
index c8d6488..75c1811 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/GeneralUserForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/GeneralUserForm.java
@@ -33,6 +33,7 @@ import org.apache.openmeetings.db.entity.user.Group;
 import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Salutation;
+import org.apache.openmeetings.web.common.datetime.AjaxOmDatePicker;
 import org.apache.openmeetings.web.util.CountryDropDown;
 import org.apache.openmeetings.web.util.RestrictiveChoiceProvider;
 import org.apache.wicket.extensions.validation.validator.RfcCompliantEmailAddressValidator;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java
index fd8bd00..e5c3763 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/InvitationForm.java
@@ -40,6 +40,7 @@ import org.apache.openmeetings.db.entity.user.User.Type;
 import org.apache.openmeetings.service.room.InvitationManager;
 import org.apache.openmeetings.util.crypt.CryptProvider;
 import org.apache.openmeetings.web.app.WebSession;
+import org.apache.openmeetings.web.common.datetime.OmDateTimePicker;
 import org.apache.openmeetings.web.util.UserMultiChoice;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.html
new file mode 100644
index 0000000..5d3d67b
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.html
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+
+-->
+<!DOCTYPE html>
+<html xmlns:wicket="http://wicket.apache.org">
+<wicket:panel>
+	<span wicket:id="picker"></span>
+	<input type="hidden" wicket:id="date" />
+</wicket:panel>
+</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java
new file mode 100644
index 0000000..c2c382e
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AbstractOmDateTimePicker.java
@@ -0,0 +1,153 @@
+/*
+ * 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.openmeetings.web.common.datetime;
+
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+import org.apache.openmeetings.web.app.WebSession;
+import org.apache.wicket.Session;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.FormComponentPanel;
+import org.apache.wicket.markup.html.form.HiddenField;
+import org.apache.wicket.model.ChainingModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.AbstractDateTimePickerWithIcon;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.DatetimePickerConfig;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.datetime.DatetimePickerIconConfig;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesome5IconType;
+
+public abstract class AbstractOmDateTimePicker<T extends Serializable> extends FormComponentPanel<T> {
+	private static final long serialVersionUID = 1L;
+	private static final String DEFAULT_DATE_FORMAT = "yyyy/MM/dd";
+	private static final String DEFAULT_DATE_TIME_FORMAT = DEFAULT_DATE_FORMAT + " HH:mm:ss";
+	private static final ResourceReference FUNCJS = new JavaScriptResourceReference(AbstractOmDateTimePicker.class, "datepicker-functions.js");
+	private final boolean dateOnly;
+	private final HiddenField<T> date;
+	private AbstractDateTimePickerWithIcon<T> picker;
+	private String markupId;
+
+	AbstractOmDateTimePicker(String id, IModel<T> model, boolean dateOnly) {
+		super(id, model);
+		this.dateOnly = dateOnly;
+		date = newHidden("date", new ChainingModel<>(model));
+	}
+
+	@Override
+	protected void onInitialize() {
+		super.onInitialize();
+		final String format = dateOnly ? getDateFormat() : getDateTimeFormat();
+		DatetimePickerConfig config = new DatetimePickerConfig()
+				.useLocale(WebSession.get().getLocale().toLanguageTag())
+				.withFormat(patch(format))
+				.withKeepInvalid(true)
+				.with(new DatetimePickerIconConfig().useDateIcon(FontAwesome5IconType.calendar_s)
+						.useTimeIcon(FontAwesome5IconType.clock_s).useUpIcon(FontAwesome5IconType.arrow_up_s)
+						.useDownIcon(FontAwesome5IconType.arrow_down_s)
+						.usePreviousIcon(FontAwesome5IconType.arrow_left_s)
+						.useNextIcon(FontAwesome5IconType.arrow_right_s)
+						.useTodayIcon(FontAwesome5IconType.calendar_check_s).useClearIcon(FontAwesome5IconType.eraser_s)
+						.useCloseIcon(FontAwesome5IconType.times_s));
+		picker = new AbstractDateTimePickerWithIcon<>("picker", new Model<>(getModelObject()), config) {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected FormComponent<T> newInput(String wicketId, String dateFormat) {
+				FormComponent<T> input = AbstractOmDateTimePicker.this.newInput(wicketId, dateFormat);
+				markupId = input.getMarkupId();
+				return input;
+			}
+		};
+		date.setOutputMarkupId(true);
+		add(date, picker.setRenderBodyOnly(false));
+	}
+
+	@Override
+	public void convertInput() {
+		date.convertInput();
+		if (date.isValid()) {
+			setConvertedInput(date.getConvertedInput());
+		} else {
+			date.getFeedbackMessages().forEach(msg -> getFeedbackMessages().add(msg));
+			date.getFeedbackMessages().clear();
+		}
+	}
+
+	protected abstract FormComponent<T> newInput(String wicketId, String dateFormat);
+
+	protected abstract HiddenField<T> newHidden(String wicketId, IModel<T> model);
+
+	@Override
+	public void renderHead(IHeaderResponse response) {
+		super.renderHead(response);
+		response.render(JavaScriptHeaderItem.forReference(FUNCJS));
+		response.render(new OnDomReadyHeaderItem("$('#" + markupId + "').on('change.datetimepicker', function (e) {omDateTimeInputHasChanged(e, '" + date.getMarkupId() + "', " + dateOnly + ");});"));
+	}
+
+	@Override
+	public boolean processChildren() {
+		return false;
+	}
+
+	public static String patch(String format) {
+		// in Java free text is escaped with single-quotes
+		// moment.js uses []
+		return format.replaceAll("[']{1}([^']*)[']{1}", "\\[$1\\]");
+	}
+
+	public static String getDateTimeFormat() {
+		return getDateTimeFormat(WebSession.get().getLocale());
+	}
+
+	@Override
+	public FormComponent<T> setLabel(IModel<String> labelModel) {
+		date.setLabel(labelModel);
+		return this;
+	}
+
+	@Override
+	protected void onModelChanged() {
+		picker.setModelObject(getModelObject());
+	}
+
+	public static String getDateTimeFormat(Locale loc) {
+		DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, loc);
+		if (fmt instanceof SimpleDateFormat) {
+			return ((SimpleDateFormat)fmt).toPattern();
+		}
+		return DEFAULT_DATE_TIME_FORMAT;
+	}
+
+	public static String getDateFormat() {
+		DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT, Session.get().getLocale());
+		if (fmt instanceof SimpleDateFormat) {
+			return ((SimpleDateFormat)fmt).toPattern();
+		}
+		return DEFAULT_DATE_FORMAT;
+	}
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AjaxOmDatePicker.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AjaxOmDatePicker.java
similarity index 58%
rename from openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AjaxOmDatePicker.java
rename to openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AjaxOmDatePicker.java
index 0fd162f..893723c 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/AjaxOmDatePicker.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/AjaxOmDatePicker.java
@@ -16,35 +16,56 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.web.common;
+package org.apache.openmeetings.web.common.datetime;
 
 import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
 
-import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
 import org.apache.wicket.extensions.markup.html.form.datetime.LocalDateTextField;
 import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.HiddenField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.util.convert.IConverter;
+import org.apache.wicket.util.convert.converter.LocalDateConverter;
 
 public class AjaxOmDatePicker extends AbstractOmDateTimePicker<LocalDate> {
 	private static final long serialVersionUID = 1L;
 
 	public AjaxOmDatePicker(String id) {
-		super(id, null, getDateFormat());
+		super(id, null, true);
 	}
 
 	@Override
-	protected FormComponent<LocalDate> newInput(String wicketId, String dateFormat) {
-		LocalDateTextField input = new LocalDateTextField(wicketId, getModel(), dateFormat);
-		input.add(new OnChangeAjaxBehavior() {
+	protected HiddenField<LocalDate> newHidden(String wicketId, IModel<LocalDate> model) {
+		final IConverter<?> converter = new LocalDateConverter() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public DateTimeFormatter getDateTimeFormatter(Locale locale) {
+				return DateTimeFormatter.ISO_LOCAL_DATE;
+			}
+		};
+		HiddenField<LocalDate> date = new HiddenField<>(wicketId, model, LocalDate.class) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			protected void onUpdate(AjaxRequestTarget target) {
-				onValueChanged(target);
+			protected IConverter<?> createConverter(Class<?> clazz) {
+				if (LocalDate.class.isAssignableFrom(clazz)) {
+					return converter;
+				}
+				return null;
 			}
-		});
-		return input;
+		};
+		date.add(OnChangeAjaxBehavior.onChange(this::onValueChanged));
+		return date;
+	}
+
+	@Override
+	protected FormComponent<LocalDate> newInput(String wicketId, String dateFormat) {
+		return new LocalDateTextField(wicketId, getModel(), dateFormat);
 	}
 
 	protected void onValueChanged(IPartialPageRequestHandler target) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmDateTimePicker.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/OmDateTimePicker.java
similarity index 77%
rename from openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmDateTimePicker.java
rename to openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/OmDateTimePicker.java
index d1bd393..5085f90 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmDateTimePicker.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/OmDateTimePicker.java
@@ -16,46 +16,37 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.web.common;
+package org.apache.openmeetings.web.common.datetime;
 
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.time.format.DateTimeFormatterBuilder;
 import java.util.Locale;
 
 import org.apache.wicket.extensions.markup.html.form.datetime.LocalDateTimeTextField;
 import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.HiddenField;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.util.convert.IConverter;
 import org.apache.wicket.util.convert.converter.LocalDateTimeConverter;
 
 public class OmDateTimePicker extends AbstractOmDateTimePicker<LocalDateTime> {
 	private static final long serialVersionUID = 1L;
-	private final LocalDateTimeConverter converter;
 
 	public OmDateTimePicker(String id, IModel<LocalDateTime> model) {
-		this(id, model, getDateTimeFormat());
+		super(id, model, false);
 	}
 
-	private OmDateTimePicker(String id, IModel<LocalDateTime> model, final String pattern) {
-		super(id, model, pattern);
-		this.converter = new LocalDateTimeConverter() {
+	@Override
+	protected HiddenField<LocalDateTime> newHidden(String wicketId, IModel<LocalDateTime> model) {
+		final IConverter<?> converter = new LocalDateTimeConverter() {
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			public DateTimeFormatter getDateTimeFormatter(Locale locale)
-			{
-				return new DateTimeFormatterBuilder()
-						.parseCaseInsensitive()
-						.appendPattern(pattern)
-						.toFormatter(Locale.ENGLISH);
+			public DateTimeFormatter getDateTimeFormatter(Locale locale) {
+				return DateTimeFormatter.ISO_LOCAL_DATE_TIME;
 			}
 		};
-	}
-
-	@Override
-	protected FormComponent<LocalDateTime> newInput(String wicketId, String dateFormat) {
-		return new LocalDateTimeTextField(wicketId, getModel(), dateFormat) {
+		return new HiddenField<>(wicketId, model, LocalDateTime.class) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
@@ -67,4 +58,9 @@ public class OmDateTimePicker extends AbstractOmDateTimePicker<LocalDateTime> {
 			}
 		};
 	}
+
+	@Override
+	protected FormComponent<LocalDateTime> newInput(String wicketId, String dateFormat) {
+		return new LocalDateTimeTextField(wicketId, getModel(), dateFormat);
+	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/datepicker-functions.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/datepicker-functions.js
new file mode 100644
index 0000000..c1e8282
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/datetime/datepicker-functions.js
@@ -0,0 +1,16 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+function omDateTimeInputHasChanged(e, hiddenId, dateOnly) {
+	const fmt = dateOnly ? 'YYYY-MM-DD' : 'YYYY-MM-DDTHH:mm:ss';
+	let val = e.target.value
+		, date = e.date;
+	if (!date) {
+		const mmnt = $(e.target).datetimepicker('date');
+		if (moment.isMoment(mmnt)) {
+			date = moment(val, mmnt.creationData().format);
+		}
+	}
+	if (date) {
+		val = date.isValid() ? date.clone().locale('en').format(fmt) : date.creationData().input;
+	}
+	$('#' + hiddenId).val(val).trigger('change');
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
index be37b7b..fd33168 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/MessageDialog.java
@@ -53,8 +53,8 @@ import org.apache.openmeetings.db.manager.IInvitationManager;
 import org.apache.openmeetings.util.CalendarHelper;
 import org.apache.openmeetings.web.app.Application;
 import org.apache.openmeetings.web.app.WebSession;
-import org.apache.openmeetings.web.common.OmDateTimePicker;
 import org.apache.openmeetings.web.common.OmModalCloseButton;
+import org.apache.openmeetings.web.common.datetime.OmDateTimePicker;
 import org.apache.openmeetings.web.user.calendar.AppointmentDialog;
 import org.apache.openmeetings.web.util.CalendarWebHelper;
 import org.apache.openmeetings.web.util.RoomTypeDropDown;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/AppointmentDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/AppointmentDialog.java
index faea5c5..1c661d9 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/AppointmentDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/AppointmentDialog.java
@@ -51,8 +51,8 @@ import org.apache.openmeetings.db.util.FormatHelper;
 import org.apache.openmeetings.service.calendar.caldav.AppointmentManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.GroupChoiceProvider;
-import org.apache.openmeetings.web.common.OmDateTimePicker;
 import org.apache.openmeetings.web.common.OmModalCloseButton;
+import org.apache.openmeetings.web.common.datetime.OmDateTimePicker;
 import org.apache.openmeetings.web.pages.MainPage;
 import org.apache.openmeetings.web.user.OmWysiwygToolbar;
 import org.apache.openmeetings.web.user.rooms.RoomEnterBehavior;
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestDateTime.java b/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestDateTime.java
index 8cd53d6..926c59a 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestDateTime.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/util/TestDateTime.java
@@ -29,7 +29,7 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
 
-import org.apache.openmeetings.web.common.AbstractOmDateTimePicker;
+import org.apache.openmeetings.web.common.datetime.AbstractOmDateTimePicker;
 import org.junit.jupiter.api.Test;
 
 class TestDateTime {
diff --git a/pom.xml b/pom.xml
index 08fbb99..f175a8e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,7 +57,7 @@
 		<maven-project-info-reports-plugin.version>3.1.2</maven-project-info-reports-plugin.version>
 		<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
 		<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
-		<maven-dependency-plugin.version>3.1.2</maven-dependency-plugin.version>
+		<maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
 		<maven-bundle-plugin.version>5.1.1</maven-bundle-plugin.version>
 		<maven-war-plugin.version>3.3.1</maven-war-plugin.version>
 		<maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
@@ -81,7 +81,7 @@
 		<wicket.version>9.3.0</wicket.version>
 		<wicket-jquery-ui.version>9.3.1</wicket-jquery-ui.version>
 		<wicketstuff.version>9.3.0</wicketstuff.version>
-		<wicket-bootstrap.version>5.0.4</wicket-bootstrap.version>
+		<wicket-bootstrap.version>5.0.5-SNAPSHOT</wicket-bootstrap.version>
 		<font-awesome.version>5.15.2</font-awesome.version>
 		<spring.version>5.3.8</spring.version>
 		<tomcat.version>9.0.46</tomcat.version>