PostgreSQL's Missing DateDiff Function
narrator.ai
narrator.ai
https://www.postgresql.org/docs/9.3/functions-datetime.html#...
Just did a search and got it from here, which shows it being used on the difference between two dates: https://stackoverflow.com/questions/24929735/how-to-calculat...
select extract(year from '2021-01-01'::timestamptz - '2020-12-31'::timestamptz);Semantically the code has to diff days (for example) but be aware that you crossed a year boundary (or any other).
> select ('2021-01-01'::timestamptz - '2020-12-31'::timestamptz) as diff;
1 dayIt gets even strange for e.g. select DATEDIFF(year, '2021-12-01', '2022-01-01') which still returns 1 even though that's a whole month. I don't see a use case for this kind of result
So this is correct for years, right?
> select floor(extract(epoch from date_trunc('year', '2021-01-01'::timestamptz) - date_trunc('year', '2020-12-31'::timestamptz)) / 3600 / 24 / 365) as diff;
1I don't think there's a good, mathematical expression for determining something as arbitrary as dates. If there was, I don't think MS would implement a special function like DateDiff for it.
> # SELECT date_part('year', '2021-01-01'::timestamptz) - date_part('year', '2020-12-31'::timestamptz) as years;
Or what am I missing?
[edit: formatting]
If you e.g. want to check if two timestamps are more than a year apart you just compare the difference to an interval of 1 year.
I'm not sure what it's used for though
select date_part('year','2021-01-01'::date) - date_part('year','2020-12-31'::date) as yeardiff;
yeardiff
----------
1
(1 row)For months and days it would be a bit bigger
extract(year from d1) - extract(year from d2)
and call it a day? select extract(month from justify_interval(timestamp '2022-01-01' - timestamp '2021-11-01'))
returns 2CREATE FUNCTION date_diff(granularity text, t1 timestamptz, t2 timestamptz) RETURNS int8 AS $$ SELECT date_part(granularity, date_trunc(granularity, t2) - date_trunc(granularity, t1))::int8 $$ LANGUAGE SQL;
I don't understand the purpose of such a calculation. If you want to check if two dates (or timestamps) fall into the same year, you compare the year part of them. If you want to check how far they are apart, you compare the difference between the two to an interval.
> The first century starts at 0001-01-01 00:00:00 AD, although they did not know it at the time
> This definition applies to all Gregorian calendar countries. There is no century number 0, you go from -1 century to 1 century. If you disagree with this, please write your complaint to: Pope, Cathedral Saint-Peter of Roma, Vatican.
teo=> select extract(minutes from interval '70 minutes');
date_part
-----------
10
(1 row)
For a sum this should return 70 (since there are obviously 70 minutes in total). Instead, it gets normalized to 1 hour and 10 minutes, and returns the minute component only.The only, and unsatisfactory, answer I've seen to get a timedelta in wholey one unit is to extract the epoch and divide by the number of seconds in that unit.
What is the use of such a function? If I saw this answer I would assume a bug somewhere (until reading the documentation).
For example, find all users who have have been active at least 10 days. This provides a simple way to do a complex operation (if a user signs up Dec 29, a naive implementation won't catch that their 10 days is in January of the next year).
A naive implementation would accidentally get tripped up by the year -- extracting the 'day' part of a timestamp, as an integer, gives you the day from the start of the year. So on one side of the year boundary you have 365 and on the other you have 1. The way to do it correctly is to multiply the day in the year by the year itself so that a '1' on a later year is a bigger number.
And of course grouping by year isn't always what you want to do :)
If you care about Days, you normally absolutely -should- use DATEDIFF('DAY' instead of DATEDIFF('YEAR' and act accordingly. The bigger thing is the semantics they provide.
Frankly, this can be a pain point at times, but IIRC PostgreSQL at least is very work-aroundable, IIRC you can get the equivalent for most common cases off the interval from an add/sub.
Honestly, Dates in SQLite are harder to deal with in the long term, since without a native data type you have to write at least the level of conversions you would in PostgreSQL. (e.x. just convert everything to/from tics at the abstraction layer.)
Edit: Also I would suggest considering instead use of getdate() <= DATEADD('day', signup_date,1) or a variant as that is probably more cross DB friendly
In my experience you almost always want to count intervals for analysis and not interval boundaries. For example when calculating someone’s age in years.
The use cases for interval boundaries seem to mostly be businesses rules based on contract dates.
Well, then you only need to compare the difference of the timestamp (or date) values with an interval of 10 days. e.g. end_time - start_time >= interval '10 days'
GROUP BY DATEDIFF('month', "transaction_date", NOW())
And get a result set that shows the relative month in one column, and the sum in another.And those values relative values won't change every month like they would if I just grouped by
EXTRACT('year-month', "transaction_date")
(or whatever the syntax is for that)Also useful for a stored procedure or view than can then be JOINed in other queries as time goes on.
MonthsAgo | Sales
==================
4 | 4,500
3 | 7,204
2 | 12,578
1 | 15,748
0 | 34,485
And maybe use a parameter so the user can go by week, or quarter, or whatever.I'm surprised there is not native function. Does anyone know why?
Redshift, famously based off Postgres, chose to implement it
But I just got done writing a reference clock driver for the Chrony NTP server/client for a GPS module which outputs GPS time. But Chrony needs samples in UTC, so I did have to care about leap seconds to make that particular GPS source usable. And I had to add a way to update the leap seconds offset when a new leap second will be scheduled. Thankfully I had no need to convert differences between wildly different timestamps to sub-second precision.
Nobody really cares except for pedantic or historian reasons. You have to use a specialized library for such non-dumbed-down dates in programming languages, and sql is not an exception. Day is exactly 86400 seconds, with an hour correction when formatting (or parsing) under system-known DST.
Almost all systems use generic dates (at a day granularity), which are isotropic at all times, by ignoring these historical jumps. The only real/modern things are DST and leap seconds, the latter also often ignored for programmer’s sanity.
Python: https://stackoverflow.com/questions/39686553/what-does-pytho...
Js (also mentions most others): https://stackoverflow.com/questions/53019726/where-are-the-l...
C#: https://stackoverflow.com/questions/8760674/are-nets-datetim...
Leap seconds only have sense in let’s name it “real-event-time” systems, where common generic dates are unusable anyway. It’s a complete nonsense in regular programming and in sql. Regular systems are okay with being off with each other, and leap seconds are smeared across much bigger differences by ntp et al.
Don’t overthink software dates.