You're viewing version 3.3 of the OpenSearch documentation. This version is no longer maintained. For the latest version, see the current documentation. For information about OpenSearch version maintenance, see Release Schedule and Maintenance Policy.
Date range aggregations
Use the date_range aggregation to group documents into buckets defined by date boundaries. The date_range aggregation behaves like the numeric range aggregation but accepts date math in addition to ISO 8601 dates and epoch milliseconds.
Note the following details:
fromis inclusive,tois exclusive.- To create an open-ended bucket, omit
fromorto. - Date math supports rounding: for example,
now-7d/d(start of the day, 7 days ago).
Parameters
The following is a table of parameters accepted by date_range aggregations.
| Parameter | Required | Description |
|---|---|---|
field | Yes | The date field to aggregate on. |
ranges | Yes | The non-empty array of range objects. Each object must specify at least one boundary, from and/or to. |
ranges[].from | One of from or to is required. | Lower inclusive bound. |
ranges[].to | One of from or to is required. | Upper exclusive bound. |
ranges[].key | No | The label for the bucket. |
format | No | Controls the *_as_string fields in the response, for example, yyyy-MM-dd. |
time_zone | No | The IANA zone or UTC offset used when evaluating date math or rounding, for example,Europe/Dublin, +01:00. |
keyed | No | If true, returns an object with the key key instead of an array. |
missing | No | The value to substitute for documents in which the field is missing. |
Accepted values for from and to
The following values of from and to are accepted:
- ISO 8601 strings:
"2025-10-01T00:00:00Z","2025-10-01" - Date math:
"now-7d/d","now+1M/M","2025-09-01||/M" - Epoch milliseconds:
1756684800000
If components are omitted in a date string, the missing parts are filled with defaults. For example, "2025-10" is treated as the start of October 2025.
Example: Three sliding windows
The following example produces three buckets (last 7 days, previous 7 days, and older), using date math and yyyy-MM-dd output format:
GET my-index/_search
{
"size": 0,
"aggs": {
"by_range": {
"date_range": {
"field": "@timestamp",
"format": "yyyy-MM-dd",
"ranges": [
{ "from": "now-7d/d", "to": "now+1d/d", "key": "last_7d" },
{ "from": "now-14d/d", "to": "now-7d/d", "key": "prev_7d" },
{ "to": "now-14d/d", "key": "older" }
]
}
}
}
}
Example response:
"aggregations": {
"by_range": {
"buckets": [
{
"key": "older",
"to": 1758067200000,
"to_as_string": "2025-09-17",
"doc_count": 1
},
{
"key": "prev_7d",
"from": 1758067200000,
"from_as_string": "2025-09-17",
"to": 1758672000000,
"to_as_string": "2025-09-24",
"doc_count": 2
},
{
"key": "last_7d",
"from": 1758672000000,
"from_as_string": "2025-09-24",
"to": 1759363200000,
"to_as_string": "2025-10-02",
"doc_count": 2
}
]
}
}
Example: Bucket for the last 10 days with a custom string format
The following request creates a single bucket that covers the last 10 calendar days. It starts at the beginning of the day 10 days ago (now-10d/d) and ends at the beginning of tomorrow (now+1d/d, exclusive). The format only affects the *_as_string fields in the response—not document matching:
GET my-index/_search
{
"size": 0,
"aggs": {
"last_10_days": {
"date_range": {
"field": "@timestamp",
"format": "yyyy-MM",
"ranges": [ { "from": "now-10d/d", "to": "now+1d/d" } ]
}
}
}
}
Example: Keyed response and custom keys
The following request returns an object organized by your labels for easier downstream processing:
GET my-index/_search
{
"size": 0,
"aggs": {
"keyed_ranges": {
"date_range": {
"field": "@timestamp",
"keyed": true,
"ranges": [
{ "from": "now-1d/d", "to": "now+1d/d", "key": "today" },
{ "to": "now-1d/d", "key": "before_today" }
]
}
}
}
}
Example response:
"aggregations": {
"keyed_ranges": {
"buckets": {
"before_today": {
"to": 1759190400000,
"to_as_string": "2025-09-30T00:00:00.000Z",
"doc_count": 4
},
"today": {
"from": 1759190400000,
"from_as_string": "2025-09-30T00:00:00.000Z",
"to": 1759363200000,
"to_as_string": "2025-10-02T00:00:00.000Z",
"doc_count": 1
}
}
}
}
Example: Epoch milliseconds with a time zone
When the field value is provided in epoch milliseconds, you can still provide from and to parameters as numbers. For example, in the following request, time_zone affects date math and boundary evaluation:
GET my-index/_search
{
"size": 0,
"aggs": {
"local_ranges": {
"date_range": {
"field": "event_time",
"time_zone": "Europe/Dublin",
"format": "epoch_millis",
"ranges": [
{ "from": "1697328000000", "to": "1697932800000", "key": "week_sample" }
]
}
}
}
}
Example: Handling missing dates
Use missing to route documents without a value into a bucket by substituting a default:
GET my-index/_search
{
"size": 0,
"aggs": {
"dated_or_undated": {
"date_range": {
"field": "@timestamp",
"missing": "1970-01-01",
"ranges": [
{ "to": "2000-01-01", "key": "undated_or_old" },
{ "from": "2000-01-01", "key": "dated_recent" }
]
}
}
}
}