PostgreSQL creating timestamp ranges - postgresql

In PostgreSQL 11, I am trying to get a weekend time range. From 17:00 Friday to Sunday 17:00.
So far I am able to get a working day by doing
select * from generate_series(date '2021-01-01',date '2021-12-31',interval '1' day) as t(dt) where extract (dow from dt) between 1 and 5;
However, I am have trouble creating 2 columns from start (17:00 Friday) to finish (17:00 Sunday).
Expected output should be something like this:
start stop
2022-10-07 17:00 2022-10-09 17:00
2022-10-14 17:00 2022-10-16 17:00
2022-10-21 17:00 2022-10-23 17:00

To get a series of all hours between 17:00 on Friday and 17:00 on Sunday.
SELECT
*
FROM
generate_series(timestamp '2021-01-01', timestamp '2021-12-31', interval '1' hour) AS t (dt)
WHERE
extract(dow FROM dt) IN (5, 6, 0)
AND CASE WHEN extract(dow FROM dt) = 5 THEN
extract(hour FROM dt) >= 17
WHEN extract(dow FROM dt) = 0 THEN
extract(hour FROM dt) <= 17
ELSE
extract(hour FROM dt) IS NOT NULL
END;
UPDATE
Get two timestamps that represent start and stop of each period Friday 17:00 to Sunday 17:00 over a range of dates.
SELECT
dt + '17:00'::time as start, (dt + '17:00'::time) + '2 days'::interval as stop
FROM
generate_series(date '2022-01-01', date '2022-12-31', interval '1' day) AS t (dt)
WHERE
extract(dow FROM dt) = 5
;
start | stop
-------------------------+-------------------------
01/07/2022 17:00:00 PST | 01/09/2022 17:00:00 PST
01/14/2022 17:00:00 PST | 01/16/2022 17:00:00 PST
01/21/2022 17:00:00 PST | 01/23/2022 17:00:00 PST
01/28/2022 17:00:00 PST | 01/30/2022 17:00:00 PST
02/04/2022 17:00:00 PST | 02/06/2022 17:00:00 PST
02/11/2022 17:00:00 PST | 02/13/2022 17:00:00 PST
02/18/2022 17:00:00 PST | 02/20/2022 17:00:00 PST
02/25/2022 17:00:00 PST | 02/27/2022 17:00:00 PST
03/04/2022 17:00:00 PST | 03/06/2022 17:00:00 PST
03/11/2022 17:00:00 PST | 03/13/2022 17:00:00 PDT
03/18/2022 17:00:00 PDT | 03/20/2022 17:00:00 PDT
03/25/2022 17:00:00 PDT | 03/27/2022 17:00:00 PDT
...

--timestamptz type.
SELECT
(day + interval '17:30') AS start,
(day + interval '17:30' + interval '2 days') AS
END
FROM
generate_series(date '2022-10-01', date '2022-12-31', interval '1' day) _ (day)
WHERE
EXTRACT(ISODOW FROM day) = 5;
--timestamp type.
SELECT
(day + interval '17:30')::timestamp AS start,
(day + interval '17:30' + interval '2 days')::timestamp AS
END
FROM
generate_series(date '2022-10-01', date '2022-12-31', interval '1' day) _ (day)
WHERE
EXTRACT(ISODOW FROM day) = 5;
I do checked the calendar, it works.

Related

Monthly-hourly-average calculate from Postgresql database

I have the time and the values in the data base. I need to calculate for a given month the average during each hour i.e.
YYYY-mm-dd (the day can be omitted)
2021-01-01 00:00:00 value=avg(values from 00:00:00 until 00:59:59 for every day of this month at this hour interval)
2021-01-01 01:00:00 value=avg(values from 01:00:00 until 01:59:59 idem as above)
...
2021-01-01 23:00:00 value=avg(values from 23:00:00 until 23:59:59)
2021-02-01 00:00:00 value=avg(values from 00:00:00 until 00:59:59)
2021-02-01 01:00:00 value=avg(values from 01:00:00 until 01:59:59)
...
2021-02-01 23:00:00 value=avg(values from 23:00:00 until 23:59:59)
...
You can use date_trunc('hour', datestamp) in a GROUP BY statement, something like this.
SELECT DATE_TRUNC('hour', datestamp) hour_beginning, AVG(value) average_value
FROM mytable
WHERE datestamp >= '2021-01-01'
AND datestamp < '2021-02-01'
GROUP BY DATE_TRUNC('hour', datestamp)
ORDER BY DATE_TRUNC('hour', datestamp)
To generalize, in place of DATE_TRUNC you can use any injective function.
You could use
to_char(datestamp, 'YYYY-MM-01 HH24:00:00')
to get one result row per hour for every month in your date range.
SELECT to_char(datestamp, 'YYYY-MM-01 HH24:00:00') hour,
AVG(value) average_value
FROM mytable
GROUP BY to_char(datestamp, 'YYYY-MM-01 HH24:00:00')
ORDER BY to_char(datestamp, 'YYYY-MM-01 HH24:00:00')

generate_series for every hour in hh24:mi:ss format

i have a generate series of every minute for the whole 24 hours. how about a generate _series showing every hour only in hh24:mi:ss and hh24:mi format.
result should be
00:00
01:00
02:00
:
:
23:00
This query:
SELECT to_char(generate_series(0, 23) * interval '1 hour' + '2019-01-01 00:00:00'::timestamp, 'HH24:MI')
will return:
00:00
01:00
02:00
03:00
...
23:00
Just modify the format string for what you desire.

Group by Date and sum of total duration for that day

I am using workbench/j Postgres DB for my query which is as follows -
Input
ID |utc_tune_start_time |utc_tune_end_time
----------------------------------------------
A |04-03-2019 19:00:00 |04-03-2019 20:00:00
----------------------------------------------
A |04-03-2019 23:00:00 |05-03-2019 01:00:00
-----------------------------------------------
A |05-03-2019 10:00:00 |05-03-2019 10:30:00
-----------------------------------------------
Output
ID |Day |Duration in Minutes
----------------------------------------
A |04-03-2019 |120
-----------------------------------
A |05-03-2019 |90
-----------------------------------
I require the duration elapsed from the utc_tune_start_time till the end of the day and similarly, the time elapsed for utc_tune_end_time since the start of the day.
Thanks for your clarifications. This is possible with some case statements. Basically, if utc_tune_start_time and utc_tune_end_time are on the same day, just use the difference, otherwise calculate the difference from the end or start of the day.
WITH all_activity as (
select date_trunc('day', utc_tune_start_time) as day,
case when date_trunc('day', utc_tune_start_time) =
date_trunc('day', utc_tune_end_time)
then utc_tune_end_time - utc_tune_start_time
else date_trunc('day', utc_tune_start_time) +
interval '1 day' - utc_tune_start_time
end as time_spent
from test
UNION ALL
select date_trunc('day', utc_tune_end_time),
case when date_trunc('day', utc_tune_start_time) =
date_trunc('day', utc_tune_end_time)
then null -- we already calculated this earlier
else utc_tune_end_time - date_trunc('day', utc_tune_end_time)
end
FROM test
)
select day, sum(time_spent)
FROM all_activity
GROUP BY day;
day | sum
---------------------+----------
2019-03-04 00:00:00 | 02:00:00
2019-03-05 00:00:00 | 01:30:00
(2 rows)

PostgreSQL: Compute number of hours in the day on daylight savings time

I'm trying to come up with a query that will properly count that there are 25 hours on daylight savings. My table has a column of type timestampz called hourly_timestamp. The incorrect answer I have so far looks like this:
select EXTRACT(epoch FROM tomorrow-today)/3600
from(
select date_trunc('day', timezone('America/New_York', hourly_timestamp) as today ,
date_trunc('day', timezone('America/New_York', hourly_timestamp)))
+ '1 day'::interval as tomorrow
)t;
When this query executed during daylight savings time, I still only get 24 hours back and not 25. Any ideas how to do this correctly?
The number of hours varies with the clock.
with hours as (
select (timestamp with time zone '2014-11-01 00:00:00 America/New_York' + (n || ' hour')::interval) as hourly_timestamp
from generate_series(0, 72) n
)
select hourly_timestamp
, hourly_timestamp + interval '1' day as one_day_later
, hourly_timestamp + interval '1' day - hourly_timestamp as elapsed_time
from hours;
hourly_timestamp one_day_later elapsed_time
--
[snip]
2014-11-01 22:00:00-04 2014-11-02 22:00:00-05 1 day 01:00:00
2014-11-01 23:00:00-04 2014-11-02 23:00:00-05 1 day 01:00:00
2014-11-02 00:00:00-04 2014-11-03 00:00:00-05 1 day 01:00:00
2014-11-02 01:00:00-04 2014-11-03 01:00:00-05 1 day 01:00:00
2014-11-02 01:00:00-05 2014-11-03 01:00:00-05 1 day
2014-11-02 02:00:00-05 2014-11-03 02:00:00-05 1 day
2014-11-02 03:00:00-05 2014-11-03 03:00:00-05 1 day
2014-11-02 04:00:00-05 2014-11-03 04:00:00-05 1 day
[snip]
Note that 01:00 repeats, but with a different offset. Daylight savings time ends at 02:00, the clocks fall back and repeat the hour between 01:00 and 02:00, but since daylight savings time has ended, there are now five hours between the UTC and America/New_York time zones.
This similar query displays dates, not timestamps.
with dates as (
select (timestamp with time zone '2014-11-01 00:00:00 America/New_York' + (n || ' day')::interval) as daily_timestamp
from generate_series(0, 2) n
)
select daily_timestamp::date
, (daily_timestamp + interval '1' day)::date as one_day_later
, daily_timestamp + interval '1' day - daily_timestamp as elapsed_time
from dates;
daily_timestamp one_day_later elapsed_time
--
2014-11-01 2014-11-02 1 day
2014-11-02 2014-11-03 1 day 01:00:00
2014-11-03 2014-11-04 1 day
Where did you go wrong? By calculating the elapsed time after you truncated the time information. (Dates don't have time zones associated with them.) If I take the second query and cast "daily_timestamp" to a date in the common table expression, I get 24 hours, too.
with dates as (
select (timestamp with time zone '2014-11-01 00:00:00 America/New_York' + (n || ' day')::interval)::date as daily_timestamp
from generate_series(0, 2) n
)
select daily_timestamp::date
, (daily_timestamp + interval '1' day)::date as one_day_later
, daily_timestamp + interval '1' day - daily_timestamp as elapsed_time
from dates;
daily_timestamp one_day_later elapsed_time
--
2014-11-01 2014-11-02 1 day
2014-11-02 2014-11-03 1 day
2014-11-03 2014-11-04 1 day
You first have to do the extraction to epoch and then the calculations:
WITH test AS (
SELECT '2014-10-26'::timestamptz at time zone 'America/New_York' AS today,
'2014-10-27'::timestamptz at time zone 'America/New_York' AS tomorrow
)
SELECT
extract(epoch from tomorrow) - extract(epoch from today) AS seconds, -- 90000
(extract(epoch from tomorrow) - extract(epoch from today)) / 3600 AS hours -- 25
FROM test;

linq to sql merge dates into groups

I have a simple sql query like so:
SELECT dt AS 'startDate'
, dt AS 'endDate'
FROM
WorkCalendar
WHERE
dt BETWEEN dateadd(yy, datediff(yy, 0, getdate()), 0) AND dateadd(MILLISECOND, -3, dateadd(YEAR, datediff(YEAR, 0, getdate()) + 1, 0))
AND isWorkDay = 0
This returns all dates from my table containing days free from work in current year.
This is sample output:
startDate endDate
2012-01-01 00:00:00 2012-01-01 00:00:00
2012-01-06 00:00:00 2012-01-06 00:00:00
2012-01-07 00:00:00 2012-01-07 00:00:00
2012-01-08 00:00:00 2012-01-08 00:00:00
2012-01-14 00:00:00 2012-01-14 00:00:00
2012-01-15 00:00:00 2012-01-15 00:00:00
2012-01-21 00:00:00 2012-01-21 00:00:00
2012-01-22 00:00:00 2012-01-22 00:00:00
What I would like to do is to group near dates like so:
startDate endDate
2012-01-01 00:00:00 2012-01-01 00:00:00
2012-01-06 00:00:00 2012-01-08 00:00:00
2012-01-14 00:00:00 2012-01-15 00:00:00
2012-01-21 00:00:00 2012-01-22 00:00:00
If I have 2 or more days that are one by another I would like to join them into groups.
I would like this done with linq to sql as it will be simpler for me to use in webservice, but simple sql will do the trick :)
Got something like this:
WITH d (d1)
AS (SELECT dt
FROM workcalendar
WHERE dt BETWEEN Dateadd(yy, Datediff(yy, 0, Getdate()), 0) AND
Dateadd(millisecond, -3,
Dateadd(year, Datediff(year,
0,
Getdate()) + 1
, 0))
AND isworkday = 0)
SELECT Z1.d1 AS startDate,
Z2.d1 AS endDate
FROM (SELECT Row_number()
OVER (
ORDER BY A.d1) AS 'ID',
A.d1
FROM d AS A
WHERE NOT EXISTS (SELECT *
FROM d AS C
WHERE A.d1 = Dateadd(d, 1, C.d1))) AS Z1,
(SELECT Row_number()
OVER (
ORDER BY A.d1) AS 'ID',
A.d1
FROM d AS A
WHERE NOT EXISTS (SELECT *
FROM d AS C
WHERE A.d1 = Dateadd(d, -1, C.d1))) AS Z2
WHERE Z1.id = Z2.id
But any comments and optimization are welcome :)