add interval splitting for bar charts
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
package org.lucares.utils;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
|
||||
public class BeginningOfNextInterval implements TemporalAdjuster {
|
||||
|
||||
private final ChronoUnit unit;
|
||||
|
||||
public BeginningOfNextInterval(final ChronoUnit unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Temporal adjustInto(final Temporal temporal) {
|
||||
Temporal result = temporal;
|
||||
final StartOfInterval startOfInterval = new StartOfInterval(unit);
|
||||
result = result.with(startOfInterval);
|
||||
|
||||
switch (unit) {
|
||||
case MINUTES: {
|
||||
result = result.plus(1, ChronoUnit.MINUTES);
|
||||
break;
|
||||
}
|
||||
case HOURS: {
|
||||
result = result.plus(1, ChronoUnit.HOURS);
|
||||
break;
|
||||
}
|
||||
case DAYS: {
|
||||
result = result.plus(1, ChronoUnit.DAYS);
|
||||
break;
|
||||
}
|
||||
case WEEKS: {
|
||||
result = result.plus(1, ChronoUnit.WEEKS);
|
||||
break;
|
||||
}
|
||||
case MONTHS: {
|
||||
result = result.plus(1, ChronoUnit.MONTHS);
|
||||
break;
|
||||
}
|
||||
case YEARS: {
|
||||
result = result.plus(1, ChronoUnit.YEARS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + unit);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
25
pdb-utils/src/main/java/org/lucares/utils/EndOfInterval.java
Normal file
25
pdb-utils/src/main/java/org/lucares/utils/EndOfInterval.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package org.lucares.utils;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
|
||||
public class EndOfInterval implements TemporalAdjuster {
|
||||
|
||||
private final ChronoUnit unit;
|
||||
|
||||
public EndOfInterval(final ChronoUnit unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Temporal adjustInto(final Temporal temporal) {
|
||||
Temporal result = temporal;
|
||||
final BeginningOfNextInterval beginningOfnextInterval = new BeginningOfNextInterval(unit);
|
||||
result = result.with(beginningOfnextInterval);
|
||||
|
||||
result = result.minus(1, ChronoUnit.NANOS);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
@@ -54,12 +55,15 @@ public class LongToDateBucket {
|
||||
*/
|
||||
private final DateTimeFormatter datePattern;
|
||||
|
||||
ChronoUnit chronoUnit;
|
||||
|
||||
// visible for test
|
||||
final ConcurrentNavigableMap<Long, DatePrefixAndRange> datePrefixCache = new ConcurrentSkipListMap<>();
|
||||
|
||||
private final AtomicReference<DatePrefixAndRange> lastAccessed = new AtomicReference<>(null);
|
||||
|
||||
public LongToDateBucket(final String dateFormatPattern) {
|
||||
public LongToDateBucket(final String dateFormatPattern, final ChronoUnit chronoUnit) {
|
||||
this.chronoUnit = chronoUnit;
|
||||
this.datePattern = DateTimeFormatter.ofPattern(dateFormatPattern);
|
||||
}
|
||||
|
||||
@@ -104,40 +108,43 @@ public class LongToDateBucket {
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toDateIndexPrefix(final long epochMilli) {
|
||||
|
||||
final Entry<Long, DatePrefixAndRange> value = datePrefixCache.floorEntry(epochMilli);
|
||||
|
||||
String result;
|
||||
if (value == null || !value.getValue().contains(epochMilli)) {
|
||||
final DatePrefixAndRange newValue = toDatePrefixAndRange(epochMilli);
|
||||
datePrefixCache.put(newValue.getMinEpochMilli(), newValue);
|
||||
result = newValue.getDatePrefix();
|
||||
} else {
|
||||
result = value.getValue().getDatePrefix();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
// public String toDateIndexPrefix(final long epochMilli) {
|
||||
//
|
||||
// final Entry<Long, DatePrefixAndRange> value = datePrefixCache.floorEntry(epochMilli);
|
||||
//
|
||||
// String result;
|
||||
// if (value == null || !value.getValue().contains(epochMilli)) {
|
||||
// final DatePrefixAndRange newValue = toDatePrefixAndRange(epochMilli);
|
||||
// datePrefixCache.put(newValue.getMinEpochMilli(), newValue);
|
||||
// result = newValue.getDatePrefix();
|
||||
// } else {
|
||||
// result = value.getValue().getDatePrefix();
|
||||
// }
|
||||
//
|
||||
// return result;
|
||||
// }
|
||||
|
||||
/**
|
||||
* only for tests, use toPartitionIds(final DateTimeRange dateRange,final
|
||||
* Collection<? extends PartitionId> availablePartitionIds) instead
|
||||
*
|
||||
* @param chronoUnit
|
||||
*
|
||||
* @param dateRange
|
||||
* @return
|
||||
*/
|
||||
public List<String> toPartitionIds(final OffsetDateTime start, final OffsetDateTime end) {
|
||||
public List<String> toPartitionIds(final OffsetDateTime start, final OffsetDateTime end,
|
||||
final ChronoUnit chronoUnit) {
|
||||
final List<String> result = new ArrayList<>();
|
||||
|
||||
OffsetDateTime current = start;
|
||||
current = current.withOffsetSameInstant(ZoneOffset.UTC).withDayOfMonth(1).withHour(0).withMinute(0)
|
||||
.withSecond(0).withNano(0);
|
||||
|
||||
current = current.with(new StartOfInterval(chronoUnit));
|
||||
|
||||
while (!current.isAfter(end)) {
|
||||
final String id = toDateIndexPrefix(current);
|
||||
result.add(id);
|
||||
current = current.plusMonths(1);
|
||||
current = current.with(new BeginningOfNextInterval(chronoUnit));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -145,12 +152,12 @@ public class LongToDateBucket {
|
||||
|
||||
private DatePrefixAndRange toDatePrefixAndRange(final long epochMilli) {
|
||||
final OffsetDateTime date = Instant.ofEpochMilli(epochMilli).atOffset(ZoneOffset.UTC);
|
||||
final OffsetDateTime beginOfMonth = date.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
|
||||
final OffsetDateTime endOfMonth = beginOfMonth.plusMonths(1).minusNanos(1);
|
||||
final OffsetDateTime begin = date.with(new StartOfInterval(chronoUnit));
|
||||
final OffsetDateTime end = begin.with(new EndOfInterval(chronoUnit));
|
||||
|
||||
final String datePrefix = date.format(datePattern);
|
||||
final long minEpochMilli = beginOfMonth.toInstant().toEpochMilli();
|
||||
final long maxEpochMilli = endOfMonth.toInstant().toEpochMilli();
|
||||
final long minEpochMilli = begin.toInstant().toEpochMilli();
|
||||
final long maxEpochMilli = end.toInstant().toEpochMilli();
|
||||
|
||||
return new DatePrefixAndRange(datePrefix, minEpochMilli, maxEpochMilli);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.lucares.utils;
|
||||
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
|
||||
public class StartOfInterval implements TemporalAdjuster {
|
||||
|
||||
private final ChronoUnit unit;
|
||||
|
||||
public StartOfInterval(final ChronoUnit unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Temporal adjustInto(final Temporal temporal) {
|
||||
Temporal result = temporal;
|
||||
for (final ChronoUnit chronoUnit : ChronoUnit.values()) {
|
||||
if (chronoUnit.compareTo(unit) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (chronoUnit) {
|
||||
case NANOS: {
|
||||
result = result.with(ChronoField.NANO_OF_SECOND, 0);
|
||||
break;
|
||||
}
|
||||
case MICROS: {
|
||||
result = result.with(ChronoField.MICRO_OF_SECOND, 0);
|
||||
break;
|
||||
}
|
||||
case MILLIS: {
|
||||
result = result.with(ChronoField.MILLI_OF_SECOND, 0);
|
||||
break;
|
||||
}
|
||||
case SECONDS: {
|
||||
result = result.with(ChronoField.SECOND_OF_MINUTE, 0);
|
||||
break;
|
||||
}
|
||||
case MINUTES: {
|
||||
result = result.with(ChronoField.MINUTE_OF_HOUR, 0);
|
||||
break;
|
||||
}
|
||||
case HOURS: {
|
||||
result = result.with(ChronoField.HOUR_OF_DAY, 0);
|
||||
break;
|
||||
}
|
||||
case DAYS: {
|
||||
switch (unit) {
|
||||
case WEEKS: {
|
||||
result = result.with(ChronoField.DAY_OF_WEEK, 1);
|
||||
break;
|
||||
}
|
||||
case MONTHS: {
|
||||
result = result.with(ChronoField.DAY_OF_MONTH, 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + unit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MONTHS: {
|
||||
result = result.with(ChronoField.MONTH_OF_YEAR, 1);
|
||||
break;
|
||||
}
|
||||
case HALF_DAYS:
|
||||
case WEEKS:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + chronoUnit);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user