getting dynamic timezone offset based on month - postgresql

I've a query
"TO_CHAR((TIMEZONE('#{ tz_offset }', created_at) + (#{ months.to_s } * INTERVAL '1 month')))"
The final query becomes:
"SELECT sum(price) as sum_total, min(TO_CHAR((TIMEZONE('-05:00', created_at) + (0 * INTERVAL '1 month')), 'YYYY-MM')) as formatted_date, min(TO_CHAR((TIMEZONE('-05:00', created_at) + (0 * INTERVAL '1 month')), 'Mon/YY')) as key FROM items WHERE created_at BETWEEN '2018-09-01 04:00:00' AND '2020-02-01 03:59:59' GROUP BY TO_CHAR((TIMEZONE('-05:00', created_at) + (0 * INTERVAL '1 month')), 'YYYY-MM') ORDER BY TO_CHAR((TIMEZONE('-05:00', created_at) + (0 * INTERVAL '1 month')), 'YYYY-MM')"
As you can see the tz_offset becomes -05:00
However, the problem I'm running into is that the tz_offset is static, and it can change based on the month. Such as, CST time is -05:00 in July but -06:00 in August. I would like to make sure the amounts fall into correct months based on daylight savings. Is it possible for postgres to calculate this tz_offset based on timezone?

I was able to get it to work using
created_at AT TIME ZONE <tz_name>

Rather than the offset, use the name of the time zone, for example America/Chicago. That includes the daylight savings time setting.

Related

How to get count for every 1 hour interval

select
count("Status") as Total_Count
from "dbo"
where "Status" = 'Pass'
and "StartDateTime" BETWEEN '2020-11-01 15:00:00' AND '2020-11-01 16:00:00'
group by "Status"
How to get data for every 1 hour interval as in the image above? As currently i changing the time interval manualy. I want get the counts from 12am to 12am next day with 1 hour interval.
demo: db<>fiddle
When you truncate the start time with date_trunc() at the hour part, all times will be normalized to full hours. This can be used as the GROUP BY criterion.
SELECT
COUNT(*)
FROM
t
GROUP BY date_trunc('hour', starttime)
To format the time column as you expect, you can use the to_char() function:
SELECT
to_char(date_trunc('hour', starttime), 'HH12:MI:SS AM') || ' - ' || to_char(date_trunc('hour', starttime) + interval '1 hour', 'HH12:MI:SS AM'),
COUNT(*)
FROM
t
GROUP BY date_trunc('hour', starttime)

Postgresql difference in time based on interval

I have a table, time_slots, which has a column start_time. My users table has a column, hours_before (int), which is the number of hours before the start_time to receive a notification.
I'm running a job every 5 minutes that checks the time slots that have a start_time which is now + hours_before. All my datetimes are store in UTC. I can't figure out the correct where clause to get the appropriate time slots.
Right now I'm passing in the current time in UTC as a string rather than doing something like current_timestamp at time zone 'UTC'.
(extract(epoch from starts_at) - extract(epoch from (date '2020-11-09 06:20:00' + (users.hours_before * INTERVAL '1 hour')))) = 0
Here is a test query to see what the values are inside of a select. In this example, (6 * INTERVAL '1 hour') would be where users.hours_before would be. I'm using a static 6 while tinkering.
select
(extract(epoch from "starts_at") - extract(epoch from date '2020-11-09 06:20:00')) / 3600 as hours,
(extract(epoch from "starts_at") - extract(epoch from (date '2020-11-09 06:20:00' + (6 * INTERVAL '1 hour')))) as other_interval,
*
from
"time_slots"
where
"starts_at" > '2020-11-09 06:20:00'
order by hours asc;
For example, the following query has the same hours and other_interval values as the above query, despite the date being 2020-11-09 10:00:00 instead of 2020-11-09 06:00:00. Shouldn't those columns be 4 hours different since they change by 4 hours?
select
(extract(epoch from "starts_at") - extract(epoch from date '2020-11-09 10:00:00')) / 3600 as hours,
(extract(epoch from "starts_at") - extract(epoch from (date '2020-11-09 10:00:00' + (6 * INTERVAL '1 hour')))) as other_interval,
*
from
"time_slots"
where
"starts_at" >= '2020-11-09 06:00:00'
order by hours asc;

How to calculate how many intervals at given daterange? simpler version

I can write:
select count(*) from generate_series(
'2019-03-01'::date, '2019-05-01'::date,
interval '3 day 1 hour'
)
-- exclude upper boundary
where generate_series <> date '2019-05-01'::date;
Is there a way to do it simpler? like:
daterange( '2019-03-01', '2019-05-01' ) / interval '3 day 1 hour'
You can use
EXTRACT(epoch FROM some_interval)
to get an interval's duration in seconds.
You could use that as follows:
SELECT EXTRACT(epoch FROM '2019-05-01'::timestamptz - '2019-03-01'::timestamptz)
/ EXTRACT(epoch FROM interval '3 day 1 hour');
Note that this will only give correct answers for intervals that are measured in days or lesser units; for months and more you have to go with your original solution.

PostgreSQL - Difference between Current Date and a Date from Database

I'm trying to make a SELECT query which will compare current time with time at database. For example in database there is a record '2018-02-07 12:00:00' and I wanna compare it to current time. If current time is '2018-02-07 11:00:00', record '2018-02-07 12:00:00' should be visible in results. It should compare two dates and shows only those who are 1h before or after current time.'
Tried something like this:
SELECT * FROM events WHERE age(current_date, event_date) < '1 hour';
or
SELECT * FROM events WHERE event_date > (now() - INTERVAL '1 hour');
those that are 1h before or after current time
Wouldn't the logic look like this?
SELECT e.*
FROM events e
WHERE e.event_date > now() - INTERVAL '1 hour' AND
e.event_date < now() + INTERVAL '1 hour'

How to get last 24 hrs data in postgreSQL

I want to get last 24 hrs data. i wrote a query in postgreSQL as follows. But I couldn't get the answer as i expected.
SELECT startdate::timestamp AS startdate,
(DATE_PART('hour',startdate::timestamp)::integer) as hrs,count(guorderid)
FROM ord_entitlement
WHERE DATE_PART('Day',CURRENT_DATE::timestamp - startdate::timestamp) < 1
AND DATE_PART('hour',startdate::timestamp) <= 24
GROUP BY hrs,startdate
ORDER BY startdate
Instead of checking date parts, do the time math to get an interval. Use NOW() to get a timestamptz.
SELECT startdate::timestamp AS startdate,
(DATE_PART('hour',startdate::timestamp)::integer) as hrs,
count(guorderid)
FROM ord_entitlement
WHERE NOW() > startdate::timestamptz
AND NOW() - startdate::timestamptz <= interval '24 hours'
GROUP BY hrs,startdate
ORDER BY startdate
This ensures you will get the last 24 hours no matter what your time zone or daylight savings says. NOW() > startdate::timestamptz ensures you don't accidentally pick up things from the future.
If you use CURRENT_DATE you will not get the time instead use now() function. Try the following,
SELECT startdate::timestamp AS startdate,
(DATE_PART('hour',startdate::timestamp)::integer) as hrs,count(guorderid)
FROM ord_entitlement
WHERE DATE_PART('Day',now() - startdate::timestamptz) < 1
GROUP BY hrs,startdate
ORDER BY startdate
date_part() works like extract(), i.e. they will extract a subfield from the source:
-- they will both yield 9 as result
select date_part('day', date '2015-01-09') "day part of 2015-01-09",
date_part('day', date '2015-02-09') "day part of 2015-02-09";
Extracting day(s) therefore is not suited to select the last 24 hours. Similarly extracting hour(s) will (almost) always yield less than or equal to 24.
Extraction day(s) from interval (that's the result of substracting 2 timestamps) is a little different. The result might depend on, whether the interval is justified, or not:
-- they will both yield 1 as result
select date_part('day', interval '1 day') "day part of 1 day",
date_part('day', interval '1 month 1 day') "day part of 1 month 1 day";
-- they will yield 1, 32 and 397 respectively
select date_part('day', timestamp '2015-02-09' - timestamp '2015-02-08') "interval 1",
date_part('day', timestamp '2015-02-09' - timestamp '2015-01-08') "interval 2",
date_part('day', timestamp '2015-02-09' - timestamp '2014-01-08') "interval 3";
Depending on the fact, that the timestamp subtraction is not giving justified intervals is not the best option, I think. You could use simpler conditions to achieve your goal:
-- if startdate is a timestamp:
where current_timestamp - interval '1 day' <= startdate
-- if startdate is a date:
where current_date - 1 <= startdate
If you want to disallow future dates too (as your question's title suggests), you could use a single between condition:
-- if startdate is a timestamp:
where startdate between current_timestamp - interval '1 day' and current_timestamp
-- if startdate is a date:
where startdate between current_date - 1 and current_date