diff --git a/server/src/main/java/org/elasticsearch/common/Rounding.java b/server/src/main/java/org/elasticsearch/common/Rounding.java index 3558b16aac1c8..07f47a997d772 100644 --- a/server/src/main/java/org/elasticsearch/common/Rounding.java +++ b/server/src/main/java/org/elasticsearch/common/Rounding.java @@ -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 { @@ -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); @@ -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 @@ -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);