Commit b50054ea authored by Arthur Bogaart's avatar Arthur Bogaart Committed by Jeroen Hoffman

CMS-11068 [back port to 11.2] Split DatePrinter, DateTimePrinter, DateLabel and

DateTimeLabel

Simplify code and tests by splitting DatePrinter from DateTimePrinter.
This means we need a DateLabel and a DateTimeLabel as well.

(cherry picked from commit 8ec8fe70)
parent 75a8adda
/*
* Copyright 2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed 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.hippoecm.frontend.plugins.standards.datetime;
import java.time.format.FormatStyle;
import java.util.Date;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.IModel;
import org.hippoecm.frontend.model.ReadOnlyModel;
public class DateLabel extends Label {
public DateLabel(final String id, final IModel<Date> model) {
super(id, ReadOnlyModel.of(() -> DatePrinter.of(model.getObject()).print()));
}
public DateLabel(final String id, final IModel<Date> model, final FormatStyle style) {
super(id, ReadOnlyModel.of(() -> DatePrinter.of(model.getObject()).print(style)));
}
public DateLabel(final String id, final IModel<Date> model, final String pattern) {
super(id, ReadOnlyModel.of(() -> DatePrinter.of(model.getObject()).print(pattern)));
}
}
/*
* Copyright 2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed 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.hippoecm.frontend.plugins.standards.datetime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.util.io.IClusterable;
import org.hippoecm.frontend.plugins.standards.ClassResourceModel;
import org.hippoecm.frontend.session.UserSession;
/**
* Utility for printing java date objects using the java.time classes. Takes care of setting the correct locale and
* timezone. Prints the date but excludes the time.
*/
public interface DatePrinter extends IClusterable {
/**
* Print with default style (medium-short).
*
* @return the date as a string formatted in default style
*/
String print();
/**
* Print with specified pattern. See <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">patterns</a>
* for all pattern options.
*
* @param pattern the pattern to use, not null
* @return the date as a string based on the pattern
*/
String print(final String pattern);
/**
* Print with specified FormatStyle. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
* for all possible styles.
*
* @param style the formatter style to obtain, not null
* @return the date as a string based on the style
*/
String print(final FormatStyle style);
/**
* Append an explanatory string to the printed date if it is in Daylight Saving Time.
* Java shifts the time zone +1 if a date is in DST (e.g. CET becomes CEST), so to avoid confusion we add
* a description after the time zone (e.g. " (DST)" in English).
*
* @return the DatePrinter instance
*/
DatePrinter appendDST();
static DatePrinter of(final Date date) {
return date != null ? of(date.toInstant()) : EmptyDatePrinter.INSTANCE;
}
static DatePrinter of(final Calendar calendar) {
return calendar != null ? of(calendar.toInstant()) : EmptyDatePrinter.INSTANCE;
}
static DatePrinter of(final Instant instant) {
return instant != null ? new JavaDatePrinter(instant) : EmptyDatePrinter.INSTANCE;
}
class JavaDatePrinter implements DatePrinter {
private final Instant instant;
private final Locale locale;
private final ZoneId zoneId;
private boolean appendDST;
JavaDatePrinter(final Instant instant) {
this.instant = instant;
final UserSession session = UserSession.get();
if (session == null) {
throw new NullPointerException("Unable to retrieve user session");
}
locale = session.getLocale();
zoneId = toZoneId(session.getTimeZone());
}
private static ZoneId toZoneId(final TimeZone timeZone) {
return ZoneId.of(timeZone.getID(), ZoneId.SHORT_IDS);
}
@Override
public DatePrinter appendDST() {
appendDST = true;
return this;
}
@Override
public String print() {
return print(FormatStyle.MEDIUM);
}
@Override
public String print(final FormatStyle style) {
return print(DateTimeFormatter.ofLocalizedDate(style));
}
@Override
public String print(final String pattern) {
return print(DateTimeFormatter.ofPattern(pattern));
}
String print(DateTimeFormatter formatter) {
final ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, zoneId);
formatter = formatter.withLocale(locale);
return dateTime.format(formatter) + getSuffix();
}
private String getSuffix() {
final String dst = new ClassResourceModel("dst", JavaDatePrinter.class, locale, null).getObject();
return appendDST && isDST() ? " (" + dst + ")" : StringUtils.EMPTY;
}
private boolean isDST() {
return zoneId.getRules().isDaylightSavings(instant);
}
}
class EmptyDatePrinter implements DatePrinter {
private final static DatePrinter INSTANCE = new EmptyDatePrinter();
@Override
public String print() {
return StringUtils.EMPTY;
}
@Override
public String print(final String pattern) {
return print();
}
@Override
public String print(final FormatStyle style) {
return print();
}
@Override
public DatePrinter appendDST() {
return this;
}
}
}
......@@ -15,58 +15,28 @@
*/
package org.hippoecm.frontend.plugins.standards.datetime;
import java.io.Serializable;
import java.time.format.FormatStyle;
import java.util.Date;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.hippoecm.frontend.model.ReadOnlyModel;
public class DateTimeLabel extends Label {
public DateTimeLabel(final String id, final IModel<Date> model) {
super(id, new DateTimePrinterModel(model, DateTimePrinter::print));
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print()));
}
public DateTimeLabel(final String id, final IModel<Date> model, final FormatStyle style) {
super(id, new DateTimePrinterModel(model, printer -> printer.print(style)));
}
public DateTimeLabel(final String id, final IModel<Date> model, final FormatStyle dateStyle, final FormatStyle timeStyle, final boolean dateOnly) {
super(id, new DateTimePrinterModel(model, printer -> {
if (dateOnly) {
return printer.printDate(dateStyle);
} else {
return printer.print(dateStyle, timeStyle);
}
}));
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print(style)));
}
public DateTimeLabel(final String id, final IModel<Date> model, final FormatStyle dateStyle, final FormatStyle timeStyle) {
super(id, new DateTimePrinterModel(model, printer -> printer.print(dateStyle, timeStyle)));
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print(dateStyle, timeStyle)));
}
public DateTimeLabel(final String id, final IModel<Date> model, final String pattern) {
super(id, new DateTimePrinterModel(model, printer -> printer.print(pattern)));
}
private interface Printer extends Serializable {
String print(DateTimePrinter printer);
}
private static class DateTimePrinterModel extends AbstractReadOnlyModel<String> {
private final IModel<Date> dateModel;
private final Printer printer;
private DateTimePrinterModel(final IModel<Date> dateModel, final Printer printer) {
this.dateModel = dateModel;
this.printer = printer;
}
@Override
public String getObject() {
final Date date = dateModel.getObject();
return printer.print(DateTimePrinter.of(date));
}
super(id, ReadOnlyModel.of(() -> DateTimePrinter.of(model.getObject()).print(pattern)));
}
}
......@@ -16,58 +16,16 @@
package org.hippoecm.frontend.plugins.standards.datetime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.util.io.IClusterable;
import org.hippoecm.frontend.plugins.standards.ClassResourceModel;
import org.hippoecm.frontend.session.UserSession;
/**
* Utility for printing java date objects using the java.time classes. Takes care of setting the correct locale and
* timezone.
* timezone and prints the date and the time.
*/
public interface DateTimePrinter extends IClusterable {
/**
* Print with default style (medium-short).
*
* @return the date as a string formatted in default style
*/
String print();
/**
* Print with specified pattern. See <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">patterns</a>
* for all pattern options.
*
* @param pattern the pattern to use, not null
* @return the date as a string based on the pattern
*/
String print(final String pattern);
/**
* Print with specified FormatStyle. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
* for all possible styles. The specified style will be used for both the date and the time part.
*
* @param style the formatter style to obtain, not null
* @return the date as a string based on the style
*/
String print(final FormatStyle style);
/**
* Print with specified FormatStyle. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
* for all possible styles. The specified style will be used for date only
*
* @param style the formatter style to obtain, not null
* @return the date as a string based on the style
*/
String printDate(final FormatStyle style);
public interface DateTimePrinter extends DatePrinter {
/**
* Print with specified FormatStyles. Check <a href="https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html">here</a>
......@@ -80,12 +38,12 @@ public interface DateTimePrinter extends IClusterable {
String print(final FormatStyle dateStyle, final FormatStyle timeStyle);
/**
* Append an explanatory string to the printed date if it is in Daylight Saving Time.
* Java shifts the time zone +1 if a date is in DST (e.g. CET becomes CEST), so to avoid confusion we add
* a description after the time zone (e.g. " (DST)" in English).
* Override appendDST to return a more specific implementation DateTimePrinter and allow proper method chaining.
*
* @see DatePrinter#appendDST()
* @return the DateTimePrinter instance
*/
@Override
DateTimePrinter appendDST();
static DateTimePrinter of(final Date date) {
......@@ -97,42 +55,13 @@ public interface DateTimePrinter extends IClusterable {
}
static DateTimePrinter of(final Instant instant) {
if (instant == null) {
return EmptyDateTimePrinter.INSTANCE;
}
final UserSession session = UserSession.get();
if (session == null) {
throw new NullPointerException("Unable to retrieve user session");
}
final Locale locale = session.getLocale();
final ZoneId zoneId = toZoneId(session.getTimeZone());
return new JavaDateTimePrinter(instant, locale, zoneId);
return instant != null ? new JavaDateTimePrinter(instant) : EmptyDateTimePrinter.INSTANCE;
}
static ZoneId toZoneId(final TimeZone timeZone) {
return ZoneId.of(timeZone.getID(), ZoneId.SHORT_IDS);
}
class JavaDateTimePrinter implements DateTimePrinter {
class JavaDateTimePrinter extends JavaDatePrinter implements DateTimePrinter {
private final Instant instant;
private final Locale locale;
private final ZoneId zoneId;
private boolean appendDST;
private JavaDateTimePrinter(final Instant instant, final Locale locale, final ZoneId zoneId) {
this.instant = instant;
this.locale = locale;
this.zoneId = zoneId;
}
@Override
public DateTimePrinter appendDST() {
appendDST = true;
return this;
JavaDateTimePrinter(final Instant instant) {
super(instant);
}
@Override
......@@ -145,58 +74,22 @@ public interface DateTimePrinter extends IClusterable {
return print(style, style);
}
@Override
public String printDate(final FormatStyle style) {
return print(DateTimeFormatter.ofLocalizedDate(style));
}
@Override
public String print(final FormatStyle dateStyle, final FormatStyle timeStyle) {
return print(DateTimeFormatter.ofLocalizedDateTime(dateStyle, timeStyle));
}
@Override
public String print(final String pattern) {
return print(DateTimeFormatter.ofPattern(pattern));
}
private String print(DateTimeFormatter formatter) {
final ZonedDateTime dateTime = ZonedDateTime.ofInstant(instant, zoneId);
formatter = formatter.withLocale(locale);
final String dst = new ClassResourceModel("dst", JavaDateTimePrinter.class, locale, null).getObject();
final String suffix = appendDST && isDST() ? " (" + dst + ")" : StringUtils.EMPTY;
return dateTime.format(formatter) + suffix;
}
private boolean isDST() {
return zoneId.getRules().isDaylightSavings(instant);
public DateTimePrinter appendDST() {
super.appendDST();
return this;
}
}
class EmptyDateTimePrinter implements DateTimePrinter {
class EmptyDateTimePrinter extends EmptyDatePrinter implements DateTimePrinter {
private final static DateTimePrinter INSTANCE = new EmptyDateTimePrinter();
@Override
public String print() {
return StringUtils.EMPTY;
}
@Override
public String print(final String pattern) {
return print();
}
@Override
public String print(final FormatStyle style) {
return print();
}
@Override
public String printDate(final FormatStyle style) {
return print();
}
@Override
public String print(final FormatStyle dateStyle, final FormatStyle timeStyle) {
return print();
......
/*
* Copyright 2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed 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.hippoecm.frontend.plugins.standards.datetime;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.FormatStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.wicket.ThreadContext;
import org.easymock.EasyMock;
import org.hippoecm.frontend.PluginTest;
import org.hippoecm.frontend.session.UserSession;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class DatePrinterTest extends PluginTest {
private Date dateEpoch;
private Instant instantEpoch;
private Calendar calendarEpoch;
private Locale locale = Locale.GERMANY;
private TimeZone timeZone = TimeZone.getTimeZone("UTC");
@Before
public void before() {
final UserSession session = EasyMock.createNiceMock(UserSession.class);
EasyMock.expect(session.getLocale()).andAnswer(() -> locale).anyTimes();
EasyMock.expect(session.getTimeZone()).andAnswer(() -> timeZone).anyTimes();
EasyMock.replay(session);
ThreadContext.setSession(session);
calendarEpoch = Calendar.getInstance();
calendarEpoch.setTimeInMillis(0);
dateEpoch = calendarEpoch.getTime();
instantEpoch = calendarEpoch.toInstant();
}
@Test
public void testNull() {
assertEquals("", DatePrinter.of((Date)null).print());
assertEquals("", DatePrinter.of((Calendar)null).print());
assertEquals("", DatePrinter.of((Instant)null).print());
}
@Test
public void testDate() {
assertEquals("01.01.1970", DatePrinter.of(dateEpoch).print());
}
@Test
public void testCalendar() {
assertEquals("01.01.1970", DatePrinter.of(calendarEpoch).print());
}
@Test
public void testInstant() {
assertEquals("01.01.1970", DatePrinter.of(instantEpoch).print());
}
@Test
public void testLocale() {
locale = Locale.JAPAN;
assertEquals("1970/01/01", DatePrinter.of(dateEpoch).print());
}
@Test
public void testTimeZone() {
timeZone = TimeZone.getTimeZone("America/Aruba"); // -4
assertEquals("31.12.1969", DatePrinter.of(dateEpoch).print());
}
@Test
public void testStyleShort() {
assertEquals("01.01.70", DatePrinter.of(dateEpoch).print(FormatStyle.SHORT));
}
@Test
public void testStyleMedium() {
assertEquals("01.01.1970", DatePrinter.of(dateEpoch).print(FormatStyle.MEDIUM));
}
@Test
public void testStyleLong() {
assertEquals("1. Januar 1970", DatePrinter.of(dateEpoch).print(FormatStyle.LONG));
}
@Test
public void testStyleFull() {
assertEquals("Donnerstag, 1. Januar 1970", DatePrinter.of(dateEpoch).print(FormatStyle.FULL));
}
@Test
public void testDST() {
timeZone = TimeZone.getTimeZone("Europe/Amsterdam");
final LocalDate dstInNL = LocalDate.of(2016, Month.MAY, 1);
final Date dstDate = Date.from(dstInNL.atStartOfDay(timeZone.toZoneId()).toInstant());
assertEquals("01.05.16", DatePrinter.of(dstDate).print(FormatStyle.SHORT));
assertEquals("01.05.16 (DST)", DatePrinter.of(dstDate).appendDST().print(FormatStyle.SHORT));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment