Oracle Spool file via CMD command deliver more Data than expected - sql

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'.

Related

remove milliseconds from datetime type

Trying to remove the milliseconds and the pm/am from the datetime field using Oracle SQL.
My query is:
select created_dt from mydatabase
Output is:
09-NOV-21 12.18.40.490000000 PM
I tried using to_char(created_dt, 'dd-mm-yy hh24:mi:ss') then using to_timestamp to convert back to datetime type but the milliseconds showing again.
A TIMESTAMP data type is stored in a table as a binary data-type with 1-byte for each of century, year-of-century, month, day, hour, minute and second and has up to 6 bytes for fractional seconds. It is NEVER stored with any particular format.
If you want to change the number of fractional digits in a timestamp then you need to use CAST:
SELECT CAST(created_dt AS TIMESTAMP(0))
FROM mydatabase
However, you could just convert it to a DATE (which also has year-to-second components but no fractional seconds):
SELECT CAST(created_dt AS DATE)
FROM mydatabase
Once you have done that then whatever client application you are using (SQL/Plus, SQL Developer, Java, C#, PHP, etc.) will still use its default rules for displaying the TIMESTAMP (or DATE) and those rules may display a certain number of fractional seconds.
If you are using SQL/Plus or SQL Developer (but not if you are using Java, C#, PHP, etc.) then the default format is controlled by the NLS_TIMESTAMP_FORMAT session parameter.
You can alter this using:
-- Display with precision of the data type
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';
-- Display with 6-digits precision
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF6';
-- Display with no fractional seconds.
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
But, if you want a consistent method of formatting a TIMESTAMP then use TO_CHAR.
How the DATE or TIMESTAMP data types are stored can't be changed.
But you can change how a query outputs a TIMESTAMP by default for the session.
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'
Example:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MON-DD HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
select ts
, CAST(ts AS DATE) dt
, CAST(ts AS TIMESTAMP WITH TIME ZONE) AS tstz
from
(
select
to_timestamp('2021-11-30 23:30:45.123456789', 'YYYY-MM-DD HH24:MI:SS.FF') AS ts
from dual
) q
TS | DT | TSTZ
:------------------ | :------------------- | :-------------------------------
2021-11-30 23:30:45 | 2021-NOV-30 23:30:45 | 30-NOV-21 23.30.45.123457 +00:00
db<>fiddle here

PostgreSQL get results in current time zone

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?

Problems with sys_extract_utc and summer/wintertime

I have a problem converting a timestamp to UTC.
Our System Timezone is 'Europe/Berlin'. The actual ZoneOffset at the moment (as it is not summertime yet) is +1. This ZoneOffset will change on March 26 2017 from +1 to +2. (wintertime to summertime).
If I use the function sys_extract_utc('2017.03.27 00:00') then the offset which now applies is being used for the calculation. So the result is '2017.03.26 23:00', but I expected the result '2017.03.26 22:00'.
How can I get the correct result?
You said
the offset which now applies is being used for the calculation
That isn't quite true. Your session time zone seems to be set to a +01:00 offset instead of a region name:
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set TIME_ZONE = '+01:00';
select sys_extract_utc(timestamp '2017-03-27 00:00:00') from dual;
SYS_EXTRACT_UTC(TIM
-------------------
2017-03-26 23:00:00
The timestamp literal being supplied has no time zone information, so it's implicitly converted to the session time zone as part of the function call. An offset has no knowledge of summer/wintertime, so no adjustment is (or can be) made. You'd see the same thing if you ran that query after the 27th.
If you set the session time zone to the region instead then you'll get the correct result.
alter session set TIME_ZONE = 'Europe/Berlin';
select sys_extract_utc(timestamp '2017-03-27 00:00:00') from dual;
SYS_EXTRACT_UTC(TIM
-------------------
2017-03-26 22:00:00
You may be able to do that automatically by modifying your operating system environment variables; if not then you can set it in your client or (for SQL*Plus) in a .login or .glogin setup file.
If you're starting from a value which actually has a region, i.e. is a timestamp with time zone data type, then the session time zone doesn't matter:
alter session set TIME_ZONE = '+01:00';
select sys_extract_utc(timestamp '2017-03-27 00:00:00 Europe/Berlin') from dual;
SYS_EXTRACT_UTC(TIM
-------------------
2017-03-26 22:00:00

Oracle sql timezone issue

My requirement here is to get time in GMT/UTC from a date type column. But when I use cast to cast date to timestamp, it is using US/Pacific timezone as reference though session timezone is set to GMT. So unless I use from_tz, I am not seeing desired result. Is there any other timezone setting in oracle sql that I need to modify to take GMT as reference always?
alter session set time_zone='+00:00';
select sessiontimezone from dual;
select current_timestamp from dual;
select sys_extract_utc(cast (sysdate as timestamp)) from dual;
select sys_extract_utc(from_tz(cast (sysdate as timestamp), '-07:00')) from dual;
select sys_extract_utc(current_timestamp) from dual;
Session altered.
SESSIONTIMEZONE
---------------------------------------------------------------------------
+00:00
CURRENT_TIMESTAMP
---------------------------------------------------------------------------
11-APR-16 08.46.42.292173 AM +00:00
SYS_EXTRACT_UTC(CAST(SYSDATEASTIMESTAMP))
---------------------------------------------------------------------------
11-APR-16 01.46.42.000000 AM
SYS_EXTRACT_UTC(FROM_TZ(CAST(SYSDATEASTIMESTAMP),'-07:00'))
---------------------------------------------------------------------------
11-APR-16 08.46.42.000000 AM
SYS_EXTRACT_UTC(CURRENT_TIMESTAMP)
---------------------------------------------------------------------------
11-APR-16 08.46.42.295310 AM
Tasks table has a date type column called task_started. I am looking to get UTC time from this date field. As part of that I was trying to alter session timezone to GMT while inserting the data so that I can simply cast it back to timestamp which is not working.
select task_started from tasks where rownum <2;
TASK_STAR
---------
10-APR-16
desc tasks;
Name Null? Type
----------------------------------------- -------- ----------------------------
...
TASK_STARTED DATE
...
This is a demo using data inserted using sysdate on a system on London time, so currently on BST (+01:00). The difference is smaller than you'd see on the west coast but the same things apply.
Mimicking your table, you can see that the session time zone has no effect on the inserted value since sysdate uses the database server time, not the session (client) time (as explained here):
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF3';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF3 TZH:TZM';
create table tasks (id number, task_started date);
alter session set time_zone='America/Los_Angeles';
insert into tasks (id, task_started) values (1, sysdate);
select task_started, cast(task_started as timestamp) as ts
from tasks where rownum < 2;
TASK_STARTED TS
------------------- -----------------------
2016-04-11 11:55:59 2016-04-11 11:55:59.000
alter session set time_zone = 'UTC';
select task_started, cast(task_started as timestamp) as ts
from tasks where rownum < 2;
TASK_STARTED TS
------------------- -----------------------
2016-04-11 11:55:59 2016-04-11 11:55:59.000
So that's the BST time in both cases. There isn't any session or database setting that will show you a date (or timestamp, without a time zone) converted to a specific time zone automatically. The database doesn't know what that stored date/time represents unless you tell it. It doesn't know or case if you use sysdate, current_date, or a date literal; at the point the data is inserted into the table it has no time zone information at all.
To get the UTC equivalent you need to use from_tz to declare that the stored value represents a specific time zone; then you can use at time zone (which keeps the time zone info) or sys_extract_utc (which doesn't) on that to convert it; and optionally cast back to a date:
select from_tz(cast(task_started as timestamp), 'Europe/London') as db_tstz,
from_tz(cast(task_started as timestamp), 'Europe/London') at time zone 'UTC' as utc_tstz,
sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as utc_ts,
cast(sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as date) as utc_date
from tasks where rownum < 2;
DB_TSTZ UTC_TSTZ UTC_TS UTC_DATE
------------------------------ ------------------------------ ----------------------- -------------------
2016-04-11 11:55:59.000 +01:00 2016-04-11 10:55:59.000 +00:00 2016-04-11 10:55:59.000 2016-04-11 10:55:59
I've used the time zone region name so it takes care of summer time for me; you could use dbtimezone but that would always use -08:00, and as you showed in the question you need to use -07:00 at the moment. ANd you could use sessiontimezone but then you have to remember to set that properly. Obviously in your case you'd use your local region, e.g. America/Los_Angeles, instead of Europe/London.
And from that you can get the epoch, via an interval by comparing timestamps or more simply from comparing dates:
select sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London'))
- timestamp '1970-01-01 00:00:00' as diff_interval,
cast(sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as date)
- date '1970-01-01' as diff_days,
86400 *
(cast(sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as date)
- date '1970-01-01') as epoch
from tasks where rownum < 2;
DIFF_INTERVAL DIFF_DAYS EPOCH
---------------- --------- -----------
16902 10:55:59.0 16902.46 1460372159
If you put 1460372159 into an online converter (there are many) it will show the UTC time.

How to store a timestamp in Oracle that occurs within the hour skipped at the start of Daylight Savings

Our Oracle server is running in Australia/Sydney time.
We plan to store some new dates in UTC.
For example, one of the dates we may store is 5 Oct 2014 2:00AM.
However in Sydney we have Daylight Savings start at the same time, which means that times from 2:00AM to 2:59AM do not exist on that day.
For example, on 5 Oct 2014 the times that occur are:
01:58
01:59
03:00
03:01
The trouble is that if I try to store the time 2014-10-05 02:00 in the database, it's silently converted to 2014-10-05 03:00
We don't have the option to change the timezone on the server, so is there any way to store 2014-10-05 02:00 in our database?
Edit for comment from #mrjoltcola
Our server is running with timezone setting (GMT +10) Canberra, Melbourne, Sydney
If I run the command select DBTIMEZONE from dual; the output is the single value +00:00 (this was unexpected).
Our original column was a TIMESTAMP only column and I did not supply any timezone details with the insert.
For further exploration I created a test table as follows:
create table TEST
(
ID number,
TS timestamp,
TS_TZ timestamp with time zone
)
I then run the following insert statements (with and without the timezone):
insert into TEST
VALUES
(
1,
TIMESTAMP '2014-10-05 02:00:00 UTC',
TIMESTAMP '2014-10-05 02:00:00 UTC'
);
insert into TEST
VALUES
(
2,
TIMESTAMP '2014-10-05 02:00:00',
TIMESTAMP '2014-10-05 02:00:00'
);
Which produces the result:
+---+---------------------------------+--------------------------------------------------+
| 1 | 05/OCT/14 03:00:00.000000000 AM | 05/OCT/14 02:00:00.000000000 AM UTC |
+---+---------------------------------+--------------------------------------------------+
| 2 | 05/OCT/14 03:00:00.000000000 AM | 05/OCT/14 03:00:00.000000000 AM AUSTRALIA/SYDNEY |
+---+---------------------------------+--------------------------------------------------+
The trouble is that if I try to store the time 2014-10-05 02:00 in the database, it's silently converted to 2014-10-05 03:00
On my database, I can set the session timezone to 'Australia/Sydney' and still store 2014-10-05 02:00:00 in a plain TIMESTAMP column without having it converted. But if I change it to a TIMESTAMP WITH TIMEZONE and try to store the same value, I get ORA-01878
So I don't think your time was being converted to 03:00:00 based on Oracle trying to adjust the daylight savings, I think you are supposed to receive an ORA-01878 when you specify an incorrect timestamp. Instead, I think your client/session timezone was mismatching your database/server timezone by an hour, and you were seeing normal Oracle adjustment.
Oracle will convert timestamps when the client/session timezone mismatches the server/database timezone. Since there is no timezone info in a regular TIMESTAMP field, Oracle won't mind storing it.
So your "silently converted to 2014-10-05 03:00" should have been happening for other time values (did you try inserting 01:00 and see if it resulted in an adjustment and/or an ORA-01878 error?).
When I try here by setting my session timezone to Australia/Sydney and insert that time, I get:
SQL> alter session set time_zone = 'Australia/Sydney';
Session altered.
SQL> insert into test(ts) values(1, timestamp '2014-10-05 02:00:00');
insert into test values(1, timestamp '2014-10-05 02:00:00')
1 row created.
SQL> insert into test(ts_tz) values(2, timestamp '2014-10-05 02:00:00');
insert into test(ts_tz) values(2, timestamp '2014-10-05 02:00:00')
*
ERROR at line 1:
ORA-01878: specified field not found in datetime or interval
Now, as to your question, if you want to store UTC time, you should either explicitly specify timestamps with UTC, or set your database or session timezone to UTC. You don't have to have DBA privs to set the session time_zone, and it can differ from the db time_zone.
SQL> alter session set time_zone = '+00:00';
Session altered.
SQL> select dbtimezone, sessiontimezone from dual;
DBTIME SESSIONTIMEZONE
------ ---------------------------------------------------------------------------
+00:00 +00:00
SQL> insert into test values(1, timestamp '2014-10-05 02:00:00');
1 row created.
SQL> select * from test;
ID TS
---------- ---------------------------------------------------------------------------
1 05-OCT-14 02.00.00.000000 AM
However, the above TIMESTAMP field isn't necessarily UTC. It could change if the DB server timezone changed. To Oracle a plain TIMESTAMP is just a timestamp with no timezone information. If you want UTC time, but may be dealing with clients in different timezones, you can use TIMESTAMP WITH TIMEZONE or TIMESTAMP WITH LOCAL TIMEZONE.