Date Subtraction in Postgres and include both the dates - postgresql

I am trying to get the difference between two dates using this
'2021-07-02'::date - '2020-07-03'::date
The actual difference if you include the end date as well is 365 days but the result from this query is 364 days. Has anyone worked with something similar?
I did think about adding 1 to the result but just wanted to check if there is a more elegant way to do this.

That is documented behavior per Date/time operators.
The reason being that the date subtraction is from Midnight to Midnight so you don't get the first day included. As demo:
--The date is effectively set to Midnight
select '2020-07-03'::date::timestamp;
timestamp
---------------------
2020-07-03 00:00:00
--Which means this
select '2021-07-02'::date - '2020-07-03'::date;
?column?
----------
364
--Is the same as
select '2021-07-02 00:00:00'::timestamp - '2020-07-03 00:00:00'::timestamp;
?column?
----------
364 days
If you want to include the start date then you need to go to the previous days Midnight:
select '2020-07-03 00:00:00'::timestamp - '1 day'::interval;
?column?
---------------------
2020-07-02 00:00:00
select '2021-07-02 00:00:00'::timestamp - ('2020-07-03 00:00:00'::timestamp - '1 day'::interval);
?column?
----------
365 days
--Getting back to date.
select '2021-07-02'::date - ('2020-07-03 00:00:00'::date - '1 day'::interval)::date;
?column?
----------
365
Or an alternate solution:
select
count(*)
from
generate_series('2020-07-03'::timestamp, '2021-07-02'::timestamp, '1 day');
365
This generates a set of dates at one day intervals over the time period and the count(*) adds them up.

Related

Finding the last date of the previous quarter from current date in PostgreSQL

For example: If my current date is 2022-07-21, my query should give me 2022-06-30.
I could easily do it in IBM DB2 but struggling in postgresql.
You can truncate the current date to its quarter, then remove 1 day from that (and potentially cast back to date):
-- You really only need the last column, the other two just show the different steps in the process
SELECT DATE_TRUNC('quarter', CURRENT_DATE)
, DATE_TRUNC('quarter', CURRENT_DATE) - '1 day'::INTERVAL
, (DATE_TRUNC('quarter', CURRENT_DATE) - '1 day'::INTERVAL)::DATE
outputs
+---------------------------------+---------------------------------+----------+
|date_trunc |?column? |date |
+---------------------------------+---------------------------------+----------+
|2022-07-01 00:00:00.000000 +00:00|2022-06-30 00:00:00.000000 +00:00|2022-06-30|
+---------------------------------+---------------------------------+----------+

I want to fetch the data where I will put the date and I will get data from last three years to the entered date

imagine if I put the date 27th Jan 2022 then I should get all the records from 26th Jan 2019 till 27th Jan 2022.
There are a couple of ways to do this. One caveat is how you are defining three years:
select '2022-01-27'::date - interval '3 years';
?column?
---------------------
2019-01-27 00:00:00
or as you have it:
2019-01-26 to 2022-01-27.
Using your definition:
SELECT * FROM some_table WHERE date_fld BETWEEN '2019-01-26' and '2022-01-27';
--Using a date range
select * from some_table where date_fld <# daterange('2019-01-26', '2022-01-27', '[]');
t
--or
select * from some_table where date_fld <# daterange('2019-01-26', '2022-01-28');
t
By default date ranges are exclusive on the upper bound, that is why you need to include either the '[]' or bump the upper bound up one date(which is actually what the '[]' does).
For more information on ranges Ranges and Range operators

PostgreSQL - Getting consistent year week pairs across years

I've been banging my head towards a solution to retrieving a consistent (year, week) pair in PostrgeSQL.
After looking at the question here, and, in particular, at this comment:
Grouping by date_part('year', x), date_part('week', x) is most likely
not what the user wants and should be considered incorrect. For
example the first day of 2017 is still part of week 52 that started in
2016 and therefore the (2017, 52) pair is probably not at all what you
want as it sorts to the end of 2017. Using ´date_trunc('week', x)` on
the other hand works as one would expect.
I tried some combination of date_trunc and date_part. However, none seem to give consistent results.
For instance: consider these two dates between different years: '2017-01-01' and '2020-01-01'.
I think the correct (year, week) pair for them should be (2016, 52) and (2020, 1).
I don't know how write a SQL statement that would give these two results.
I tried:
1.
SELECT date_part('year', date_trunc('week', TIMESTAMP '2017-01-01 00:00:00')); --2016 OK
SELECT date_part('week', date_trunc('week', TIMESTAMP '2017-01-01 00:00:00')); --52 OK
SELECT date_part('year', date_trunc('week', TIMESTAMP '2020-01-01 00:00:00')); --2019 BUT I NEED 2020
SELECT date_part('week', date_trunc('week', TIMESTAMP '2020-01-01 00:00:00')); --1 OK
SELECT date_part('year', TIMESTAMP '2017-01-01 00:00:00'); --2017 BUT I NEED 2016
SELECT date_part('week', TIMESTAMP '2017-01-01 00:00:00'); --52 OK
SELECT date_part('year', TIMESTAMP '2020-01-01 00:00:00'); --2020 OK
SELECT date_part('week', TIMESTAMP '2020-01-01 00:00:00'); --1 OK
Either way, there is always something wrong, and I would possibly like something that can give the correct results. My problem is that when I am grouping by year AND week, this way sometimes I am considering more than 7 days, and I would need to avoid this.
The week unit returns a week number based on the ISO standard. To get the equivalent year, use isoyear
SELECT date_part('isoyear', TIMESTAMP '2017-01-01 00:00:00'),
date_part('week', TIMESTAMP '2017-01-01 00:00:00'),
date_part('isoyear', TIMESTAMP '2020-01-01 00:00:00'),
date_part('week', TIMESTAMP '2020-01-01 00:00:00');
returns:
date_part | date_part | date_part | date_part
----------+-----------+-----------+----------
2016 | 52 | 2020 | 1

Column of type "timestamp(6) with timezone" and "current time" difference in minutes

I have Oracle 12c DB table and one of it's column utc_timestamp is of type
UTC_TIMESTAMP TIMESTAMP(6) WITH TIME ZONE
It stores timestamp in UTC while current_timestamp and systimestamp both gives timestamp in different timezones.
How can I get time difference in MAX(utc_timestamp) and current_timestamp in minutes ignoring time difference due to different time zones.
For example:
select current_timestamp from dual;
Gives=> 23-AUG-17 04.43.16.253931000 PM AMERICA/CHICAGO
select systimestamp from dual;
Gives=> 23-AUG-17 05.43.16.253925000 PM -04:00
select max(UTC_TIMESTAMP) from table_name;
Gives=> 23-AUG-17 09.40.02.000000000 PM +00:00
For above condition when I run SQL to check time difference between in MAX(utc_timestamp) and current_timestamp I should get number 3.
I think I need something like:
select (extract(minute from current_timestamp) - extract(minute from max(UTC_TIMESTAMP)) * 1440) AS minutesBetween from table_name;
But different timezones are messing it up and I get negative number like -4317. This might be correct as current_timestamp will be higher than max(utc_timestamp) being in CST. So I tried:
select (extract(minute from CAST(current_timestamp as TIMESTAMP(6) WITH TIME ZONE)) - extract(minute from max(UTC_TIMESTAMP)) * 1440) AS minutesBetween from table_name;
This SQL runs without error but producing a big negative number like -83461. Please help me find what am I doing wrong.
You really have two problems here.
One is to convert CURRENT_TIMESTAMP to UTC. That is trivial:
select CURRENT_TIMESTAMP AT TIME ZONE 'UTC' from dual [.....]
(use the AT TIME ZONE clause https://docs.oracle.com/cd/B19306_01/server.102/b14225/ch4datetime.htm#i1007699)
The other is that the difference between two timestamps is an interval, not a number.
select current_timestamp at time zone 'UTC'
- to_timestamp_tz('24-AUG-17 04.00.00.000 AM UTC', 'dd-MON-yy hh.mi.ss.ff AM TZR')
from dual;
produces something like
+00 00:02:39.366000
which means + (positive difference) 00 days, 00 hours, 02 minutes, 39.366 seconds.
If you just want the minutes (always rounded down), you may wrap this whole expression within extract( minute from < ...... > ). Be aware though that the answer will still be 2 (minutes) even if the difference is five hours and two minutes. It is probably best to leave the result in interval data type, unless you are 100% sure (or more) that the result is always less than 1 hour.

How to get the no of Days from the difference of two dates in PostgreSQL?

select extract(day from age('2013-04-06','2013-04-04'));`
gives me the no of days ! i.e.: 2 days
but it failed when I have a differnt month:
select extract(day from age('2013-05-02','2013-04-01'));
So what I need is to get the no of days as 32 days
Subtraction seems more intuitive.
select '2013-05-02'::date - '2013-04-01'::date
Run this query to see why the result is 31 instead of 32
with dates as (
select generate_series('2013-04-01'::date, '2013-05-02'::date, '1 day')::date as end_date,
'2013-04-01'::date as start_date
)
select end_date, start_date, end_date - start_date as difference
from dates
order by end_date
The difference between today and today is zero days. Whether that matters is application-dependent. You can just add 1 if you need to.