Skip to content

Commit

Permalink
Remove obsoleted time-zone handling
Browse files Browse the repository at this point in the history
Signed-off-by: Jacob Laursen <[email protected]>
  • Loading branch information
jlaur committed Nov 14, 2024
1 parent e1d2570 commit 82466b3
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.openhab.core.config.core.ConfigUtil;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.events.Event;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.rest.DTOMapper;
import org.openhab.core.io.rest.JSONResponse;
import org.openhab.core.io.rest.RESTConstants;
Expand Down Expand Up @@ -133,6 +134,7 @@ public class RuleResource implements RESTResource {
private final RuleManager ruleManager;
private final RuleRegistry ruleRegistry;
private final ManagedRuleProvider managedRuleProvider;
private final TimeZoneProvider timeZoneProvider;
private final RegistryChangedRunnableListener<Rule> resetLastModifiedChangeListener = new RegistryChangedRunnableListener<>(
() -> lastModified = null);

Expand All @@ -144,11 +146,13 @@ public RuleResource( //
final @Reference DTOMapper dtoMapper, //
final @Reference RuleManager ruleManager, //
final @Reference RuleRegistry ruleRegistry, //
final @Reference ManagedRuleProvider managedRuleProvider) {
final @Reference ManagedRuleProvider managedRuleProvider, //
final @Reference TimeZoneProvider timeZoneProvider) {
this.dtoMapper = dtoMapper;
this.ruleManager = ruleManager;
this.ruleRegistry = ruleRegistry;
this.managedRuleProvider = managedRuleProvider;
this.timeZoneProvider = timeZoneProvider;

this.ruleRegistry.addRegistryChangeListener(resetLastModifiedChangeListener);
}
Expand Down Expand Up @@ -431,9 +435,9 @@ public Response simulateRules(
return Response.ok(ruleExecutions.toList()).build();
}

private static ZonedDateTime parseTime(String sTime) {
private ZonedDateTime parseTime(String sTime) {
final DateTimeType dateTime = new DateTimeType(sTime);
return dateTime.getZonedDateTime();
return dateTime.getInstant().atZone(timeZoneProvider.getTimeZone());
}

private static long daysBetween(ZonedDateTime d1, ZonedDateTime d2) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ private void process(Type value) {
cronExpression = CronAdjuster.REBOOT;
} else if (value instanceof DateTimeType dateTimeType) {
boolean itemIsTimeOnly = dateTimeType.toString().startsWith("1970-01-01T");
cronExpression = dateTimeType.getZonedDateTime().withZoneSameInstant(ZoneId.systemDefault())
.plusSeconds(offset.longValue())
cronExpression = dateTimeType.getInstant().atZone(ZoneId.systemDefault()).plusSeconds(offset.longValue())
.format(timeOnly || itemIsTimeOnly ? CRON_TIMEONLY_FORMATTER : CRON_FORMATTER);
startScheduler();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.openhab.core.automation.internal.module.handler;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
Expand Down Expand Up @@ -158,9 +159,9 @@ private boolean lessThanOrEqualsToItemState(String itemName, String state) throw
Item item = itemRegistry.getItem(itemName);
State compareState = TypeParser.parseState(item.getAcceptedDataTypes(), state);
State itemState = item.getState();
if (itemState instanceof DateTimeType type) {
ZonedDateTime itemTime = type.getZonedDateTime();
ZonedDateTime compareTime = getCompareTime(state);
if (itemState instanceof DateTimeType dateTimeState) {
Instant itemTime = dateTimeState.getInstant();
Instant compareTime = getCompareTime(state).toInstant();
return itemTime.compareTo(compareTime) <= 0;
} else if (itemState instanceof QuantityType qtState) {
if (compareState instanceof DecimalType type) {
Expand Down Expand Up @@ -195,9 +196,9 @@ private boolean greaterThanOrEqualsToItemState(String itemName, String state) th
Item item = itemRegistry.getItem(itemName);
State compareState = TypeParser.parseState(item.getAcceptedDataTypes(), state);
State itemState = item.getState();
if (itemState instanceof DateTimeType type) {
ZonedDateTime itemTime = type.getZonedDateTime();
ZonedDateTime compareTime = getCompareTime(state);
if (itemState instanceof DateTimeType dateTimeState) {
Instant itemTime = dateTimeState.getInstant();
Instant compareTime = getCompareTime(state).toInstant();
return itemTime.compareTo(compareTime) >= 0;
} else if (itemState instanceof QuantityType qtState) {
if (compareState instanceof DecimalType type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ public Response httpPutPersistenceItemData(@Context HttpHeaders headers,

private ZonedDateTime convertTime(String sTime) {
DateTimeType dateTime = new DateTimeType(sTime);
return dateTime.getZonedDateTime();
return dateTime.getInstant().atZone(timeZoneProvider.getTimeZone());
}

private Response getItemHistoryDTO(@Nullable String serviceId, String itemName, @Nullable String timeBegin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
*/
package org.openhab.core.thing.internal.profiles;

import java.time.DateTimeException;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Instant;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.internal.i18n.I18nProviderImpl;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.thing.profiles.ProfileCallback;
import org.openhab.core.thing.profiles.ProfileContext;
Expand All @@ -36,22 +33,18 @@
/**
* Applies the given parameter "offset" to a {@link DateTimeType} state.
*
* Options for the "timezone" parameter are provided by the {@link I18nProviderImpl}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
public class TimestampOffsetProfile implements StateProfile {

static final String OFFSET_PARAM = "offset";
static final String TIMEZONE_PARAM = "timezone";

private final Logger logger = LoggerFactory.getLogger(TimestampOffsetProfile.class);

private final ProfileCallback callback;

private final Duration offset;
private @Nullable ZoneId timeZone;

public TimestampOffsetProfile(ProfileCallback callback, ProfileContext context) {
this.callback = callback;
Expand All @@ -68,19 +61,6 @@ public TimestampOffsetProfile(ProfileCallback callback, ProfileContext context)
OFFSET_PARAM);
offset = Duration.ZERO;
}

String timeZoneParam = toStringOrNull(context.getConfiguration().get(TIMEZONE_PARAM));
logger.debug("Configuring profile with {} parameter '{}'", TIMEZONE_PARAM, timeZoneParam);
if (timeZoneParam == null || timeZoneParam.isBlank()) {
timeZone = null;
} else {
try {
timeZone = ZoneId.of(timeZoneParam);
} catch (DateTimeException e) {
logger.debug("Error setting time zone '{}': {}", timeZoneParam, e.getMessage());
timeZone = null;
}
}
}

private @Nullable String toStringOrNull(@Nullable Object value) {
Expand All @@ -98,20 +78,20 @@ public void onStateUpdateFromItem(State state) {

@Override
public void onCommandFromItem(Command command) {
callback.handleCommand((Command) applyOffsetAndTimezone(command, false));
callback.handleCommand((Command) applyOffset(command, false));
}

@Override
public void onCommandFromHandler(Command command) {
callback.sendCommand((Command) applyOffsetAndTimezone(command, true));
callback.sendCommand((Command) applyOffset(command, true));
}

@Override
public void onStateUpdateFromHandler(State state) {
callback.sendUpdate((State) applyOffsetAndTimezone(state, true));
callback.sendUpdate((State) applyOffset(state, true));
}

private Type applyOffsetAndTimezone(Type type, boolean towardsItem) {
private Type applyOffset(Type type, boolean towardsItem) {
if (type instanceof UnDefType) {
// we cannot adjust UNDEF or NULL values, thus we simply return them without reporting an error or warning
return type;
Expand All @@ -120,20 +100,15 @@ private Type applyOffsetAndTimezone(Type type, boolean towardsItem) {
Duration finalOffset = towardsItem ? offset : offset.negated();
Type result;
if (type instanceof DateTimeType timeType) {
ZonedDateTime zdt = timeType.getZonedDateTime();
Instant instant = timeType.getInstant();

// apply offset
if (!Duration.ZERO.equals(offset)) {
// we do not need apply an offset equals to 0
zdt = zdt.plus(finalOffset);
instant = instant.plus(finalOffset);
}

// apply time zone
ZoneId localTimeZone = timeZone;
if (localTimeZone != null && !zdt.getZone().equals(localTimeZone) && towardsItem) {
zdt = zdt.withZoneSameInstant(localTimeZone);
}
result = new DateTimeType(zdt);
result = new DateTimeType(instant);
} else {
logger.warn(
"Offset '{}' cannot be applied to the incompatible state '{}' sent from the binding. Returning original state.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,5 @@
in the reverse
direction.</description>
</parameter>
<parameter name="timezone" type="text">
<label>Time Zone</label>
<description>A time zone to be applied on the state towards the item.</description>
<advanced>true</advanced>
</parameter>
</config-description>
</config-description:config-descriptions>
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,5 @@ profile-type.system.timestamp-change.label = Timestamp on Change
profile-type.system.timestamp-offset.label = Timestamp Offset
profile.config.system.timestamp-offset.offset.label = Offset
profile.config.system.timestamp-offset.offset.description = Offset to be applied on the state towards the item. The negative offset will be applied in the reverse direction.
profile.config.system.timestamp-offset.timezone.label = Time Zone
profile.config.system.timestamp-offset.timezone.description = A time zone to be applied on the state.
profile-type.system.timestamp-trigger.label = Timestamp on Trigger
profile-type.system.timestamp-update.label = Timestamp on Update
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
Expand All @@ -44,28 +43,23 @@ public class TimestampOffsetProfileTest {

public static class ParameterSet {
public final long seconds;
public final @Nullable String timeZone;

public ParameterSet(long seconds, @Nullable String timeZone) {
public ParameterSet(long seconds) {
this.seconds = seconds;
this.timeZone = timeZone;
}
}

public static Collection<Object[]> parameters() {
return List.of(new Object[][] { //
{ new ParameterSet(0, null) }, //
{ new ParameterSet(30, null) }, //
{ new ParameterSet(-30, null) }, //
{ new ParameterSet(0, "Europe/Berlin") }, //
{ new ParameterSet(30, "Europe/Berlin") }, //
{ new ParameterSet(-30, "Europe/Berlin") } });
{ new ParameterSet(0) }, //
{ new ParameterSet(30) }, //
{ new ParameterSet(-30) } });
}

@Test
public void testUNDEFOnStateUpdateFromHandler() {
ProfileCallback callback = mock(ProfileCallback.class);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(60), null);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(60));

State state = UnDefType.UNDEF;
offsetProfile.onStateUpdateFromHandler(state);
Expand All @@ -81,8 +75,7 @@ public void testUNDEFOnStateUpdateFromHandler() {
@MethodSource("parameters")
public void testOnCommandFromItem(ParameterSet parameterSet) {
ProfileCallback callback = mock(ProfileCallback.class);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(parameterSet.seconds),
parameterSet.timeZone);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(parameterSet.seconds));

Command cmd = DateTimeType.valueOf("2021-03-30T10:58:47.033+0000");
offsetProfile.onCommandFromItem(cmd);
Expand All @@ -93,16 +86,15 @@ public void testOnCommandFromItem(ParameterSet parameterSet) {
Command result = capture.getValue();
DateTimeType updateResult = (DateTimeType) result;
DateTimeType expectedResult = new DateTimeType(
((DateTimeType) cmd).getZonedDateTime().minusSeconds(parameterSet.seconds));
((DateTimeType) cmd).getInstant().minusSeconds(parameterSet.seconds));
assertEquals(expectedResult.getInstant(), updateResult.getInstant());
}

@ParameterizedTest
@MethodSource("parameters")
public void testOnCommandFromHandler(ParameterSet parameterSet) {
ProfileCallback callback = mock(ProfileCallback.class);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(parameterSet.seconds),
parameterSet.timeZone);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(parameterSet.seconds));

Command cmd = new DateTimeType("2021-03-30T10:58:47.033+0000");
offsetProfile.onCommandFromHandler(cmd);
Expand All @@ -113,16 +105,15 @@ public void testOnCommandFromHandler(ParameterSet parameterSet) {
Command result = capture.getValue();
DateTimeType updateResult = (DateTimeType) result;
DateTimeType expectedResult = new DateTimeType(
((DateTimeType) cmd).getZonedDateTime().plusSeconds(parameterSet.seconds));
((DateTimeType) cmd).getInstant().plusSeconds(parameterSet.seconds));
assertEquals(expectedResult.getInstant(), updateResult.getInstant());
}

@ParameterizedTest
@MethodSource("parameters")
public void testOnStateUpdateFromHandler(ParameterSet parameterSet) {
ProfileCallback callback = mock(ProfileCallback.class);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(parameterSet.seconds),
parameterSet.timeZone);
TimestampOffsetProfile offsetProfile = createProfile(callback, Long.toString(parameterSet.seconds));

State state = new DateTimeType("2021-03-30T10:58:47.033+0000");
offsetProfile.onStateUpdateFromHandler(state);
Expand All @@ -133,17 +124,14 @@ public void testOnStateUpdateFromHandler(ParameterSet parameterSet) {
State result = capture.getValue();
DateTimeType updateResult = (DateTimeType) result;
DateTimeType expectedResult = new DateTimeType(
((DateTimeType) state).getZonedDateTime().plusSeconds(parameterSet.seconds));
((DateTimeType) state).getInstant().plusSeconds(parameterSet.seconds));
assertEquals(expectedResult.getInstant(), updateResult.getInstant());
}

private TimestampOffsetProfile createProfile(ProfileCallback callback, String offset, @Nullable String timeZone) {
private TimestampOffsetProfile createProfile(ProfileCallback callback, String offset) {
ProfileContext context = mock(ProfileContext.class);
Map<String, Object> properties = new HashMap<>();
properties.put(TimestampOffsetProfile.OFFSET_PARAM, offset);
if (timeZone != null) {
properties.put(TimestampOffsetProfile.TIMEZONE_PARAM, timeZone);
}
when(context.getConfiguration()).thenReturn(new Configuration(properties));
return new TimestampOffsetProfile(callback, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;

import java.time.ZonedDateTime;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
Expand All @@ -40,15 +40,15 @@ public void testTimestampOnUpdateStateUpdateFromHandler() {
ProfileCallback callback = mock(ProfileCallback.class);
TimestampUpdateProfile timestampProfile = new TimestampUpdateProfile(callback);

ZonedDateTime now = ZonedDateTime.now();
Instant now = Instant.now();
timestampProfile.onStateUpdateFromHandler(new DecimalType(23));

ArgumentCaptor<State> capture = ArgumentCaptor.forClass(State.class);
verify(callback, times(1)).sendUpdate(capture.capture());

State result = capture.getValue();
DateTimeType updateResult = (DateTimeType) result;
ZonedDateTime timestamp = updateResult.getZonedDateTime();
Instant timestamp = updateResult.getInstant();
long difference = ChronoUnit.MINUTES.between(now, timestamp);
assertTrue(difference < 1);
}
Expand All @@ -66,7 +66,7 @@ public void testTimestampOnChangeStateUpdateFromHandler() {
State result = capture.getValue();
DateTimeType changeResult = (DateTimeType) result;

waitForAssert(() -> assertTrue(ZonedDateTime.now().isAfter(changeResult.getZonedDateTime())));
waitForAssert(() -> assertTrue(Instant.now().isAfter(changeResult.getInstant())));

// The state is unchanged, no additional call to the callback
timestampProfile.onStateUpdateFromHandler(new DecimalType(23));
Expand All @@ -77,6 +77,6 @@ public void testTimestampOnChangeStateUpdateFromHandler() {
verify(callback, times(2)).sendUpdate(capture.capture());
result = capture.getValue();
DateTimeType updatedResult = (DateTimeType) result;
assertTrue(updatedResult.getZonedDateTime().isAfter(changeResult.getZonedDateTime()));
assertTrue(updatedResult.getInstant().isAfter(changeResult.getInstant()));
}
}
Loading

0 comments on commit 82466b3

Please sign in to comment.