Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Refactoring DateTimeType and impacts on ESH #4259

Merged
merged 4 commits into from
Nov 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
import static org.junit.Assert.*;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -25,6 +28,7 @@
/**
* @author Thomas.Eichstaedt-Engelen
* @author Gaël L'hopital - Added Timezone and Milliseconds
* @author Erdoan Hadzhiyusein - Added ZonedDateTime tests
*/
@RunWith(Parameterized.class)
public class DateTimeTypeTest {
Expand Down Expand Up @@ -115,18 +119,20 @@ public static Collection<Object[]> parameters() {
"2014-03-30T10:58:47.033+0000") },
{ new ParameterSet(TimeZone.getTimeZone("UTC"), initTimeMap(), TimeZone.getTimeZone("CET"),
"2014-03-30T08:58:47.033+0000") },
{ new ParameterSet(TimeZone.getTimeZone("UTC"), "2014-03-30T10:58:47UTS",
{ new ParameterSet(TimeZone.getTimeZone("UTC"), "2014-03-30T10:58:47UTC",
"2014-03-30T10:58:47.000+0000") },
{ new ParameterSet(TimeZone.getTimeZone("CET"), initTimeMap(), TimeZone.getTimeZone("UTC"),
"2014-03-30T12:58:47.033+0200") },
{ new ParameterSet(TimeZone.getTimeZone("CET"), initTimeMap(), TimeZone.getTimeZone("CET"),
"2014-03-30T10:58:47.033+0200") },
{ new ParameterSet(TimeZone.getTimeZone("CET"), "2014-03-30T10:58:47UTS",
{ new ParameterSet(TimeZone.getTimeZone("CET"), "2014-03-30T10:58:47CET",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have added a pattern for numeric time zone notation (DATE_PATTERN_WITH_TZ_AND_MS_GENERAL) - therefore wouldn't it make sense to add a parameter set here which uses this notation as an input?

"2014-03-30T10:58:47.000+0200") },
{ new ParameterSet(TimeZone.getTimeZone("GMT+5"), "2014-03-30T10:58:47.000Z",
"2014-03-30T15:58:47.000+0500") },
{ new ParameterSet(TimeZone.getTimeZone("GMT"), initTimeMap(), TimeZone.getTimeZone("GMT"),
"2014-03-30T10:58:47.033+0000") },
{ new ParameterSet(TimeZone.getTimeZone("CET"), initTimeMap(), TimeZone.getTimeZone("+02:00"),
"2014-03-30T12:58:47.033+0200") },
{ new ParameterSet(TimeZone.getTimeZone("GMT+2"), initTimeMap(), TimeZone.getTimeZone("GML"),
"2014-03-30T12:58:47.033+0200") },
{ new ParameterSet(TimeZone.getTimeZone("GMT-2"), initTimeMap(), TimeZone.getTimeZone("GMT+3"),
Expand Down Expand Up @@ -165,14 +171,34 @@ public void serializationTest() {
assertTrue(dt.equals(new DateTimeType(dt.toString())));
}

@Test
public void serializationTestZoned() {
ZonedDateTime zoned = ZonedDateTime.now();
DateTimeType dt = new DateTimeType(zoned);
DateTimeType sdt = new DateTimeType(dt.toFullString());
assertEquals(dt.getZonedDateTime(), sdt.getZonedDateTime());
}

@Test
public void equalityTest() {
DateTimeType dt1 = new DateTimeType(Calendar.getInstance());
DateTimeType dt2 = DateTimeType.valueOf(dt1.toString());
DateTimeType dt2 = DateTimeType.valueOf(dt1.toFullString());

assertTrue(dt1.toString().equals(dt2.toString()));
assertTrue(dt1.equals(dt2));
assertTrue(dt1.getCalendar().equals(dt2.getCalendar()));

assertTrue(dt1.equals(dt2));
}

@Test
public void equalityTestZoned() {
ZonedDateTime zoned = ZonedDateTime.now();
DateTimeType dt1 = new DateTimeType(zoned);
DateTimeType dt2 = DateTimeType.valueOf(dt1.toFullString());

assertTrue(dt1.toString().equals(dt2.toFullString()));
assertTrue(dt1.getZonedDateTime().equals(dt2.getZonedDateTime()));
assertTrue(dt1.equals(dt2));
}

Expand All @@ -190,16 +216,45 @@ public void createDate() {
parameterSet.inputTimeMap.get("date"), parameterSet.inputTimeMap.get("hourOfDay"),
parameterSet.inputTimeMap.get("minute"), parameterSet.inputTimeMap.get("second"));
calendar.set(Calendar.MILLISECOND, parameterSet.inputTimeMap.get("milliseconds"));

inputTimeString = new SimpleDateFormat(DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS).format(calendar.getTime());
} else {
inputTimeString = parameterSet.inputTimeString;
}

DateTimeType dt = DateTimeType.valueOf(inputTimeString);

if (parameterSet.inputTimeZone == null) {
dt = new DateTimeType(dt.getZonedDateTime().withZoneSameInstant(TimeZone.getDefault().toZoneId()));
}
// Test
assertEquals(parameterSet.expectedResult, dt.toString());
}

@Test
public void createZonedDate() {
String inputTimeString;

// set default time zone
TimeZone.setDefault(parameterSet.defaultTimeZone);

// get formatted time string
if (parameterSet.inputTimeString == null) {
int durationInNano = (int) TimeUnit.NANOSECONDS.convert(parameterSet.inputTimeMap.get("milliseconds"),
TimeUnit.MILLISECONDS);

LocalDateTime dateTime = LocalDateTime.of(parameterSet.inputTimeMap.get("year"),
parameterSet.inputTimeMap.get("month") + 1, parameterSet.inputTimeMap.get("date"),
parameterSet.inputTimeMap.get("hourOfDay"), parameterSet.inputTimeMap.get("minute"),
parameterSet.inputTimeMap.get("second"), durationInNano);
ZonedDateTime zonedDate = ZonedDateTime.of(dateTime, parameterSet.inputTimeZone.toZoneId()).toInstant()
.atZone(parameterSet.defaultTimeZone.toZoneId());
inputTimeString = zonedDate.format((DateTimeFormatter.ofPattern(DateTimeType.DATE_PATTERN_WITH_TZ_AND_MS)));
} else {
inputTimeString = parameterSet.inputTimeString;
}
DateTimeType dt = new DateTimeType(inputTimeString);
if (parameterSet.inputTimeZone == null) {
dt = new DateTimeType(dt.getZonedDateTime().withZoneSameInstant(TimeZone.getDefault().toZoneId()));
}
// Test
assertEquals(parameterSet.expectedResult, dt.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
*/
package org.eclipse.smarthome.core.library.types;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.PrimitiveType;
Expand All @@ -20,53 +24,88 @@
/**
*
* @author Kai Kreuzer - Initial contribution
* @author Erdoan Hadzhiyusein - Refactored to use ZonedDateTime
*/
public class DateTimeType implements PrimitiveType, State, Command {

public static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
public static final String DATE_PATTERN_WITH_TZ = "yyyy-MM-dd'T'HH:mm:ssz";

// this pattern returns the time zone in RFC822 format
public static final String DATE_PATTERN_WITH_TZ_AND_MS = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";

public static final String DATE_PATTERN_WITH_TZ_AND_MS_GENERAL = "yyyy-MM-dd'T'HH:mm:ss.SSSz";
public static final String DATE_PATTERN_WITH_TZ_AND_MS_ISO = "yyyy-MM-dd'T'HH:mm:ss.SSSX";

private Calendar calendar;
private ZonedDateTime zonedDateTime;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_PATTERN);
private final DateTimeFormatter formatterTz = DateTimeFormatter.ofPattern(DATE_PATTERN_WITH_TZ);
private final DateTimeFormatter formatterTzMs = DateTimeFormatter.ofPattern(DATE_PATTERN_WITH_TZ_AND_MS_GENERAL);
private final DateTimeFormatter formatterTzMsRFC = DateTimeFormatter.ofPattern(DATE_PATTERN_WITH_TZ_AND_MS);
private final DateTimeFormatter formatterTzMsIso = DateTimeFormatter.ofPattern(DATE_PATTERN_WITH_TZ_AND_MS_ISO);

/**
* @deprecated The constructor uses Calendar object hence it doesn't store time zone. A new constructor is
* available. Use {@link #DateTimeType(ZonedDateTime)} instead.
*
* @param calendar - The Calendar object containing the time stamp.
*/
@Deprecated
public DateTimeType(Calendar calendar) {
this.zonedDateTime = ZonedDateTime.ofInstant(calendar.toInstant(), TimeZone.getDefault().toZoneId())
.withFixedOffsetZone();
}

public DateTimeType() {
this(Calendar.getInstance());
this(ZonedDateTime.now());
}

public DateTimeType(Calendar calendar) {
this.calendar = (Calendar) calendar.clone();
public DateTimeType(ZonedDateTime zoned) {
this.zonedDateTime = ZonedDateTime.from(zoned).withFixedOffsetZone();
}

public DateTimeType(String calendarValue) {
Date date = null;
public DateTimeType(String zonedValue) {
ZonedDateTime date = null;

try {
try {
date = new SimpleDateFormat(DATE_PATTERN_WITH_TZ_AND_MS).parse(calendarValue);
} catch (ParseException fpe3) {
date = ZonedDateTime.parse(zonedValue, formatterTzMsRFC);
} catch (DateTimeParseException tzMsRfcException) {
try {
date = new SimpleDateFormat(DATE_PATTERN_WITH_TZ_AND_MS_ISO).parse(calendarValue);
} catch (ParseException fpe4) {
date = ZonedDateTime.parse(zonedValue, formatterTzMsIso);
} catch (DateTimeParseException tzMsException) {
try {
date = new SimpleDateFormat(DATE_PATTERN_WITH_TZ).parse(calendarValue);
} catch (ParseException fpe2) {
date = new SimpleDateFormat(DATE_PATTERN).parse(calendarValue);
date = ZonedDateTime.parse(zonedValue, formatterTz);
} catch (DateTimeParseException tzException) {
try {
date = ZonedDateTime.parse(zonedValue, formatterTzMs);
} catch (DateTimeParseException regularFormatException) {
// A ZonedDateTime object cannot be creating by parsing directly a pattern without zone
LocalDateTime localDateTime = LocalDateTime.parse(zonedValue, formatter);
date = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());
}
}
}
}
} catch (ParseException fpe) {
throw new IllegalArgumentException(calendarValue + " is not in a valid format.", fpe);
} catch (DateTimeParseException invalidFormatException) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e usually also is a commonly used choice for the variable name.

throw new IllegalArgumentException(zonedValue + " is not in a valid format.", invalidFormatException);
}

if (date != null) {
calendar = Calendar.getInstance();
calendar.setTime(date);
zonedDateTime = date;
}
}

/**
* @deprecated The method is deprecated. You can use {@link #getZonedDateTime()} instead.
*/
@Deprecated
public Calendar getCalendar() {
return (Calendar) calendar.clone();
return GregorianCalendar.from(zonedDateTime);
}

public ZonedDateTime getZonedDateTime() {
return zonedDateTime;
}

public static DateTimeType valueOf(String value) {
Expand All @@ -76,14 +115,14 @@ public static DateTimeType valueOf(String value) {
@Override
public String format(String pattern) {
try {
return String.format(pattern, calendar);
return String.format(pattern, zonedDateTime);
} catch (NullPointerException npe) {
return new SimpleDateFormat(DATE_PATTERN).format(calendar.getTime());
return DateTimeFormatter.ofPattern(DATE_PATTERN).format(zonedDateTime);
}
}

public String format(Locale locale, String pattern) {
return String.format(locale, pattern, calendar);
return String.format(locale, pattern, zonedDateTime);
}

@Override
Expand All @@ -93,14 +132,14 @@ public String toString() {

@Override
public String toFullString() {
return new SimpleDateFormat(DATE_PATTERN_WITH_TZ_AND_MS).format(calendar.getTime());
return zonedDateTime.format(formatterTzMsRFC);
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((calendar == null) ? 0 : calendar.hashCode());
result = prime * result + ((getZonedDateTime() == null) ? 0 : getZonedDateTime().hashCode());
return result;
}

Expand All @@ -116,14 +155,13 @@ public boolean equals(Object obj) {
return false;
}
DateTimeType other = (DateTimeType) obj;
if (calendar == null) {
if (other.calendar != null) {
if (zonedDateTime == null) {
if (other.zonedDateTime != null) {
return false;
}
} else if (calendar.compareTo(other.calendar) != 0) {
} else if (zonedDateTime.compareTo(other.zonedDateTime) != 0) {
return false;
}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
* @author Chris Jackson - Initial Contribution and add support for ModifiablePersistenceService
* @author Kai Kreuzer - Refactored to use PersistenceServiceRegistryImpl
* @author Franck Dechavanne - Added DTOs to ApiResponses
* @author Erdoan Hadzhiyusein - Adapted the convertTime() method to work with the new DateTimeType
*
*/
@Path(PersistenceResource.PATH)
Expand Down Expand Up @@ -188,7 +189,7 @@ public Response httpPutPersistenceItemData(@Context HttpHeaders headers,

private Date convertTime(String sTime) {
DateTimeType dateTime = new DateTimeType(sTime);
return dateTime.getCalendar().getTime();
return Date.from(dateTime.getZonedDateTime().toInstant());
}

private Response getItemHistoryDTO(String serviceId, String itemName, String timeBegin, String timeEnd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ public void getLabel_labelWithDate() throws ItemNotFoundException {
assertEquals("Label [01.06.2011]", label);
}

@Test
public void getLabel_labelWithZonedDate() throws ItemNotFoundException {
String testLabel = "Label [%1$td.%1$tm.%1$tY]";
Widget w = mock(Widget.class);
Item item = mock(Item.class);
when(w.getLabel()).thenReturn(testLabel);
when(w.getItem()).thenReturn("Item");
when(registry.getItem("Item")).thenReturn(item);
when(item.getState()).thenReturn(new DateTimeType("2011-06-01T00:00:00Z"));
String label = uiRegistry.getLabel(w);
assertEquals("Label [01.06.2011]", label);
}

@Test
public void getLabel_labelWithTime() throws ItemNotFoundException {
String testLabel = "Label [%1$tT]";
Expand All @@ -176,10 +189,25 @@ public void getLabel_labelWithTime() throws ItemNotFoundException {
when(w.getItem()).thenReturn("Item");
when(registry.getItem("Item")).thenReturn(item);
when(item.getState()).thenReturn(new DateTimeType("2011-06-01T15:30:59"));

String label = uiRegistry.getLabel(w);
assertEquals("Label [15:30:59]", label);
}

@Test
public void getLabel_labelWithZonedTime() throws ItemNotFoundException {
String testLabel = "Label [%1$tT]";
Widget w = mock(Widget.class);
Item item = mock(Item.class);
when(w.getLabel()).thenReturn(testLabel);
when(w.getItem()).thenReturn("Item");
when(registry.getItem("Item")).thenReturn(item);
when(item.getState()).thenReturn(new DateTimeType("2011-06-01T15:30:59Z"));

String label = uiRegistry.getLabel(w);
assertEquals("Label [15:30:59]", label);
}

@Test
public void getLabel_widgetWithoutLabelAndItem() throws ItemNotFoundException {
Widget w = mock(Widget.class);
Expand Down
Loading