Add date_diff pipeline function for date difference calculations#26143
Open
danotorrey wants to merge 7 commits into
Open
Add date_diff pipeline function for date difference calculations#26143danotorrey wants to merge 7 commits into
danotorrey wants to merge 7 commits into
Conversation
Computes the difference between two DateTime values and returns it as a map keyed by unit (millis, seconds, minutes, hours, days, weeks). By default the result is signed (right - left); pass absolute=true to get absolute values. Fixes #26142
- direction: "ahead" | "behind" | "equal" describing right relative to left; derived from signed millis so it is preserved when absolute=true. - friendly: human-readable rendering of the (signed) interval, with zero components omitted and sub-second deltas in milliseconds (e.g. "2 days", "-2 days", "1 week 1 day 3 hours 15 minutes", "250 ms").
- Parameter/function descriptions refer to "start" and "end" instead of "left"/"right" since left > right is explicitly supported. - friendly now emits a ms component when the total interval is below one minute, so e.g. 1500ms renders as "1 second 500 ms" instead of dropping the remainder. ms is still suppressed for longer intervals to avoid millisecond noise on multi-day deltas; raw millis carries the exact value either way. - friendly builds the string with leading separators instead of always trimming a trailing space at the end.
Each numeric unit in the result map (seconds, minutes, hours, days, weeks) now uses half-away-from-zero rounding instead of integer truncation. This matches what users intuitively expect when reading "how many minutes ago" - 38m59s reports as 39 minutes, not 38. Callers who need exact values can drop to a smaller unit; the raw millis field remains unrounded. friendly stays decomposition-based (truncate + modulo) so its components still sum to the total interval. Also trims the unit tests down to one assertion per behavior.
kingzacko1
reviewed
Jun 6, 2026
| private static long roundDiv(long value, long divisor) { | ||
| final long half = divisor / 2; | ||
| return value >= 0 ? (value + half) / divisor : (value - half) / divisor; | ||
| } |
Contributor
There was a problem hiding this comment.
Should we really do half-away-from-zero rounding? Seems like if we want to keep whole numbers then truncation may be the better option. If you were to add a days field in your pipeline processing and then query for days < 7 you're really getting days < 6.5. @danotorrey
Contributor
|
@danotorrey Everything with the exception of the rounding logic LGTM. Not sure if its just what Claude defaulted to, but a simple truncated rounding makes more sense to me than half-from-zero unless we have some other reason to go with it. |
kingzacko1
approved these changes
Jun 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a new
date_diffpipeline function. Fixes #26142.Function shape
leftrightabsolutetrue, return absolute numeric values. Defaultfalse.leftmay be before or afterright— the result is signed by default. Inputs must beDateTime— feed strings throughparse_date(orflex_parse_date) first.Result shape
A map keyed by unit (every unit expresses the full interval rounded to the nearest whole unit — drop to a smaller unit if you need more precision), plus
directionandfriendly:E.g. a 2-day interval (end after start) yields:
end − start). Passabsolute: truefor absolute values.directiondescribes the end relative to the start:"ahead"(later),"behind"(earlier),"equal"(same instant). Always derived from the signed result, so it is preserved even whenabsolute: true.friendlyis a human-readable rendering. Zero-valued components are omitted. For intervals shorter than one minute, sub-second remainder is included (e.g."250 ms","1 second 500 ms"); for longer intervals the millisecond remainder is suppressed to avoid noise — the rawmillisfield always has the exact value. Negative intervals are prefixed with-. Further examples:"30 seconds","2 days","-2 days","1 week 1 day 3 hours 15 minutes".Months and years are deliberately omitted because they aren't constant-length; a calendar-aware helper can land separately if there's demand.
Why
There was no first-class way to compute date differences in a pipeline rule. The only path was
dateA - dateB(returns a JodaDuration) followed byto_string(...)and a regex against the ISO‑8601 form (PT123SvsPT123.456S) to recover a numeric value — brittle and easy to get wrong.Examples
All examples below are exercised end-to-end by
FunctionsSnippetsTest#dateDiffPrExamplesso they're guaranteed to parse and execute.1. VPN session duration from a RADIUS accounting record
Input message has
acct_session_startlike"2025-05-27T13:42:10.000+0000"and the message timestamp is when accounting-stop hit the pipeline.2. Account age at login (flag brand-new accounts)
Auth event has
user_createdlike"03/15/2024".Note: map field access (
age.days) returnsObject, so it needsto_long(...)before numeric comparison.3. HTTP request latency from access log fields
Access log has
request_received_atandresponse_sent_atas ISO timestamps.4. Stale record detection (uses
friendlyanddirection)Test plan
./mvnw -pl :graylog2-server -Dtest='FunctionsSnippetsTest#dateDiff+dateDiffPrExamples' test -Dskip.web.build=true -Dmaven.javadoc.skip=truedate_diffagainst a real message, confirm each unit field matches expectations.Assisted with Claude Code