Skip to content

Commit

Permalink
Implement rounding optimization for fixed offset timezones (#46809)
Browse files Browse the repository at this point in the history
Fixes #45702 with date_histogram aggregation when using fixed_interval.
Optimization has been implemented for both fixed and calendar intervals
  • Loading branch information
csoulios authored Sep 18, 2019
1 parent e59be03 commit 0076083
Showing 1 changed file with 27 additions and 9 deletions.
36 changes: 27 additions & 9 deletions server/src/main/java/org/elasticsearch/common/Rounding.java
Original file line number Diff line number Diff line change
Expand Up @@ -219,17 +219,21 @@ public Rounding build() {
static class TimeUnitRounding extends Rounding {

static final byte ID = 1;
/** Since, there is no offset of -1 ms, it is safe to use -1 for non-fixed timezones */
static final long TZ_OFFSET_NON_FIXED = -1;

private final DateTimeUnit unit;
private final ZoneId timeZone;
private final boolean unitRoundsToMidnight;
private final boolean isUtcTimeZone;
/** For fixed offset timezones, this is the offset in milliseconds, otherwise TZ_OFFSET_NON_FIXED */
private final long fixedOffsetMillis;

TimeUnitRounding(DateTimeUnit unit, ZoneId timeZone) {
this.unit = unit;
this.timeZone = timeZone;
this.unitRoundsToMidnight = this.unit.field.getBaseUnit().getDuration().toMillis() > 3600000L;
this.isUtcTimeZone = timeZone.normalized().equals(ZoneOffset.UTC);
this.fixedOffsetMillis = timeZone.getRules().isFixedOffset() == true ?
timeZone.getRules().getOffset(Instant.EPOCH).getTotalSeconds() * 1000 : TZ_OFFSET_NON_FIXED;
}

TimeUnitRounding(StreamInput in) throws IOException {
Expand Down Expand Up @@ -277,11 +281,12 @@ private LocalDateTime truncateLocalDateTime(LocalDateTime localDateTime) {

@Override
public long round(long utcMillis) {
// this works as long as the offset doesn't change. It is worth getting this case out of the way first, as
// the calculations for fixing things near to offset changes are a little expensive and are unnecessary in the common case
// of working in UTC.
if (isUtcTimeZone) {
return unit.roundFloor(utcMillis);
// This works as long as the tz offset doesn't change. It is worth getting this case out of the way first,
// as the calculations for fixing things near to offset changes are a little expensive and unnecessary
// in the common case of working with fixed offset timezones (such as UTC).
if (fixedOffsetMillis != TZ_OFFSET_NON_FIXED) {
long localMillis = utcMillis + fixedOffsetMillis;
return unit.roundFloor(localMillis) - fixedOffsetMillis;
}

Instant instant = Instant.ofEpochMilli(utcMillis);
Expand Down Expand Up @@ -437,20 +442,25 @@ public String toString() {
}

static final byte ID = 2;
/** Since, there is no offset of -1 ms, it is safe to use -1 for non-fixed timezones */
private static final long TZ_OFFSET_NON_FIXED = -1;

private final long interval;
private final ZoneId timeZone;
/** For fixed offset timezones, this is the offset in milliseconds, otherwise TZ_OFFSET_NON_FIXED */
private final long fixedOffsetMillis;

TimeIntervalRounding(long interval, ZoneId timeZone) {
if (interval < 1)
throw new IllegalArgumentException("Zero or negative time interval not supported");
this.interval = interval;
this.timeZone = timeZone;
this.fixedOffsetMillis = timeZone.getRules().isFixedOffset() == true ?
timeZone.getRules().getOffset(Instant.EPOCH).getTotalSeconds() * 1000 : TZ_OFFSET_NON_FIXED;
}

TimeIntervalRounding(StreamInput in) throws IOException {
interval = in.readVLong();
timeZone = DateUtils.of(in.readString());
this(in.readVLong(), DateUtils.of(in.readString()));
}

@Override
Expand All @@ -460,6 +470,14 @@ public byte id() {

@Override
public long round(final long utcMillis) {
// This works as long as the tz offset doesn't change. It is worth getting this case out of the way first,
// as the calculations for fixing things near to offset changes are a little expensive and unnecessary
// in the common case of working with fixed offset timezones (such as UTC).
if (fixedOffsetMillis != TZ_OFFSET_NON_FIXED) {
long localMillis = utcMillis + fixedOffsetMillis;
return (roundKey(localMillis, interval) * interval) - fixedOffsetMillis;
}

final Instant utcInstant = Instant.ofEpochMilli(utcMillis);
final LocalDateTime rawLocalDateTime = LocalDateTime.ofInstant(utcInstant, timeZone);

Expand Down

0 comments on commit 0076083

Please sign in to comment.