Skip to main content

Date Calc

Date

Synopsis

Performs calendar arithmetic: the current time, a time in the past, truncation to a period boundary, and the number of period boundaries between two timestamps.

Schema

- date_calc:
operation: <enum>
field: <ident>
right_field: <ident>
unit: <enum>
amount: <numeric>
description: <text>
if: <script>
ignore_failure: <boolean>
ignore_missing: <boolean>
on_failure: <processor[]>
on_success: <processor[]>
tag: <string>
target_field: <ident>

Configuration

The following fields are used to define the processor:

FieldRequiredDefaultDescription
operationY-now, ago, start_of, end_of or diff. Matched case-insensitively
fieldY*-Source timestamp. Required by start_of, end_of and diff; unused by now and ago
right_fieldY*-The second timestamp. Required by diff only
unitY*-Period unit. Required by every operation except now. Accepted values differ per operation — see below
amountY*-How far back to go. Used by ago only
descriptionN-Explanatory note
ifN-Condition to run
ignore_failureNfalseSee Handling Failures
ignore_missingNfalseIf true, quietly exit if field doesn't exist
on_failureN-See Handling Failures
on_successN-See Handling Success
tagN-Identifier
disabledNfalseWhen true, the processor is skipped and the event continues to the next one. Lets you take a processor out of the path without removing its configuration
target_fieldNfieldField to store the result. Falls back to field; if neither is set the processor fails with date_calc requires a target_field

* = Conditionally required, by operation. See the table below.

Details

OperationReadsWrites
nowNothingThe current UTC time
agoamount + unitThe current UTC time minus that duration
start_offield + unitThe first instant of the period containing that timestamp
end_offield + unitThe last instant of that period
difffield + right_field + unitA number: how many period boundaries lie between them

The four operations that produce a time write an RFC 3339 string in UTC, not a timestamp object. diff is the exception and writes an integer.

Units by operation

The accepted units are not the same for every operation, and a unit valid for one is an error on another:

OperationAccepted units
agoseconds, minutes, hours, daysplural
start_of, end_ofday, week, month, yearsingular
diffsecond, minute, hour, day, week, month, yearsingular

ago is the only operation taking plural unit names, because it expresses a duration rather than naming a period.

What diff counts

diff counts boundaries crossed, not elapsed time. With unit: day, two timestamps 30 minutes apart return 1 if they fall either side of midnight, and 0 if they do not. With unit: year, the 31st of December and the following 1st of January are 1 year apart even though an hour separates them.

This is what you want for questions like "was this on a different day?" and wrong for "how long did this take?" — use Math on epoch values for elapsed duration.

end_of returns the last representable instant inside the period — one nanosecond before the next period begins — so a range test using it is inclusive at both ends.

Examples

Truncating to a Day

Bucketing an event to the start of its day...

{
"@timestamp": "2026-09-02T14:37:12.418Z"
}
- date_calc:
operation: start_of
field: "@timestamp"
unit: day
target_field: event.day

as an RFC 3339 string in UTC:

{
"@timestamp": "2026-09-02T14:37:12.418Z",
"event": {"day": "2026-09-02T00:00:00Z"}
}

A Retention Cutoff

Writing the timestamp of 30 days ago for comparison...

{
"message": "audit record"
}
- date_calc:
operation: ago
amount: 30
unit: days
target_field: retention.cutoff

note the plural unit, which ago requires:

{
"message": "audit record",
"retention": {"cutoff": "2026-08-03T09:15:44.201Z"}
}

Counting Boundaries

Asking whether two events fall on different days...

{
"session": {
"started": "2026-09-01T23:50:00Z",
"ended": "2026-09-02T00:20:00Z"
}
}
- date_calc:
operation: diff
field: session.ended
right_field: session.started
unit: day
target_field: session.days_spanned

which is 1, despite only 30 minutes elapsing:

{
"session": {
"started": "2026-09-01T23:50:00Z",
"ended": "2026-09-02T00:20:00Z",
"days_spanned": 1
}
}

The End of a Month

end_of gives the last instant inside the period...

{
"@timestamp": "2026-09-02T14:37:12Z"
}
- date_calc:
operation: end_of
field: "@timestamp"
unit: month
target_field: period.end

so an inclusive range test works at both ends:

{
"@timestamp": "2026-09-02T14:37:12Z",
"period": {"end": "2026-09-30T23:59:59.999999999Z"}
}