[persistence] use Instant instead of ZonedDateTime in Riemann sum methods (#5101)

Signed-off-by: Jörg Sautter <joerg.sautter@gmx.net>
This commit is contained in:
joerg1985
2025-12-28 00:10:03 +01:00
committed by GitHub
parent df64ab2a75
commit 2fc75f61ba
2 changed files with 41 additions and 24 deletions
@@ -12,6 +12,8 @@
*/ */
package org.openhab.core.persistence; package org.openhab.core.persistence;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
@@ -22,7 +24,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.persistence.FilterCriteria.Ordering; import org.openhab.core.persistence.FilterCriteria.Ordering;
import org.openhab.core.types.State; import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/** /**
* A queryable persistence service which can be used to store and retrieve * A queryable persistence service which can be used to store and retrieve
@@ -65,6 +66,11 @@ public interface QueryablePersistenceService extends PersistenceService {
FilterCriteria aliasFilter = new FilterCriteria(filter).setItemName(alias); FilterCriteria aliasFilter = new FilterCriteria(filter).setItemName(alias);
return StreamSupport.stream(query(aliasFilter).spliterator(), false).map(hi -> new HistoricItem() { return StreamSupport.stream(query(aliasFilter).spliterator(), false).map(hi -> new HistoricItem() {
@Override
public Instant getInstant() {
return hi.getInstant();
}
@Override @Override
public ZonedDateTime getTimestamp() { public ZonedDateTime getTimestamp() {
return hi.getTimestamp(); return hi.getTimestamp();
@@ -111,8 +117,8 @@ public interface QueryablePersistenceService extends PersistenceService {
* @return a {@link PersistedItem} or null if the item has not been persisted * @return a {@link PersistedItem} or null if the item has not been persisted
*/ */
default @Nullable PersistedItem persistedItem(String itemName, @Nullable String alias) { default @Nullable PersistedItem persistedItem(String itemName, @Nullable String alias) {
State currentState = UnDefType.NULL; State currentState;
ZonedDateTime lastUpdate = null; Instant lastUpdate;
FilterCriteria filter = new FilterCriteria().setItemName(itemName).setEndDate(ZonedDateTime.now()) FilterCriteria filter = new FilterCriteria().setItemName(itemName).setEndDate(ZonedDateTime.now())
.setOrdering(Ordering.DESCENDING).setPageSize(1).setPageNumber(0); .setOrdering(Ordering.DESCENDING).setPageSize(1).setPageNumber(0);
@@ -120,19 +126,23 @@ public interface QueryablePersistenceService extends PersistenceService {
if (it.hasNext()) { if (it.hasNext()) {
HistoricItem historicItem = it.next(); HistoricItem historicItem = it.next();
currentState = historicItem.getState(); currentState = historicItem.getState();
lastUpdate = historicItem.getTimestamp(); lastUpdate = historicItem.getInstant();
} else { } else {
return null; return null;
} }
final State state = currentState; final State state = currentState;
final ZonedDateTime lastStateUpdate = lastUpdate; final Instant lastStateUpdate = lastUpdate;
return new PersistedItem() { return new PersistedItem() {
@Override
public Instant getInstant() {
return lastStateUpdate;
}
@Override @Override
public ZonedDateTime getTimestamp() { public ZonedDateTime getTimestamp() {
return lastStateUpdate; return lastStateUpdate.atZone(ZoneId.systemDefault());
} }
@Override @Override
@@ -15,6 +15,7 @@ package org.openhab.core.persistence.extensions;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.ArrayList;
@@ -71,6 +72,7 @@ import org.slf4j.LoggerFactory;
* @author Mark Herwege - add median methods * @author Mark Herwege - add median methods
* @author Mark Herwege - use item lastChange and lastUpdate methods if not in peristence * @author Mark Herwege - use item lastChange and lastUpdate methods if not in peristence
* @author Mark Herwege - add Riemann sum methods * @author Mark Herwege - add Riemann sum methods
* @author Jörg Sautter - use Instant instead of ZonedDateTime in Riemann sum methods
*/ */
@Component(immediate = true) @Component(immediate = true)
@NonNullByDefault @NonNullByDefault
@@ -1926,7 +1928,7 @@ public class PersistenceExtensions {
private static @Nullable BigDecimal average(ZonedDateTime begin, ZonedDateTime end, Iterator<HistoricItem> it, private static @Nullable BigDecimal average(ZonedDateTime begin, ZonedDateTime end, Iterator<HistoricItem> it,
@Nullable Unit<?> unit, @Nullable RiemannType type) { @Nullable Unit<?> unit, @Nullable RiemannType type) {
BigDecimal sum = riemannSum(begin, end, it, unit, type); BigDecimal sum = riemannSum(begin.toInstant(), end.toInstant(), it, unit, type);
BigDecimal totalDuration = BigDecimal.valueOf(Duration.between(begin, end).toMillis()); BigDecimal totalDuration = BigDecimal.valueOf(Duration.between(begin, end).toMillis());
if (totalDuration.signum() == 0) { if (totalDuration.signum() == 0) {
return null; return null;
@@ -2195,35 +2197,39 @@ public class PersistenceExtensions {
Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item; Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item;
Unit<?> unit = (baseItem instanceof NumberItem numberItem) Unit<?> unit = (baseItem instanceof NumberItem numberItem)
&& (numberItem.getUnit() instanceof Unit<?> numberItemUnit) ? numberItemUnit.getSystemUnit() : null; && (numberItem.getUnit() instanceof Unit<?> numberItemUnit) ? numberItemUnit.getSystemUnit() : null;
BigDecimal sum = riemannSum(beginTime, endTime, it, unit, type).scaleByPowerOfTen(-3); BigDecimal sum = riemannSum(beginTime.toInstant(), endTime.toInstant(), it, unit, type).scaleByPowerOfTen(-3);
if (unit != null) { if (unit != null) {
return new QuantityType<>(sum, unit.multiply(Units.SECOND)); return new QuantityType<>(sum, unit.multiply(Units.SECOND));
} }
return new DecimalType(sum); return new DecimalType(sum);
} }
private static BigDecimal riemannSum(ZonedDateTime begin, ZonedDateTime end, Iterator<HistoricItem> it, private static BigDecimal riemannSum(Instant begin, Instant end, Iterator<HistoricItem> it, @Nullable Unit<?> unit,
@Nullable Unit<?> unit, @Nullable RiemannType type) { @Nullable RiemannType type) {
RiemannType riemannType = type == null ? RiemannType.LEFT : type; RiemannType riemannType = type == null ? RiemannType.LEFT : type;
BigDecimal sum = BigDecimal.ZERO; BigDecimal sum = BigDecimal.ZERO;
HistoricItem prevItem = null; HistoricItem prevItem = null;
HistoricItem nextItem = null; HistoricItem nextItem;
DecimalType prevState = null; DecimalType prevState = null;
DecimalType nextState = null; DecimalType nextState;
Instant prevInstant = null;
Instant nextInstant;
Duration prevDuration = Duration.ZERO; Duration prevDuration = Duration.ZERO;
Duration nextDuration = Duration.ZERO; Duration nextDuration;
boolean midpointStartBucket = true; // The start and end buckets for the midpoint calculation should be boolean midpointStartBucket = true; // The start and end buckets for the midpoint calculation should be
// considered for the full length, this flag is used to find the start // considered for the full length, this flag is used to find the start
// bucket // bucket
if ((riemannType == RiemannType.MIDPOINT) && it.hasNext()) { if ((riemannType == RiemannType.MIDPOINT) && it.hasNext()) {
prevItem = it.next(); prevItem = it.next();
prevInstant = prevItem.getInstant();
prevState = getPersistedValue(prevItem, unit); prevState = getPersistedValue(prevItem, unit);
} }
while (it.hasNext()) { while (it.hasNext()) {
nextItem = it.next(); nextItem = it.next();
nextInstant = nextItem.getInstant();
BigDecimal weight = BigDecimal.ZERO; BigDecimal weight = BigDecimal.ZERO;
BigDecimal value = BigDecimal.ZERO; BigDecimal value = BigDecimal.ZERO;
switch (riemannType) { switch (riemannType) {
@@ -2232,24 +2238,24 @@ public class PersistenceExtensions {
prevState = getPersistedValue(prevItem, unit); prevState = getPersistedValue(prevItem, unit);
if (prevState != null) { if (prevState != null) {
value = prevState.toBigDecimal(); value = prevState.toBigDecimal();
weight = BigDecimal.valueOf( weight = BigDecimal.valueOf(Duration.between(prevInstant, nextInstant).toMillis());
Duration.between(prevItem.getTimestamp(), nextItem.getTimestamp()).toMillis());
} }
} }
prevItem = nextItem; prevItem = nextItem;
prevInstant = nextInstant;
break; break;
case RIGHT: case RIGHT:
nextState = getPersistedValue(nextItem, unit); nextState = getPersistedValue(nextItem, unit);
if (nextState != null) { if (nextState != null) {
value = nextState.toBigDecimal(); value = nextState.toBigDecimal();
if (prevItem == null) { if (prevItem == null) {
weight = BigDecimal.valueOf(Duration.between(begin, nextItem.getTimestamp()).toMillis()); weight = BigDecimal.valueOf(Duration.between(begin, nextInstant).toMillis());
} else { } else {
weight = BigDecimal.valueOf( weight = BigDecimal.valueOf(Duration.between(prevInstant, nextInstant).toMillis());
Duration.between(prevItem.getTimestamp(), nextItem.getTimestamp()).toMillis());
} }
} }
prevItem = nextItem; prevItem = nextItem;
prevInstant = nextInstant;
break; break;
case TRAPEZOIDAL: case TRAPEZOIDAL:
if (prevItem != null) { if (prevItem != null) {
@@ -2258,11 +2264,11 @@ public class PersistenceExtensions {
if (prevState != null && nextState != null) { if (prevState != null && nextState != null) {
value = prevState.toBigDecimal().add(nextState.toBigDecimal()) value = prevState.toBigDecimal().add(nextState.toBigDecimal())
.divide(BigDecimal.valueOf(2)); .divide(BigDecimal.valueOf(2));
weight = BigDecimal.valueOf( weight = BigDecimal.valueOf(Duration.between(prevInstant, nextInstant).toMillis());
Duration.between(prevItem.getTimestamp(), nextItem.getTimestamp()).toMillis());
} }
} }
prevItem = nextItem; prevItem = nextItem;
prevInstant = nextInstant;
break; break;
case MIDPOINT: case MIDPOINT:
if (prevItem != null) { if (prevItem != null) {
@@ -2272,12 +2278,12 @@ public class PersistenceExtensions {
if (midpointStartBucket && !prevDuration.isZero() && prevState != null) { if (midpointStartBucket && !prevDuration.isZero() && prevState != null) {
// Add half of the start bucket with the start value (left approximation) // Add half of the start bucket with the start value (left approximation)
sum = sum.add(prevState.toBigDecimal() sum = sum.add(prevState.toBigDecimal()
.multiply(BigDecimal.valueOf(prevDuration.dividedBy(2).toMillis()))); .multiply(BigDecimal.valueOf(prevDuration.toMillis() / 2)));
midpointStartBucket = false; midpointStartBucket = false;
} }
nextDuration = Duration.between(prevItem.getTimestamp(), nextItem.getTimestamp()); nextDuration = Duration.between(prevInstant, nextInstant);
weight = prevDuration.isZero() || nextDuration.isZero() ? BigDecimal.ZERO weight = prevDuration.isZero() || nextDuration.isZero() ? BigDecimal.ZERO
: BigDecimal.valueOf(prevDuration.plus(nextDuration).dividedBy(2).toMillis()); : BigDecimal.valueOf(prevDuration.plus(nextDuration).toMillis() / 2);
if (!nextDuration.isZero()) { if (!nextDuration.isZero()) {
prevDuration = nextDuration; prevDuration = nextDuration;
} }
@@ -2285,6 +2291,7 @@ public class PersistenceExtensions {
} }
} }
prevItem = nextItem; prevItem = nextItem;
prevInstant = nextInstant;
break; break;
} }
sum = sum.add(value.multiply(weight)); sum = sum.add(value.multiply(weight));
@@ -2295,7 +2302,7 @@ public class PersistenceExtensions {
DecimalType dtState = getPersistedValue(prevItem, unit); DecimalType dtState = getPersistedValue(prevItem, unit);
if (dtState != null) { if (dtState != null) {
BigDecimal value = dtState.toBigDecimal(); BigDecimal value = dtState.toBigDecimal();
BigDecimal weight = BigDecimal.valueOf(prevDuration.dividedBy(2).toMillis()); BigDecimal weight = BigDecimal.valueOf(prevDuration.toMillis() / 2);
sum = sum.add(value.multiply(weight)); sum = sum.add(value.multiply(weight));
} }
} }