PostgreSQL get results in current time zone - sql

as said in the title I would like to have a query that returns the value of the time stamp in my current time zone (even according summer time!).
my_table is:
|timestamp|name|value|property1|property2|
|---|---|---|---|---|
|2021-08-01 00:00:00+00|10|0.44|0|0|
|2021-08-01 00:05:00+00|15|0.76|0|0|
|2021-08-01 00:10:00+00|12|0.28|0|0|
(Don't ask me why I cannot put this table directly in markdown...prob cause the dates)
Now for example if I have to select the 24h corresponding to the entire day in my time zone at the moment my solution is:
SELECT timestamp AT TIME ZONE 'CEST',name,value
FROM my_table
WHERE name IN (10,11,12)
AND timestamp BETWEEN '2021-08-01 00:00:00+02' AND '2021-08-02 00:00:00+02'
ORDER BY timestamp DESC
As you can see there is a problems here:
I have to specify every time if I is CEST or CET (now is CEST here)
and then I have to add +02 at the end of the dates (or +01 in CET)
There is a way to avoid this conceptual repetition?? any suggestion even to improve the query is appreciated
the command SELECT version(); gives me back PostgreSQL 12.7

Set your session's timezone appropriately.
set timezone TO 'Europe/Berlin';
select '2021-08-01 00:00:00+00'::timestamptz;
timestamptz
------------------------
2021-08-01 02:00:00+02
select '2021-12-01 00:00:00+00'::timestamptz;
timestamptz
------------------------
2021-12-01 01:00:00+01
select '2021-08-01 00:00:00'::timestamptz;
timestamptz
------------------------
2021-08-01 00:00:00+02
What is your session timezone set to now?

Related

Oracle Spool file via CMD command deliver more Data than expected

i have a Oracle Table "Sales" with columns ID,Sales,TIMESTAMP. Data looks like this:
ID Sales TimeStamp
1 30 2018-08-20 00:00:00.989900 +02:00
1 35 2018-08-21 05:00:00.989900 +02:00
...
1 35 2018-08-27 05:00:00.989900 +02:00
i created a Talend Job to execute a SQL Spool file in CMD mode to export a Query into csv. The Spoolfile look like this:
alter session set NLS_TIMESTAMP_TZ_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM';
alter session set NLS_TIMESTAMP_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6';
alter session set NLS_DATE_FORMAT ='YYYY-MM-DD';
alter session set NLS_NUMERIC_CHARACTERS ='.,';
spool C:/test.csv
SET ECHO OFF
SET ...
SELECT * FROM Sales where timestamp< to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff66 TZH:TZM')
when the TalendJob runs the Query on CMD mode, it gives me more data than expected with the Data to '2018-08-25 01:00:00'.
when i execute the SQL Query on Oracle Server manually, it gives correct Data to '2018-08-25 00:00:00'
==> Query on CMD on Talend give 1 hours of Data more than expected.
i don't really understand why that Problem happens.
My assumption is the Problem Timestamp in the Query "'2018-08-25 00:00:00.0000000'". this Timestamp has no time zone. but i am not sure.
can you please help me with this Problem?
Thankyou.
The manual query and the Talend query seem to be running in sessions with different time zones.
You aren't specifying a time zone in your fixed value, despite having TZH:TZM in the format model; and in fact you can't with to_timestamp():
select to_timestamp('2018-08-25 00:00:00.0000000 +02:00','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
from dual;
ORA-01821: date format not recognized
because that function gives you a plain timestamp:
alter session set NLS_TIMESTAMP_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6';
alter session set NLS_TIMESTAMP_TZ_FORMAT ='YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM';
select to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
as plain_timestamp
from dual;
PLAIN_TIMESTAMP
--------------------------
2018-08-25 00:00:00.000000
When you use that plain timestamp in a comparison with your table column, which is a timestamp with time zone, there is an implicit conversion into the session time zone. You can see the effect that has by manually setting it:
alter session set time_zone = 'Europe/London';
select cast(
to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
as timestamp with time zone
) as timestamp_with_session_zone
from dual;
TIMESTAMP_WITH_SESSION_ZONE
---------------------------------
2018-08-25 00:00:00.000000 +01:00
alter session set time_zone = 'America/New_York';
select cast(
to_timestamp('2018-08-25 00:00:00.0000000','YYYY-MM-DD HH24:mi:ss:ff6 TZH:TZM')
as timestamp with time zone
) as timestamp_with_session_zone
from dual;
TIMESTAMP_WITH_SESSION_ZONE
---------------------------------
2018-08-25 00:00:00.000000 -04:00
So, to be getting different data from your two sessions, that comparison is using a different value, therefore the session time zones must be different.
The simple fix is to specify the time zone explicitly in your fixed value, but you need a different function to avoid the error seen earlier; and preferably with a region instead of an offset to allow for daylight savings (assuming the values in your table are region-based too):
select to_timestamp_tz('2018-08-25 00:00:00.0000000 Europe/Berlin','YYYY-MM-DD HH24:mi:ss:ff6 TZR')
as timestamp_with_berlin_zone
from dual;
TIMESTAMP_WITH_BERLIN_ZONE
---------------------------------
2018-08-25 00:00:00.000000 +02:00
or you could use a timestamp literal:
select timestamp '2018-08-25 00:00:00.0 Europe/Berlin' as timestamp_with_berlin_zone
from dual;
which gets the same value.
i haved tried to format the time zone in the Query with to_timestamp_tz(substr('2018-08-25 00:00:00.0000000'),1,25), 'YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM' at time zone 'berlin/europe') as input_timestamp but it stills gives me more data than expected.
Ignoring the odd substr() which just strips the last two zeros off what is already a fixed string, if you do:
select to_timestamp_tz('2018-08-25 00:00:00.0000000', 'YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM')
at time zone 'Europe/Berlin' as timestamp_with_wrong_time
from dual;
you get (with my session still on New York time for greater effect)
TIMESTAMP_WITH_WRONG_TIME
---------------------------------
2018-08-25 06:00:00.000000 +02:00
The time zone is now what you expected, but the time is wrong. You have much the same problem as before. You're still converting the fixed value with no time zone supplied into a timestamp with time zone, so it's implicitly using the session time zone:
select to_timestamp_tz('2018-08-25 00:00:00.0000000', 'YYYY-MM-DD HH24:mi:ss.ff6 TZH:TZM')
as timestamp_with_wrong_time
from dual;
TIMESTAMP_WITH_WRONG_TIME
---------------------------------
2018-08-25 00:00:00.000000 -04:00
and then the at timezone 'Europe/Berlin' just gives that exact same point in universal time - midnight in New York, which is 04:00 UTC - but in Berlin local time, which is 06:00. It's the same point of time, just viewed from different places/time zone.
Again, you just need to specify the time zone for the fixed time you're using for the comparison - as timestamp '2018-08-25 00:00:00.0 Europe/Berlin'.

Oracle timestamp with timezone : replace date portion to current date

I have a oracle table for maintaining cutoff time. here we are storing cutoff time for various securities.. currently we are defaulting date portion of cutoff to 1st Jan 1970. The requirement is to replace this date portion with current date without changing time and timezone portion of cutoff.
You could calculate the number of days between today and 1970-01-01, and then add that as a day-to-second interval to your cutoff timestamp value:
create table my_table (cutoff timestamp with time zone);
insert into my_table values (timestamp '1970-01-01 18:00:00 -5:00');
select cutoff + numtodsinterval(trunc(sysdate) - date '1970-01-01', 'DAY') as adjusted
from my_table;
ADJUSTED
-----------------------------------
05-JUN-17 18.00.00.000000000 -05:00
or if you prefer, you can generate an interval directly (as #mathguy pointed out):
select cutoff + (trunc(sysdate) - timestamp '1970-01-01 00:00:00') as adjusted
from my_table;
ADJUSTED
-------------------------
06-JUN-17 18:00:00 -05:00
The -05:00 time zone offset ignores daylight savings time, of course, but that seems to be what you intended.

PostgreSQL: Adding an interval to a timestamp in a different time zone

What is the best way to add a specified interval to a timestamp with time zone, if I don't want to do the calculation in the time zone of the server. This is particularly important around daylight savings transitions.
e.g.
consider the evening that we "spring forward". (Here in Toronto, I think it was 2016-03-13 at 2am).
If I take a time stamp:
2016-03-13 00:00:00-05
and add '1 day' to it, in Canada/Eastern, I would expect to get 2016-03-14 00:00:00-04 -> 1 day later, but actually only 23 hours
But if I add 1 day to it in Saskatchewan (a place that doesn't use DST), I would want it to add 24 hours, so that I'd end up with
2016-03-13 01:00:00-04.
If I have columns / variables
t1 timestamp with time zone;
t2 timestamp with time zone;
step interval;
zoneid text; --represents the time zone
I essentially want to say
t2 = t1 + step; --in a time zone of my choosing
Postgres documentation seems to indicate that timestamp with time zone is internally stored in UTC time, which seems to indicate that a timestamptz column has no reckoning of a time zone in it.
The SQL standard indicates that
datetime + interval operation should maintain the time zone of the first operand.
t2 = (t1 AT TIME ZONE zoneid + step) AT TIME ZONE zoneid;
doesn't seem to work because the first cast turns t1 into a timezone-less timestamp and thus can't reckon DST transitions
t2 = t1 + step;
doesn't seem to work as it does the operation in the time zone of my SQL server
set the postgres time zone before the operation and change it back after?
A better illustration:
CREATE TABLE timestamps (t1 timestamp with time zone, timelocation text);
SET Timezone 'America/Toronto';
INSERT INTO timestamps(t1, timelocation) VALUES('2016-03-13 00:00:00 America/Toronto', 'America/Toronto');
INSERT INTO timestamps(t1, timelocation) VALUES('2016-03-13 00:00:00 America/Regina', 'America/Regina');
SELECT t1, timelocation FROM timestamps; -- shows times formatted in Toronto time. OK
"2016-03-13 00:00:00-05";"America/Toronto"
"2016-03-13 01:00:00-05";"America/Regina"
SELECT t1 + '1 day', timelocation FROM timestamps; -- Toronto timestamp has advanced by 23 hours. OK. Regina time stamp has also advanced by 23 hours. NOT OK.
"2016-03-14 00:00:00-04";"America/Toronto"
"2016-03-14 01:00:00-04";"America/Regina"
How to get around this?
a) Cast the timestamptz to a timestamp tz in the appropriate time zone?
SELECT t1 AT TIME ZONE timelocation + '1 day', timelocation FROM timestamps; --OK. Though my results are timestamps without time zone now.
"2016-03-14 00:00:00";"America/Toronto"
"2016-03-14 00:00:00";"America/Regina"
SELECT t1 AT TIME ZONE timelocation + '4 hours', timelocation FROM timestamps; -- NOT OK. I want the Toronto time to be 5am
"2016-03-13 04:00:00";"America/Toronto"
"2016-03-13 04:00:00";"America/Regina"
b) Change timezone of postgres and proceed.
SET TIMEZONE = 'America/Regina';
SELECT t1 + '1 day', timelocation FROM timestamps; -- Now the Regina time stamp is correct, but toronto time stamp is incorrect (should be 22:00-06)
"2016-03-13 23:00:00-06";"America/Toronto"
"2016-03-14 00:00:00-06";"America/Regina"
SET TIMEZONE = 'America/Toronto';
SELECT t1 + '1 day', timelocation FROM timestamps; -- toronto is correct, regina is not, as before
"2016-03-14 00:00:00-04";"America/Toronto"
"2016-03-14 01:00:00-04";"America/Regina"
This solution will only work if I continually switch the postgres timezone before every operation time interval operation.
It is a combination of two properties that causes your problem:
timestamp with time zone is stored in UTC and does not contain any time zone information. A better name for it would be “UTC timestamp”.
Addition of timestamp with time zone and interval is always performed in the current time zone, i.e. the one set with the configuration parameter TimeZone.
Since what you really need to store is a timestamp and the time zone in which it is valid, you should store a combination of timestamp without time zone and a text representing the time zone.
As you correctly noticed, you would have to switch the current time zone to perform interval addition over the daylight savings time shift correctly (otherwise PostgreSQL does not know how long 1 day is).
But you don't have to do that by hand, you can use a PL/pgSQL function to do it for you:
CREATE OR REPLACE FUNCTION add_in_timezone(
ts timestamp without time zone,
tz text,
delta interval
) RETURNS timestamp without time zone
LANGUAGE plpgsql IMMUTABLE AS
$$DECLARE
result timestamp without time zone;
oldtz text := current_setting('TimeZone');
BEGIN
PERFORM set_config('TimeZone', tz, true);
result := (ts AT TIME ZONE tz) + delta;
PERFORM set_config('TimeZone', oldtz, true);
RETURN result;
END;$$;
That would give you the following, where the result is to be understood in the same time zone as the argument:
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Toronto', '1 day');
add_in_timezone
---------------------
2016-03-14 00:00:00
(1 row)
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Regina', '1 day');
add_in_timezone
---------------------
2016-03-14 00:00:00
(1 row)
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Toronto', '4 hours');
add_in_timezone
---------------------
2016-03-13 05:00:00
(1 row)
test=> SELECT add_in_timezone('2016-03-13 00:00:00', 'America/Regina', '4 hours');
add_in_timezone
---------------------
2016-03-13 04:00:00
(1 row)
You could consider creating a combined type
CREATE TYPE timestampattz AS (
ts timestamp without time zone,
zone text
);
and define operators and casts on it, but that's probably a major project that exceeds what you want for this.
There even is a PostgreSQL extension timestampandtz that does exactly that; maybe that's just what you need (I didn't look what the semantics for addition are).
Based on the informations given by #LaurenzAlbe, I used something different to handle adding an interval to a timestamptz and using DST. I had the problem this 2020-10-25 since in Belgium (timezone "Europe/Brussels" which is UTC+1) we observe DST and on 2020-10-25 at 03:00 we went backward for 1 hour ending at 02:00, i.e. we went from summer time UTC+2 to winter time UTC+1.
The code below which has to find the timestamptz at 16:00 on a given day failed that day and instead ended at 15:00 because we went backward 1h. The problematic line of code was:
select date_trunc('day', myfct.datetime) + interval 'PT16H'
For example, first query is ok, second is not.
select date_trunc('day', timestamptz '2020-10-27 17:00:00+01') + interval 'PT16H';
?column?
------------------------
2020-10-27 16:00:00+01
select date_trunc('day', timestamptz '2020-10-25 17:00:00+01') + interval 'PT16H';
?column?
------------------------
2020-10-25 15:00:00+01
The idea is to make the addition with a timestamp (without time zone) instead of a timestamptz and finally convert it to a timestamptz with the configured time zone of the database.
hydro_dev=> select date_trunc('day', timestamptz '2020-10-25 17:00:00+01');
date_trunc
------------------------
2020-10-25 00:00:00+02
(1 row)
hydro_dev=> select date_trunc('day', timestamptz '2020-10-25 17:00:00+01')::timestamp;
date_trunc
---------------------
2020-10-25 00:00:00
(1 row)
hydro_dev=> select date_trunc('day', timestamptz '2020-10-25 17:00:00+01')::timestamp + interval 'PT16H';
?column?
---------------------
2020-10-25 16:00:00
(1 row)
select (date_trunc('day', timestamptz '2020-10-25 17:00:00+01')::timestamp + interval 'PT16H')::timestamptz;
timestamptz
------------------------
2020-10-25 16:00:00+01

SQL UTC date instead of SYSDATE

Working on the following query.
SELECT MIN(departure_date), ch_invoice.invoice_id
FROM ch_invoice
INNER JOIN ch_trip
ON ch_invoice.invoice_id = ch_trip.invoice_id
WHERE departure_date < SYSDATE
AND service_rendered = 0
AND paid = 1
Group By ch_invoice.invoice_id
Since the database that it is hitting may be in a location where the date hasn't changed yet, and we are using UTC as a standard for our dates, is it possible to replace SYSDATE with something like UTCDATE? I just need to know what day UTC is currently at.
As AntDC suggested, you can use the SYS_EXTRACT_UTC() function. That converts a 'datetime with time zone' from whatever time zone it is in to UTC.
SYSDATE doesn't have a time zone. If you passed that in then it would be implicitly converted to the system time zone, so it would work, but you can use SYSTIMESTAMP instead as that is already zone-aware.
You said in a comment that you want the date, but all Oracle dates have a time component; you can get the result as a date type with the time set to midnight (which makes sense for the comparison you're doing) with TRUNC():
select systimestamp,
sys_extract_utc(systimestamp) as utctime,
trunc(sys_extract_utc(systimestamp)) as utcdate
from dual;
SYSTIMESTAMP UTCTIME UTCDATE
-------------------------------- ------------------------- -------------------
2016-04-08 17:18:27.352 +01:00 2016-04-08 16:18:27.352 2016-04-08 00:00:00
That's running on a server in the UK, so the local time is BST.
Since that doesn't involve a day change, the effect can be demonstrated using the session time instead of the server time:
alter session set time_zone = '+12:00';
select current_timestamp,
sys_extract_utc(current_timestamp) as utctime,
trunc(sys_extract_utc(current_timestamp)) as utcdate
from dual;
CURRENT_TIMESTAMP UTCTIME UTCDATE
-------------------------------- ------------------------- -------------------
2016-04-09 04:23:17.910 +12:00 2016-04-08 16:23:17.910 2016-04-08 00:00:00
My session time is 2016-04-09, but it still finds the UTC date as 2016-04-08.
(To be clear, I am not suggesting you switch to using current_timestamp or current_date; I'm using those instead of systimestamp and sysdate purely as a demo).
SELECT SYS_EXTRACT_UTC(departure_date)UTC_SYS, SYSTIMESTAMP FROM DUAL;

Extract date,month,year and month name from the unix timestamp with postgresql

I use postgres for the rails app and I have a unix timestamp in postgresql db. I have a requirement to select and group by the dd-mm-yyyy and by month name.
Consider I have the following unix timestamp
1425148200
and I would need to change this to datetime and I used to_timestamp which returned
2015-02-28 18:30:00 UTC
and I tried to convert the datetime to local timezone using
::timestamp without time zone AT TIME ZONE 'IST'
but that did not give time in required timezone and instead it returned
2015-02-28 16:30:00 UTC
and I tried to get the date part using ::date which returned
Sat, 28 Feb 2015
So please help me get the dd-mm-yyyy in specified timezone and month name(March) from the unix timestamp.
Thanks in Advance!
select to_char(to_timestamp('1425148200')::timestamptz at time zone 'UTC-5:30','DD-MM-YYYY & of course Month')
01-03-2015 & of course March
It is postgres mistake I guess
according to http://www.postgresql.org/docs/7.2/static/timezones.html