Repository navigation
feat(pg-functions): add age(timestamp, timestamp) and age(timestamp) - #437
Merged
Merged
Conversation
Implement PostgreSQL's age() in the datetime category, ported from timestamp_age/timestamptz_age in PostgreSQL's timestamp.c: the two timestamps are subtracted field by field, negative fields borrow from the next larger one, and a day borrows the length of the earlier timestamp's month. - Accept timestamps of any unit, with or without a time zone, dates and string literals; always return Interval(MonthDayNano). - Rewrite age(x) to age(current_date, x) during planning, as PostgreSQL defines it. - Take the calendar fields of a zoned timestamp in its own zone, and decide which argument is earlier by the instants, as PostgreSQL does. - Keep Arrow's nanosecond precision. - Register the UDF under the `datetime` feature, which now enables the workspace's chrono dependency, mark it implemented in functions.md, and add age cases to datetime.slt.
PostgreSQL takes the calendar fields of both age() arguments in the session time zone. age() took each argument in its own zone, so a timestamp in New York against one in Kolkata, or a zoned timestamp against a plain one, produced mixed-sign results such as "-1 mons 30 days 14:00:00" where PostgreSQL returns "00:30:00". When an argument carries a time zone, coerce every argument to the first such zone: a timestamp in another zone keeps its instant, and a plain timestamp, date or string literal is read as local time there, as PostgreSQL reads them in the session zone.
The time-zone fix takes both arguments in the first argument's zone; the functions.md note still said each timestamp uses its own zone.
sunng87
approved these changes
Oct 16, 2026
Member
|
Awesome, thank you @Ishaan400 ! |
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.
Closes #436
Adds PostgreSQL's
age()to thedatetimecategory, ported fromtimestamp_age/timestamptz_agein PostgreSQL'stimestamp.c. Todayage(...)fails withInvalid function 'age'.age('2024-03-01', '2024-01-31')is1 mon 1 dayand the reverse is-1 mons -1 days.age('2024-03-01', '2024-01-31')works, as in PostgreSQL). Always returnsInterval(MonthDayNano); other argument types are a planning error.age(x)is rewritten toage(current_date, x)during planning, which is how PostgreSQL defines it.TimeZone. Here, when an argument carries an Arrow time zone, both arguments are cast to the first such zone. A timestamp in another zone keeps its instant; a plain timestamp, date or literal is read as local time in that zone. Which argument is earlier is decided by the instants, as PostgreSQL does. This matches PostgreSQL when the session is in that zone: for example1 dayacross a DST change in America/New_York, where UTC gives23:00:00.justify_*functions.chrono(already a workspace dependency) becomes an optional dependency enabled by thedatetimefeature.functions.mdmarksageimplemented under Date/Time; thexidform under System Information is unchanged.Tests
datetime/age.rs, with expected values recorded from PostgreSQL 16.15. They cover month-end and leap-day borrows, negative results, sub-second borrows, DST, mixed time zones, dates and mixed units, NULL, empty input, out-of-range timestamps, and the SQL forms (two arguments, string literals, one argument, rejected types).agesection indatetime.slt, with each PostgreSQL result noted.cargo testandcargo test --features datafusion-pg-functions/full: pass.cargo clippy --features datafusion-pg-functions/full -- -D warnings: clean.cargo fmt -- --check: clean.datafusion-postgres-cliand PostgreSQL side by side and sending the same queries throughpsql:.250000vs PostgreSQL's.25), whichjustify_hoursand plain interval literals also show.Limitations
TimeZoneis not UTC,timestamptzvalues reach DataFusion as UTC timestamps, soage()computes in UTC, asextract,date_truncand::datedo today. For example, in an America/New_York sessionextract(day from timestamptz '2024-03-01 02:00:00+00')gives1here and29in PostgreSQL.age(xid)is not provided. Arrow timestamps have noinfinity, so PostgreSQL's infinite cases do not arise.