In Postgres, how do you extract the month (according to specific timezone) from a given TIMESTAMP WITH TIME ZONE column? - sql

I have a column called login_timestamp, which is of type TIMESTAMP WITH TIME ZONE.
To retrieve the month for this timestamp, I would do: EXTRACT(MONTH FROM login_timestamp).
However, I would like to retrieve the month for a specific time zone (in my case, Pakistan), but can't figure out how to do that.

Documentation for this is under Date/Time Functions and Operators. Search that page for "at time zone".
select extract(month from login_timestamp at time zone 'Asia/Karachi');
You can change the time zone for a single session or for a single transaction with set session... or set local.... For example, this changes the time zone for the current session.
set session time zone 'Asia/Karachi';

Use the AT TIME ZONE construct:
SELECT EXTRACT(MONTH FROM login_timestamp AT TIME ZONE '-5');
-5 is the constant offset for Pakistan.
Details:
Ignoring timezones altogether in Rails and PostgreSQL

Try applying AT TIME ZONE. Demo
select extract(month from cast ('2017-07-01 01:00+03' as TIMESTAMP WITH TIME ZONE) AT TIME ZONE '+08') as monthNo
returns
monthno
1 6

Related

Oracle SQL : how to specify Time Zone Region

to_date('30/03/2022', 'DD/MM/YYYY')
Underlined, as hours are not specified, that means that hour is '00:00'
I would like to specify that this is for Europe/Paris time zone region.
Can you help me set-up this ?
Thanks
A DATE data type has the components: year, month, day, hour, minute and second. It ALWAYS has those components and NEVER stores anything else (such as a time zone); so it is impossible to store a time zone in a DATE data type.
A TIMESTAMP data type has the components: year, month, day, hour, minute and second and, optionally, can store fractional seconds.
A TIMESTAMP WITH TIME ZONE data type has the components: year, month, day, hour, minute, second and time zone and, optionally, can store fractional seconds information.
Therefore, if you want to store a time zone then you should use TIMESTAMP WITH TIME ZONE and not DATE.
Your code would then be:
TO_TIMESTAMP_TZ('30/03/2022 Europe/Paris', 'DD/MM/YYYY TZR')
or using a timestamp literal:
TIMESTAMP '2022-03-30 00:00:00 Europe/Paris'
or, if you want to pass in your date in that format and add the time zone in a two-step process:
FROM_TZ(TO_TIMESTAMP('30/03/2022', 'DD/MM/YYYY'), 'Europe/Paris')
db<>fiddle here

How to get all data post midnight of different timezone?

I have a PostgreSQL table named testing with a column named creation_time as timestamp with time zone. The database timezone is UTC
Now I want to get all rows whose time is greater than 00:00 of the current day as per the timezone "America/New_York".
I know how to get all rows after local midnight:
SELECT * FROM testing
WHERE ( creation_time >= now()::date)
ORDER BY id DESC
But how to use this query with a different timezone?
Assuming "the current day" is also defined by NY time, not by the current timezone setting.
SELECT *
FROM testing
WHERE creation_time >= date_trunc('day', now() AT TIME ZONE 'America/New_York') AT TIME ZONE 'America/New_York'
ORDER BY id DESC;
Yes, AT TIME ZONE 'America/New_York' twice. No typo there.
now() AT TIME ZONE 'America/New_York') gets local NY time. date_trunc gets 00:00 of that day. The 2nd AT TIME ZONE 'America/New_York' converts the local time back to timestamptz, which we finally compare to.
If you want NY 00:00 of your local date, it's simpler:
WHERE creation_time >= CURRENT_DATE::timestamp AT TIME ZONE 'America/New_York'
Same time, but can be a different day!
CURRENT_DATE is the local date (date according to the time zone setting of the current session). Effectively the same as now()::date.
Further reading:
Ignoring time zones altogether in Rails and PostgreSQL

Can't extract timezone_hour from postgres timestamp

ERROR: timestamp units "timezone" not supported
That's the error I get for all timezone fields.
Here's a minimal query you can run:
select extract(timezone_hour from now()::timestamptz at time zone 'US/Eastern');
What I want is the utc offset in hours. So this should return -4.
The requirement is that I use a dynamic time zone in the query. So I have a table of "facilities" with time zone strings, and I need to get the current time for each one.
So my end query should look something like this:
SELECT
EXTRACT(timezone_hour from now() with time zone timezone) # timezone is the name of the field
FROM facilities;
I thought I had it for a second with this, but this is giving me my current offset, not the offset of the tz I'm passing:
select
extract(timezone_hour from (select now()::timestamp at time zone 'US/Eastern'))
date_part
-----------
-7
I ended up getting this to work with creating two timestamps, one at utc and one at the desired time zone, but I'll leave this open just in case there's a better solution than my current one:
select
extract(hour from (
select (
select now() at time zone 'US/Eastern') - (select now() at time zone 'UTC')));
date_part
-----------
-4

Group by time with timezone conversion in Postgresql

I am working with time data that is currently stores in UTC but I want it to be in PST, which is 8 hours behind. I have a pretty lengthy and involved query, but the only thing I am interested in is the time right now so I have included those parts. I want to convert the times to PST and then group by the date for the last week of data. The query has the following structure:
select
date_trunc('day', time1) AT TIME ZONE 'US/Pacific'
...
where
time1 AT TIME ZONE 'US/Pacific' > now() AT TIME ZONE current_setting('TimeZone') - INTERVAL '168 HOURS'
...
group by date_trunc('day', time1)
This results in the following time groupings. From my understanding, it groups from the 0:00 UTC, which is 16:00 in PST. However, I want the groupby to start at 0:00 PST. How do I do this? Right now, the counts in each group are misleading for each day because they go from 4 pm to 4 pm instead of 12 am to 12 am. For example, Sundays have uncharacteristically high counts because Sunday includes part of Monday's data in the groupby. I would appreciate any input to fix this issue. Thank you.
The answer depends on whether it is a timestamp with time zone or one without:
If it's a timestamp with time zone, you can convert to PST with select time1 AT TIME ZONE 'US/Pacific' and get the date with select date_trunc('day', time1 AT TIME ZONE 'US/Pacific')
If it's a timestamp without time zone stored in UTC that you want to convert, you first have to tell PostgreSQL to interpret it as UTC, then convert it, like so: select (time1 AT TIME ZONE 'Z') AT TIME ZONE 'US/Pacific' and of course you can get the date with select date_trunc('day', (time1 AT TIME ZONE 'Z') AT TIME ZONE 'US/Pacific')
In either case you have to convert time zones before truncating to the day level or you may end up with inaccurate results.

pgSql not pulling all data for a date

The following query against Postgres database some how leaves few rows for the date: 2017-10-01. I have added time zone also. Is there a way to solve this issues?
select min(p.start_timestamp AT TIME ZONE p.timezone AT TIME ZONE 'America/Phoenix') as Date,
'America/Phoenix' AS Timezone, sum(GREATEST(0, p.value)) as Value, p.uom as UnitOfMeasurement
from main.production_ts_2017_10 p
where p.start_timestamp AT TIME ZONE p.timezone >= to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
and p.start_timestamp AT TIME ZONE p.timezone <= '2017-10-30'
and p.serial_number = '5T7842974Z'
group by date_trunc('hour', p.start_timestamp AT TIME ZONE p.timezone AT TIME ZONE 'America/Phoenix'), p.uom
order by Date
Let's simplify it down to just this part of the query:
p.start_timestamp AT TIME ZONE p.timezone >= to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
This is a conditional expression around the >= operator. It has a left-hand side (p.start_timestamp AT TIME ZONE p.timezone) and a right-hand side: (to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day') that are each evaluated separately, so the final conditional result (true/false) can be determined for each record.
Postgres database some how leaves few rows for the date: 2017-10-01
Look again at the right-hand side of the expression:
to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
That reduces down to this value:
'2017-10-02'
In other words, you're limited to records that come after '2017-10-01'. Do you really want that INTERVAL section in there? Did you want to subtract two days instead of add?
Let's assume two things:
You're asking "why does this query show a few rows for October 1st when it should show only rows for October 2nd or later"
p.start_timestamp is of type TIMESTAMP WITHOUT TIME ZONE (I feel safe in assuming this, because otherwise the double-timezone casting of it in other parts of the query makes no sense).
You have this WHERE clause:
where p.start_timestamp AT TIME ZONE p.timezone >= to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day'
Now, the left-hand side of the query is p.start_timestamp AT TIME ZONE p.timezone which will evaluate to a TIMESTAMP WITH TIME ZONE. The right-hand side of the query, however, is to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day' which evaluates to a TIMESTAMP WITHOUT TIMEZONE.
Whenever you compare a TIMESTAMP WITH TIMEZONE to a TIMESTAMP WITHOUT TIMEZONE, the results are going to be dependent on your current TimeZone setting in your psql session, because the TIMESTAMP-no-TZ will be evaluated as if it is in the psql TimeZone. For example, 12:31AM in 'America/Phoenix' on October 2nd can be 11:31AM in 'America/Los_Angeles', depending on the date (and BTW, you chose the worst possible time zone as your default, because America/Phoenix is just wierd). This would result in you seeing records from October 1st, depending on the value of p.timezone.
You're compounding the problem with this:
select min(p.start_timestamp AT TIME ZONE p.timezone AT TIME ZONE 'America/Phoenix') as Date
... so now you're displaying all timestamps as America/Phoenix timestamps regardless of what timezone they were originally in, or what timezone they were compared with. If your TimeZone setting is UTC, you'll see a LOT of October 1st records, because Oct 2nd in UTC overlaps with Oct 1st in America/Phoenix for six or seven hours depending on the date.
I suspect, given the rest of the query, what you want for that WHERE clause is:
where ( p.start_timestamp AT TIME ZONE p.timezone )
>=
( to_date('2017-09-30','YYYY-MM-DD') + INTERVAL '2 day' )
AT TIME ZONE 'America/Phoenix'
AND p.start_timestamp AT TIME ZONE p.timezone
<=
( TIMESTAMP '2017-10-30' AT TIME ZONE 'America/Phoenix' )
All of this is an illustrative lesson in why you should be storing all of your timestamp data as TIMESTAMP WITH TIME ZONE, instead of what you're doing here. As long as you're storing the timezone in a separate field an using AT TIME ZONE all the time, you're going to keep breaking queries. Also, with all of this casting time zones, any indexes you have on those time columns are unlikely to be used.