Describe the bug
Rounding.isUTC() only recognizes ZoneOffset.UTC (display name "Z") as UTC. When a timezone is passed as ZoneId.of("UTC"), ZoneId.of("Etc/UTC"), or ZoneOffset.of("+00:00"), the method returns false even though these are all semantically UTC.
This matters because isUTC() gates the filter rewrite optimization for date histogram aggregations (DateHistogramAggregatorBridge and CompositeAggregator). When isUTC() incorrectly returns false, the optimized path is skipped, resulting in slower queries.
Root Cause: ZoneId.of("UTC") and ZoneId.of("Etc/UTC") are ZoneRegion instances whose getDisplayName(TextStyle.FULL, Locale.ENGLISH) returns "Coordinated Universal Time", not "Z".
| Input |
getDisplayName() (before) |
normalized().getDisplayName() (after) |
ZoneId.of("UTC") |
Coordinated Universal Time |
Z |
ZoneId.of("Etc/UTC") |
Coordinated Universal Time |
Z |
ZoneOffset.of("+00:00") |
Z |
Z |
ZoneOffset.UTC ("Z") |
Z |
Z |
Fix: Call .normalized() before .getDisplayName(). ZoneId.normalized() converts all UTC-equivalent zones to ZoneOffset.UTC:
// Before
return "Z".equals(timeZone.getDisplayName(TextStyle.FULL, Locale.ENGLISH));
// After
return "Z".equals(timeZone.normalized().getDisplayName(TextStyle.FULL, Locale.ENGLISH));
Both TimeUnitRounding.isUTC() and TimeIntervalRounding.isUTC() need this fix.
Related component
Search:Aggregations
To Reproduce
- Send a date_histogram aggregation with
"time_zone": "UTC" (or "+00:00" or "Etc/UTC")
- The filter rewrite optimization is skipped because
Rounding.isUTC() returns false
- The same query with no timezone (defaults to
ZoneOffset.UTC / "Z") correctly uses the optimization
This is the common case when using OpenSearch Dashboards, which sends timezone values like:
"UTC" — from explicit dateFormat:tz setting or moment.tz.guess()
"Etc/UTC" — from Timeline plugin configuration
"+00:00" — from moment().format('Z') fallback
Expected behavior
Rounding.isUTC() should return true for all UTC-equivalent timezone representations ("UTC", "Etc/UTC", "+00:00", "Z"), so that the date histogram filter rewrite optimization is applied correctly.
Additional Details
Version: main (3.0)
Describe the bug
Rounding.isUTC()only recognizesZoneOffset.UTC(display name"Z") as UTC. When a timezone is passed asZoneId.of("UTC"),ZoneId.of("Etc/UTC"), orZoneOffset.of("+00:00"), the method returnsfalseeven though these are all semantically UTC.This matters because
isUTC()gates the filter rewrite optimization for date histogram aggregations (DateHistogramAggregatorBridgeandCompositeAggregator). WhenisUTC()incorrectly returnsfalse, the optimized path is skipped, resulting in slower queries.Root Cause:
ZoneId.of("UTC")andZoneId.of("Etc/UTC")areZoneRegioninstances whosegetDisplayName(TextStyle.FULL, Locale.ENGLISH)returns"Coordinated Universal Time", not"Z".getDisplayName()(before)normalized().getDisplayName()(after)ZoneId.of("UTC")Coordinated Universal TimeZZoneId.of("Etc/UTC")Coordinated Universal TimeZZoneOffset.of("+00:00")ZZZoneOffset.UTC("Z")ZZFix: Call
.normalized()before.getDisplayName().ZoneId.normalized()converts all UTC-equivalent zones toZoneOffset.UTC:Both
TimeUnitRounding.isUTC()andTimeIntervalRounding.isUTC()need this fix.Related component
Search:Aggregations
To Reproduce
"time_zone": "UTC"(or"+00:00"or"Etc/UTC")Rounding.isUTC()returnsfalseZoneOffset.UTC/"Z") correctly uses the optimizationThis is the common case when using OpenSearch Dashboards, which sends timezone values like:
"UTC"— from explicitdateFormat:tzsetting ormoment.tz.guess()"Etc/UTC"— from Timeline plugin configuration"+00:00"— frommoment().format('Z')fallbackExpected behavior
Rounding.isUTC()should returntruefor all UTC-equivalent timezone representations ("UTC","Etc/UTC","+00:00","Z"), so that the date histogram filter rewrite optimization is applied correctly.Additional Details
Version: main (3.0)