Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Use Instant internally for DateTimeType #3583

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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 @@ -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 @@ -13,6 +13,7 @@
package org.openhab.core.io.rest.core.internal.item;

import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -57,6 +58,7 @@
import org.openhab.core.auth.Role;
import org.openhab.core.common.registry.RegistryChangedRunnableListener;
import org.openhab.core.events.EventPublisher;
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.LocaleService;
Expand Down Expand Up @@ -180,6 +182,7 @@ private static void respectForwarded(final UriBuilder uriBuilder, final @Context
private final MetadataRegistry metadataRegistry;
private final MetadataSelectorMatcher metadataSelectorMatcher;
private final SemanticTagRegistry semanticTagRegistry;
private final TimeZoneProvider timeZoneProvider;

private final RegistryChangedRunnableListener<Item> resetLastModifiedItemChangeListener = new RegistryChangedRunnableListener<>(
() -> lastModified = null);
Expand All @@ -198,7 +201,8 @@ public ItemResource(//
final @Reference ManagedItemProvider managedItemProvider,
final @Reference MetadataRegistry metadataRegistry,
final @Reference MetadataSelectorMatcher metadataSelectorMatcher,
final @Reference SemanticTagRegistry semanticTagRegistry) {
final @Reference SemanticTagRegistry semanticTagRegistry,
final @Reference TimeZoneProvider timeZoneProvider) {
this.dtoMapper = dtoMapper;
this.eventPublisher = eventPublisher;
this.itemBuilderFactory = itemBuilderFactory;
Expand All @@ -208,6 +212,7 @@ public ItemResource(//
this.metadataRegistry = metadataRegistry;
this.metadataSelectorMatcher = metadataSelectorMatcher;
this.semanticTagRegistry = semanticTagRegistry;
this.timeZoneProvider = timeZoneProvider;

this.itemRegistry.addRegistryChangeListener(resetLastModifiedItemChangeListener);
this.metadataRegistry.addRegistryChangeListener(resetLastModifiedMetadataChangeListener);
Expand Down Expand Up @@ -240,6 +245,7 @@ public Response getItems(final @Context UriInfo uriInfo, final @Context HttpHead
@QueryParam("fields") @Parameter(description = "limit output to the given fields (comma separated)") @Nullable String fields,
@DefaultValue("false") @QueryParam("staticDataOnly") @Parameter(description = "provides a cacheable list of values not expected to change regularly and checks the If-Modified-Since header, all other parameters are ignored except \"metadata\"") boolean staticDataOnly) {
final Locale locale = localeService.getLocale(language);
final ZoneId zoneId = timeZoneProvider.getTimeZone();
final Set<String> namespaces = splitAndFilterNamespaces(namespaceSelector, locale);

final UriBuilder uriBuilder = uriBuilder(uriInfo, httpHeaders);
Expand All @@ -256,7 +262,7 @@ public Response getItems(final @Context UriInfo uriInfo, final @Context HttpHead
}

Stream<EnrichedItemDTO> itemStream = getItems(type, tags).stream() //
.map(item -> EnrichedItemDTOMapper.map(item, false, null, uriBuilder, locale)) //
.map(item -> EnrichedItemDTOMapper.map(item, false, null, uriBuilder, locale, zoneId)) //
.peek(dto -> addMetadata(dto, namespaces, null)) //
.peek(dto -> dto.editable = isEditable(dto.name));
itemStream = dtoMapper.limitToFields(itemStream,
Expand All @@ -267,7 +273,7 @@ public Response getItems(final @Context UriInfo uriInfo, final @Context HttpHead
}

Stream<EnrichedItemDTO> itemStream = getItems(type, tags).stream() //
.map(item -> EnrichedItemDTOMapper.map(item, recursive, null, uriBuilder, locale)) //
.map(item -> EnrichedItemDTOMapper.map(item, recursive, null, uriBuilder, locale, zoneId)) //
.peek(dto -> addMetadata(dto, namespaces, null)) //
.peek(dto -> dto.editable = isEditable(dto.name)) //
.peek(dto -> {
Expand Down Expand Up @@ -318,6 +324,7 @@ public Response getItemByName(final @Context UriInfo uriInfo, final @Context Htt
@DefaultValue("true") @QueryParam("recursive") @Parameter(description = "get member items if the item is a group item") boolean recursive,
@PathParam("itemname") @Parameter(description = "item name") String itemname) {
final Locale locale = localeService.getLocale(language);
final ZoneId zoneId = timeZoneProvider.getTimeZone();
final Set<String> namespaces = splitAndFilterNamespaces(namespaceSelector, locale);

// get item
Expand All @@ -326,7 +333,7 @@ public Response getItemByName(final @Context UriInfo uriInfo, final @Context Htt
// if it exists
if (item != null) {
EnrichedItemDTO dto = EnrichedItemDTOMapper.map(item, recursive, null, uriBuilder(uriInfo, httpHeaders),
locale);
locale, zoneId);
addMetadata(dto, namespaces, null);
dto.editable = isEditable(dto.name);
if (dto instanceof EnrichedGroupItemDTO enrichedGroupItemDTO) {
Expand Down Expand Up @@ -424,6 +431,7 @@ public Response putItemState(
@PathParam("itemname") @Parameter(description = "item name") String itemname,
@Parameter(description = "valid item state (e.g. ON, OFF)", required = true) String value) {
final Locale locale = localeService.getLocale(language);
final ZoneId zoneId = timeZoneProvider.getTimeZone();

// get Item
Item item = getItem(itemname);
Expand All @@ -436,7 +444,7 @@ public Response putItemState(
if (state != null) {
// set State and report OK
eventPublisher.post(ItemEventFactory.createStateEvent(itemname, state));
return getItemResponse(null, Status.ACCEPTED, null, locale, null);
return getItemResponse(null, Status.ACCEPTED, null, locale, zoneId, null);
} else {
// State could not be parsed
return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "State could not be parsed: " + value);
Expand Down Expand Up @@ -739,6 +747,7 @@ public Response createOrUpdateItem(final @Context UriInfo uriInfo, final @Contex
@PathParam("itemname") @Parameter(description = "item name") String itemname,
@Parameter(description = "item data", required = true) @Nullable GroupItemDTO item) {
final Locale locale = localeService.getLocale(language);
final ZoneId zoneId = timeZoneProvider.getTimeZone();

// If we didn't get an item bean, then return!
if (item == null) {
Expand All @@ -763,12 +772,12 @@ public Response createOrUpdateItem(final @Context UriInfo uriInfo, final @Contex
// item does not yet exist, create it
managedItemProvider.add(newItem);
return getItemResponse(uriBuilder(uriInfo, httpHeaders), Status.CREATED, itemRegistry.get(itemname),
locale, null);
locale, zoneId, null);
} else if (managedItemProvider.get(itemname) != null) {
// item already exists as a managed item, update it
managedItemProvider.update(newItem);
return getItemResponse(uriBuilder(uriInfo, httpHeaders), Status.OK, itemRegistry.get(itemname), locale,
null);
zoneId, null);
} else {
// Item exists but cannot be updated
logger.warn("Cannot update existing item '{}', because is not managed.", itemname);
Expand Down Expand Up @@ -872,7 +881,8 @@ public Response getSemanticItem(final @Context UriInfo uriInfo, final @Context H
@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @Parameter(description = "language") @Nullable String language,
@PathParam("itemName") @Parameter(description = "item name") String itemName,
@PathParam("semanticClass") @Parameter(description = "semantic class") String semanticClassName) {
Locale locale = localeService.getLocale(language);
final Locale locale = localeService.getLocale(language);
final ZoneId zoneId = timeZoneProvider.getTimeZone();

Class<? extends org.openhab.core.semantics.Tag> semanticClass = semanticTagRegistry
.getTagClassById(semanticClassName);
Expand All @@ -886,7 +896,7 @@ public Response getSemanticItem(final @Context UriInfo uriInfo, final @Context H
}

EnrichedItemDTO dto = EnrichedItemDTOMapper.map(foundItem, false, null, uriBuilder(uriInfo, httpHeaders),
locale);
locale, zoneId);
dto.editable = isEditable(dto.name);
return JSONResponse.createResponse(Status.OK, dto, null);
}
Expand Down Expand Up @@ -935,8 +945,8 @@ private static Response getItemNotFoundResponse(String itemname) {
* @return Response configured to represent the Item in depending on the status
*/
private Response getItemResponse(final @Nullable UriBuilder uriBuilder, Status status, @Nullable Item item,
Locale locale, @Nullable String errormessage) {
Object entity = null != item ? EnrichedItemDTOMapper.map(item, true, null, uriBuilder, locale) : null;
Locale locale, ZoneId zoneId, @Nullable String errormessage) {
Object entity = null != item ? EnrichedItemDTOMapper.map(item, true, null, uriBuilder, locale, zoneId) : null;
return JSONResponse.createResponse(status, entity, errormessage);
}

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,6 +12,7 @@
*/
package org.openhab.core.io.rest.core.item;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
Expand All @@ -29,7 +30,9 @@
import org.openhab.core.items.Item;
import org.openhab.core.items.dto.ItemDTO;
import org.openhab.core.items.dto.ItemDTOMapper;
import org.openhab.core.library.items.DateTimeItem;
import org.openhab.core.library.items.NumberItem;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationHelper;
import org.openhab.core.transform.TransformationService;
Expand Down Expand Up @@ -63,28 +66,39 @@ public class EnrichedItemDTOMapper {
* @param uriBuilder if present the URI builder contains one template that will be replaced by the specific item
* name
* @param locale locale (can be null)
* @param zoneId time-zone id (can be null)
* @return item DTO object
*/
public static EnrichedItemDTO map(Item item, boolean drillDown, @Nullable Predicate<Item> itemFilter,
@Nullable UriBuilder uriBuilder, @Nullable Locale locale) {
@Nullable UriBuilder uriBuilder, @Nullable Locale locale, @Nullable ZoneId zoneId) {
ItemDTO itemDTO = ItemDTOMapper.map(item);
return map(item, itemDTO, drillDown, itemFilter, uriBuilder, locale, new ArrayList<>());
return map(item, itemDTO, drillDown, itemFilter, uriBuilder, locale, zoneId, new ArrayList<>());
}

private static EnrichedItemDTO mapRecursive(Item item, @Nullable Predicate<Item> itemFilter,
@Nullable UriBuilder uriBuilder, @Nullable Locale locale, List<Item> parents) {
@Nullable UriBuilder uriBuilder, @Nullable Locale locale, @Nullable ZoneId zoneId, List<Item> parents) {
ItemDTO itemDTO = ItemDTOMapper.map(item);
return map(item, itemDTO, true, itemFilter, uriBuilder, locale, parents);
return map(item, itemDTO, true, itemFilter, uriBuilder, locale, zoneId, parents);
}

private static EnrichedItemDTO map(Item item, ItemDTO itemDTO, boolean drillDown,
@Nullable Predicate<Item> itemFilter, @Nullable UriBuilder uriBuilder, @Nullable Locale locale,
List<Item> parents) {
@Nullable ZoneId zoneId, List<Item> parents) {
if (item instanceof GroupItem) {
// only add as parent item if it is a group, otherwise duplicate memberships trigger false warnings
parents.add(item);
}
String state = item.getState().toFullString();
String state;
if (item instanceof DateTimeItem dateTimeItem && zoneId != null) {
DateTimeType dateTime = dateTimeItem.getStateAs(DateTimeType.class);
if (dateTime == null) {
state = item.getState().toFullString();
} else {
state = dateTime.toFullString(zoneId);
}
} else {
state = item.getState().toFullString();
}
String transformedState = considerTransformation(item, locale);
if (state.equals(transformedState)) {
transformedState = null;
Expand Down Expand Up @@ -117,7 +131,8 @@ private static EnrichedItemDTO map(Item item, ItemDTO itemDTO, boolean drillDown
"Recursive group membership found: {} is a member of {}, but it is also one of its ancestors.",
member.getName(), groupItem.getName());
} else if (itemFilter == null || itemFilter.test(member)) {
members.add(mapRecursive(member, itemFilter, uriBuilder, locale, new ArrayList<>(parents)));
members.add(
mapRecursive(member, itemFilter, uriBuilder, locale, zoneId, new ArrayList<>(parents)));
}
}
memberDTOs = members.toArray(new EnrichedItemDTO[0]);
Expand Down
Loading