mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-25 11:45:49 +01:00
fix QuantityType dimensionless one and time formatting (#4169)
Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
This commit is contained in:
parent
3b9a97101b
commit
6b5eed782c
@ -18,16 +18,22 @@ import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.IllegalFormatConversionException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingFormatArgumentException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.measure.Dimension;
|
||||
import javax.measure.IncommensurableException;
|
||||
import javax.measure.MetricPrefix;
|
||||
import javax.measure.Quantity;
|
||||
import javax.measure.Quantity.Scale;
|
||||
import javax.measure.UnconvertibleException;
|
||||
@ -40,7 +46,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.internal.library.unit.UnitInitializer;
|
||||
import org.openhab.core.items.events.ItemStateEvent;
|
||||
import org.openhab.core.library.unit.MetricPrefix;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.PrimitiveType;
|
||||
@ -69,6 +74,13 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
||||
private static final long serialVersionUID = 8828949721938234629L;
|
||||
private static final BigDecimal BIG_DECIMAL_HUNDRED = BigDecimal.valueOf(100);
|
||||
|
||||
// Patterns to identify formatting strings to format Time, derived from Java String formatting for DateTime
|
||||
private static final Pattern DAYS_PATTERN = Pattern.compile("%(?:1\\$)?[tT][de]");
|
||||
private static final Pattern HOURS_PATTERN = Pattern.compile("%(?:1\\$)?[tT][HkIl]");
|
||||
private static final Pattern MINUTES_PATTERN = Pattern.compile("%(?:1\\$)?[tT]M");
|
||||
private static final Pattern SECONDS_PATTERN = Pattern.compile("%(?:1\\$)?[tT][Ss]");
|
||||
private static final Pattern MILLIS_PATTERN = Pattern.compile("%(?:1\\$)?[tT][LQ]");
|
||||
|
||||
public static final QuantityType<Dimensionless> ZERO = new QuantityType<>(0, AbstractUnit.ONE);
|
||||
public static final QuantityType<Dimensionless> ONE = new QuantityType<>(1, AbstractUnit.ONE);
|
||||
|
||||
@ -376,6 +388,14 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
||||
|
||||
@Override
|
||||
public String format(String pattern) {
|
||||
if (pattern.contains("%s") || pattern.contains("%S")) {
|
||||
try {
|
||||
return String.format(pattern, quantity);
|
||||
} catch (IllegalFormatConversionException ifce) {
|
||||
// The conversion is not valid. Fall through trying other formatting options.
|
||||
}
|
||||
}
|
||||
|
||||
boolean unitPlaceholder = pattern.contains(UnitUtils.UNIT_PLACEHOLDER);
|
||||
final String formatPattern;
|
||||
|
||||
@ -386,16 +406,83 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
||||
formatPattern = pattern;
|
||||
}
|
||||
|
||||
// The dimension could be a time value thus we want to support patterns to format datetime
|
||||
// The dimension could be a time value thus we want to support patterns to format.
|
||||
// Wile time is representing a duration (Scale.RELATIVE), formatting patterns mimic String format patterns for
|
||||
// DateTime to not break backward compatibility and to avoid introducing specific duration formatting.
|
||||
if (quantity.getUnit().isCompatible(Units.SECOND) && !unitPlaceholder) {
|
||||
|
||||
QuantityType<T> millis = toUnit(MetricPrefix.MILLI(Units.SECOND));
|
||||
if (millis != null) {
|
||||
Duration duration = Duration.ofMillis(millis.longValue());
|
||||
|
||||
String timeFormatPattern = formatPattern;
|
||||
timeFormatPattern = timeFormatPattern.replaceAll("%(?:1\\$)?[tT]R", "%tH:%tM");
|
||||
timeFormatPattern = timeFormatPattern.replaceAll("%(?:1\\$)?[tT]T", "%tH:%tM:%tS");
|
||||
|
||||
enum Type {
|
||||
DAYS,
|
||||
HOURS,
|
||||
MINUTES,
|
||||
SECONDS,
|
||||
MILLIS
|
||||
}
|
||||
Map<Integer, Type> patternIndex = new HashMap<>();
|
||||
Matcher matcher = DAYS_PATTERN.matcher(timeFormatPattern);
|
||||
while (matcher.find()) {
|
||||
patternIndex.put(matcher.start(), Type.DAYS);
|
||||
}
|
||||
matcher = HOURS_PATTERN.matcher(timeFormatPattern);
|
||||
while (matcher.find()) {
|
||||
patternIndex.put(matcher.start(), Type.HOURS);
|
||||
}
|
||||
matcher = MINUTES_PATTERN.matcher(timeFormatPattern);
|
||||
while (matcher.find()) {
|
||||
patternIndex.put(matcher.start(), Type.MINUTES);
|
||||
}
|
||||
matcher = SECONDS_PATTERN.matcher(timeFormatPattern);
|
||||
while (matcher.find()) {
|
||||
patternIndex.put(matcher.start(), Type.SECONDS);
|
||||
}
|
||||
matcher = MILLIS_PATTERN.matcher(timeFormatPattern);
|
||||
while (matcher.find()) {
|
||||
patternIndex.put(matcher.start(), Type.MILLIS);
|
||||
}
|
||||
|
||||
long dd = duration.toDays();
|
||||
long hh = (patternIndex.values().contains(Type.DAYS) ? 0 : dd * 24) + duration.toHoursPart();
|
||||
long mm = (patternIndex.values().contains(Type.HOURS) ? 0 : hh * 60) + duration.toMinutesPart();
|
||||
long ss = (patternIndex.values().contains(Type.MINUTES) ? 0 : mm * 60) + duration.toSecondsPart();
|
||||
long mmm = (patternIndex.values().contains(Type.SECONDS) ? 0 : ss * 1000) + duration.toMillisPart();
|
||||
|
||||
List<Long> formatArgs = new ArrayList<>();
|
||||
patternIndex.entrySet().stream().sorted(Comparator.comparingInt(e -> e.getKey())).forEach(p -> {
|
||||
switch (p.getValue()) {
|
||||
case DAYS:
|
||||
formatArgs.add(dd);
|
||||
break;
|
||||
case HOURS:
|
||||
formatArgs.add(hh);
|
||||
break;
|
||||
case MINUTES:
|
||||
formatArgs.add(mm);
|
||||
break;
|
||||
case SECONDS:
|
||||
formatArgs.add(ss);
|
||||
break;
|
||||
case MILLIS:
|
||||
formatArgs.add(mmm);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
timeFormatPattern = timeFormatPattern.replaceAll("%(?:1\\$)?[tT][eklsQ]", "%d");
|
||||
timeFormatPattern = timeFormatPattern.replaceAll("%(?:1\\$)?[tT][dHIMS]", "%02d");
|
||||
timeFormatPattern = timeFormatPattern.replaceAll("%(?:1\\$)?[tT]L", "%03d");
|
||||
|
||||
try {
|
||||
return String.format(formatPattern,
|
||||
ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis.longValue()), ZoneOffset.UTC));
|
||||
} catch (IllegalFormatConversionException ifce) {
|
||||
// The conversion is not valid for the type ZonedDateTime. This happens, if the format is like
|
||||
// "%.1f". Fall through to default behavior.
|
||||
return String.format(timeFormatPattern, formatArgs.toArray());
|
||||
} catch (IllegalFormatConversionException | MissingFormatArgumentException ifce) {
|
||||
// The conversion is not valid. Fall through to default behavior.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -439,11 +526,7 @@ public class QuantityType<T extends Quantity<T>> extends Number
|
||||
|
||||
@Override
|
||||
public String toFullString() {
|
||||
if (AbstractUnit.ONE.equals(quantity.getUnit())) {
|
||||
return quantity.getValue().toString();
|
||||
} else {
|
||||
return quantity.toString();
|
||||
}
|
||||
return quantity.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -250,11 +250,17 @@ public class QuantityTypeTest {
|
||||
assertThat(millis.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("80000" + ds + "0 ms"));
|
||||
assertThat(minutes.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("1" + ds + "3 min"));
|
||||
|
||||
assertThat(seconds.format("%s"), is("80 s"));
|
||||
assertThat(millis.format("%s"), is("80000 ms"));
|
||||
|
||||
assertThat(seconds.format("%.1f"), is("80" + ds + "0"));
|
||||
assertThat(minutes.format("%.1f"), is("1" + ds + "3"));
|
||||
|
||||
assertThat(seconds.format("%1$tH:%1$tM:%1$tS"), is("00:01:20"));
|
||||
assertThat(millis.format("%1$tHh %1$tMm %1$tSs"), is("00h 01m 20s"));
|
||||
assertThat(millis.format("%1$tT.%1$tL"), is("00:01:20.000"));
|
||||
assertThat(seconds.format("%1$tss and %1$tSs"), is("80s and 80s"));
|
||||
assertThat(seconds.format("%1$tSs and %1$tMm"), is("20s and 01m"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user