Postgres search missing date with generate_series - postgresql

I have a table in my Postgres database which has a column of dates. I want to search which of those dates are missing, for example:
date
|2016-11-09 18:00:00|
|2016-11-09 19:00:00|
|2016-11-09 20:00:00|
|2016-11-09 22:00:00|
|2016-11-09 23:00:00|
Here 2016-11-09 21:00:00 is missing. I want to make a query with generate_series that returns the date which is missing.

SELECT t
FROM generate_series(
TIMESTAMP WITH TIME ZONE '2016-11-09 18:00:00',
TIMESTAMP WITH TIME ZONE '2016-11-09 23:00:00',
INTERVAL '1 hour'
) t
EXCEPT
SELECT tscol
FROM mytable;

Related

FROM_TZ equivalent in PostgreSQL

How can I convert this to work in PostgreSQL?
TO_CHAR(CAST(FROM_TZ(CAST(columnname AS TIMESTAMP), 'GMT') AT TIME ZONE 'US/Eastern' AS DATE),'MM/DD/YY HH:MI AM') AS dt
testdb=# select TO_CHAR(CAST('2020-02-28T18:43' AS TIMESTAMP) AT TIME ZONE 'UTC' AT TIME ZONE 'US/Eastern','MM/DD/YY HH:MI AM') as dt;
dt
-------------------
02/28/20 01:43 PM
(1 row)
To make it clear what's going on, we'll start with the cast to TIMESTAMP, show that adding the first AT TIME ZONE makes it a tz-aware timestamp, and then how the 2nd does the timezone conversion.
testdb=# select CAST('2020-02-28T18:43' AS TIMESTAMP),
testdb-# CAST('2020-02-28T18:43' AS TIMESTAMP) AT TIME ZONE 'GMT',
testdb-# CAST('2020-02-28T18:43' AS TIMESTAMP) AT TIME ZONE 'GMT' AT TIME ZONE 'US/Eastern';
timestamp | timezone | timezone
---------------------+------------------------+---------------------
2020-02-28 18:43:00 | 2020-02-28 18:43:00+00 | 2020-02-28 13:43:00
(1 row)
See the timezone conversion docs for more details.

Postgres search available time slots with generate_series

I have a table in my postgres database which has a column of dates. I want to search which of those dates is missing - for example:
date
2016-11-09 18:30:00
2016-11-09 19:00:00
2016-11-09 20:15:00
2016-11-09 22:20:00
2016-11-09 23:00:00
Here, |2016-11-09 21:00:00| is missing. After sorting my generated series if my table has an entry between two slots (slot of 1 hr interval) i need to remove that.
I want to make a query with generate_series that returns me the date which is missing. Is this possible?.
sample query that i used to generate series.
SELECT t
FROM generate_series(
TIMESTAMP WITH TIME ZONE '2016-11-09 18:00:00',
TIMESTAMP WITH TIME ZONE '2016-11-09 23:00:00',
INTERVAL '1 hour'
) t
EXCEPT
SELECT tscol
FROM mytable;
But this query is not removing 2016-11-09 18:30:00,2016-11-09 20:15:00 etc. cuz i used except.
This is not a gaps-and-island problem. You just want to find the 1 hour intervals for which no record exist in the table.
EXCEPT does not work here because it does equality comparison, while you want to check if a record exists or not within a range.
A typical solution for this is to use a left join antipattern:
select dt
from generate_series(
timestamp with time zone '2016-11-09 18:00:00',
timestamp with time zone '2016-11-09 23:00:00',
interval '1 hour'
) d(dt)
left join mytable t
on t.tscol >= dt and t.tscol < dt + interval '1 hour'
where t.tscol is null
You can also use not exists:
select dt
from generate_series(
timestamp with time zone '2016-11-09 18:00:00',
timestamp with time zone '2016-11-09 23:00:00',
interval '1 hour'
) d(dt)
where not exists (
select 1
from mytable t
where t.tscol >= dt and t.tscol < dt + interval '1 hour'
)
In this demo on DB Fiddle, both queries return:
| dt |
| :--------------------- |
| 2016-11-09 21:00:00+00 |

Postgres expand time window using date_part

Have two dates - '2018-05-01' and '2018-06-01'. I would like to expand this window to the past by day difference of those dates.
SELECT * FROM data
WHERE
start_time > CAST('2018-05-01' AS timestamptz) - INTERVAL '30 DAY'
AND start_time < CAST('2018-06-01' AS timestamptz)
How can I replace INTERVAL '30 DAY' with number of days between given dates without explicitly defining number of days? I know to calculate day difference:
date_part('day',age('2018-05-01', '2018-06-01'))
But not sure how to incorporate into the substraction. Dates and days between them will change.
You can use date_trunc('mon', some_date_expression) to round down to the start of a month:
select date_trunc('mon', now() - '3 mon'::interval) as date_begin
, date_trunc('mon', now() - '1 day'::interval) as date_end
;
Result
date_begin | date_end
------------------------+------------------------
2018-03-01 00:00:00+01 | 2018-06-01 00:00:00+02
(1 row)
You can simply subtract the difference from the start date:
with t (start_date, end_date) as (
values (date '2018-05-01', date '2018-06-01')
)
select start_date - (end_date - start_date) as new_start,
end_date
from t;
returns
new_start | new_end
-----------+-----------
2018-03-31 | 2018-06-01

Postgresql: add 24 hours on a timestamp

Hi i have to add 24 hours on a timestamp converted from a string in postgres db.
here my code:
select to_timestamp(timestamp_start, 'YYYY-MM-DD HH24:MI:SS.US') + interval '24 hour' as tstamp from tablename
the query works but it adds two 0 at the end of the timestamp: "2017-05-23 17:35:13.105867+00"
why and how to solve it?
+00 meant it is timestamp with timezone and your client timezone is UTC.
If you dont want those +00 on the screen, cast it to timestamp without timezone, eg:
t=# select now();
now
-------------------------------
2017-05-23 09:04:46.105322+00
(1 row)
Time: 0.690 ms
t=# select now()::timestamp;
now
----------------------------
2017-05-23 09:04:51.849522
(1 row)
Time: 0.537 ms
So for query in original post it would be:
select (to_timestamp(timestamp_start, 'YYYY-MM-DD HH24:MI:SS.US') + interval '24 hour')::timestamp as tstamp
from tablename

Postgresql extraction between two DateTime but ingnore only seconds

Postgresql extraction between two DateTime but ignore only seconds (seconds for two datetime = 00)
06.09.2014 18:54:35 - 06.09.2014 18:54:35
DD.MM.YYYY HH24:MI - DD.MM.YYYY HH24:MI
It sounds like you're looking for the date_trunc() function.
with test_data as (
select timestamp '2015-01-01 08:00:13' as ts union all
select timestamp '2015-01-01 08:13:27' union all
select timestamp '2015-01-01 09:00:27' union all
select timestamp '2015-01-01 09:01:42'
)
select *, date_trunc('minute', ts)
from test_data
where date_trunc('minute', ts) between '2015-01-01 08:00' and '2015-01-01 09:00';
ts date_trunc
--
2015-01-01 08:00:13 2015-01-01 08:00:00
2015-01-01 08:13:27 2015-01-01 08:13:00
2015-01-01 09:00:27 2015-01-01 09:00:00
If you need this kind of query to use an index, you'll need to create an index on the expression date_trunc('minute', your_column_name).