Get today's date - Different timezone - sql

I just wanted to know whether the following SQL is good to convert US server time to Thailand date. As we have 12 hours difference in the time, and TH time is ahead of U.S time
SELECT TO_DATE(
TO_CHAR(
SYSTIMESTAMP AT TIME ZONE 'Asia/Bangkok', 'yyyy-mm-dd',
'NLS_DATE_LANGUAGE = american'), 'yyyy-mm-dd') AS TODAY
FROM dual;
It works perfectly fine. But are there any other better way to convert timestamp of server from one timezone to another as I need to compare today's date based on this result in my outer SQL.

You could CAST the timestamp to your desired timezone.
For example,
SQL> WITH data AS (
2 SELECT SYSTIMESTAMP AT TIME ZONE 'Asia/Bangkok' tm_bangkok FROM dual
3 )
4 SELECT tm_bangkok,
5 CAST(tm_bangkok AT TIME ZONE 'EST' AS TIMESTAMP) tm_est
6 FROM data;
TM_BANGKOK TM_EST
--------------------------------------------- ----------------------------
03-NOV-15 12.54.18.951000 PM ASIA/BANGKOK 03-NOV-15 12.54.18.951000 AM

There is no reason to cast a TIMESTAMP to CHAR and then back to TIMESTAMP again. Simply do
SELECT SYSTIMESTAMP AT TIME ZONE 'Asia/Bangkok' AS TODAY
FROM dual;

Related

How to get 24 hours back time from current time in Oracle

I have a column in my Oracle db which records the creation time of a user in the following format
30-NOV-20 11.49.11.000000000 AM (TIMESTAMP(6) format).
What I wanted to do is select all records whose creation time is 24 hours earlier than current time
So what I was going to do was subtract 1 from current time and compare it. But when I subtract 1 it returns only the date.
select * from user where created_date < SYSTIMESTAMP-1
dbms_output.put_line (SYSTIMESTAMP-1);-->29-NOV-20
The time parts are missing which makes me unable to compare with created time in the table
Please help me to complete this task.
If you subtract 1 from a date datatype (e.g. sysdate), it'll move you back one day. But, if you subtract it from a timestamp datatype value, Oracle will convert it to date and return a date (moreover, it'll be truncated).
See the following example:
SQL> select
2 systimestamp val1,
3 systimestamp - 1 val2,
4 --
5 systimestamp - interval '1' day val3
6 from dual;
VAL1
-----------------------------------------------------
VAL2
--------
VAL3
-----------------------------------------------------
30.11.20 09:55:01,439352 +01:00
29.11.20
29.11.20 09:55:01,439352000 +01:00
SQL>
So, what you should do is to subtract an interval, i.e.
select *
from user
where created_date < systimestamp - interval '1' day;
Half the problem is that whatever client program is being used to display the values is using the default date format for their territory and that default format is set to DD-MON-RR.
You can change the NLS_DATE_FORMAT and NLS_TIMESTAMP_FORMAT session parameters and that will (assuming your client program uses them and not some internal settings) give you the output you are expecting:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF9';
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF9TZR';
Then
SELECT SYSDATE, SYSTIMESTAMP FROM DUAL;
Outputs (depending on your system time zone):
SYSDATE | SYSTIMESTAMP
:------------------ | :----------------------------------
2020-11-30 10:12:22 | 2020-11-30 10:12:22.476282000+00:00
If you use SYSTIMESTAMP-1 then Oracle does not support subtracting a NUMBER data type from a TIMESTAMP [WITH TIME ZONE] data type but it does support subtracting a NUMBER data type from a DATE data type and will perform an implicit cast from TIMESTAMP to DATE so that the query is valid.
For example:
SELECT SYSDATE - 1, SYSTIMESTAMP - 1, SYSTIMESTAMP - INTERVAL '1' DAY FROM DUAL;
Outputs:
SYSDATE-1 | SYSTIMESTAMP-1 | SYSTIMESTAMP-INTERVAL'1'DAY
:------------------ | :------------------ | :----------------------------------
2020-11-29 10:24:02 | 2020-11-29 10:24:02 | 2020-11-29 10:24:02.651735000+00:00
You can see that in the middle column SYSTIMESTAMP-1 gives the same output as SYSDATE-1 but in the right-hand column, subtracting an interval has ensured that TIMESTAMP WITH TIME ZONE data type is maintained.
So your query:
SELECT * FROM user WHERE created_date < SYSTIMESTAMP-1
Is effectively:
SELECT * FROM user WHERE created_date < CAST( SYSTIMESTAMP AS DATE )-1
Which will have exactly the same year, month, day, hour, minute and (integer) second components but will lose the fractional seconds and time zone information from the SYSTIMESTAMP.
If your column does not have time zone data and the level of precision in the fractional seconds does not matter to you then your query will work adequately.
However, if you want to keep the time zone and/or fractional seconds information then you can use:
SELECT * FROM user WHERE created_date < SYSTIMESTAMP - INTERVAL '1' DAY;
However, if created_date is a DATE column, you probably want:
SELECT * FROM user WHERE created_date < SYSDATE - INTERVAL '1' DAY;
or
SELECT * FROM user WHERE created_date < SYSDATE - 1;
db<>fiddle here
You can use it as (SYSDATE, -1).
Also, if you want to search for new records within 24 hours, it should be "created_date> = (sysdate-1)".

Casting Local Time to UTC Formating Incorrect

HIRE_DATE is in a 'DATE' column. The timestamp is local (Los Angeles); I would like to convert it to UTC.
I can't for the life of me fathom why the UTC output is mangled (Last 2 digits of YY is the DD; and vice-versa) -- and the time does not convert to UTC.
HIRE_DATE: 30/04/2019 12:00:00 AM
select from_tz(to_timestamp(HIRE_DATE,'DD-MM-YY HH24:MI:SS'), 'America/Los_Angeles') at time zone 'UTC' from TABLE
OUTPUT: 19/04/2030 12:00:00 AM
If HIRE_DATE is a DATE data type then you don't need TO_TIMESTAMP.
TO_TIMESTAMP is used to convert a string (i.e. VARCHAR2) into a TIMESTAMP value but you have a DATE value.
Just do
select from_tz(CAST(HIRE_DATE AS TIMESTAMP), 'America/Los_Angeles') at time zone 'UTC'
from TABLE
Actually I don't understand why FROM_TZ does not accept DATE values whereas almost any other date/timestamp related function accept either DATE or TIMESTAMP value as input.
Note, the default output display format of this query is defined by current user session NLS_TIMESTAMP_TZ_FORMAT setting. If you are not satisfied with the output format, either change NLS_TIMESTAMP_TZ_FORMAT setting by executing ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = '...' or use TO_CHAR function to set output format explicitly.
Instead of
... AT TIME ZONE 'UTC'
you can also use
SYS_EXTRACT_UTC(...)
The upper returns a TIMESTAMP WITH TIME ZONE value, the second one returns a TIMESTAMP value.
Would this do any good?
SQL> select from_tz(cast (sysdate as timestamp), 'UTC') result from dual;
RESULT
---------------------------------------------------------------------------
27.09.20 10:59:28,000000 UTC
Or, in your case
select from_tz(cast (hire_date as timestamp), 'UTC' from dual
No need to apply any format mask to hire_date as it is a DATE datatype (at least, that's what you said).
You use the word "convert" which can mean one of two things:
change the data type, which is what FROM_TZ does
change the value from one time zone to another, which FROM_TZ does not do.
You didn't give your expected output, so we may misunderstand.
To change the data type:
with data(dte) as (
select date '2019-04-30' + interval '12' hour from dual
)
select from_tz(cast(dte as timestamp), 'America/Los_Angeles') from data
FROM_TZ(CAST(DTEASTIMESTAMP),'AMERICA/LOS_ANGELES')
30-APR-19 12.00.00.000000 PM AMERICA/LOS_ANGELES
To get the simultaneous datetime value in UTC:
with data(dte) as (
select date '2019-04-30' + interval '12' hour from dual
)
select cast(sys_extract_utc(from_tz(cast(dte as timestamp), 'America/Los_Angeles')) as date) from data
CAST(SYS_EXTRACT_UTC(FROM_TZ(CAST(DTEASTIMESTAMP),'AMERICA/LOS_ANGELES'))ASDATE)
2019-04-30 19:00:00

to_Char(DATE) with time

I have a DB (Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production). In there is a table (Course) in which I have a Date column (start_dte). I want to format the output to a char so I used:
SELECT start_dte,
to_Char(start_dte) AS start_dte_2,
to_Char(start_dte,'DD.MM.YYYY') AS start_dte_3,
to_Char(start_dte,'DD.MM.YYYY HH24:MI:SS') AS start_dte_4,
to_Char(start_dte,'DD.MM.YYYY HH12:MI:SS') AS start_dte_5,
to_Char(start_dte,'DD.MM.YYYY HH.MI.SSXFF AM') AS start_dte_6,
to_Char(start_dte,'DD.MM.YYYY HH24:MI:SSxFF') AS start_dte_7,
to_Char(start_dte,'DD-MON-YYYY HH24:MI:SSxFF TZH:TZM') AS start_dte_8
FROM Course
The Results:
Number 6,7,8 give me
java.sql.SQLException: ORA-01821: date format not recognized
6 has the same format as:
SELECT value
FROM V$NLS_PARAMETERS
WHERE parameter = 'NLS_TIME_FORMAT'
7 and 8 are taken from the Oracle documentation but this is only for timestamps. So what i am missing? I know the actual datatype should be datetime but I only have read access to that DB. What i would actually like:
Or even better:
A DATE doesn't have any time zone information, so if you want to adjust the displayed time then you'll need to specify which time zone that date nominally represents, and which time zone you want to convert it to.
An an example, if your stored date represents UTC and you want to see the equivalent local time in Paris, you can state that the stored time is UTC by casting it as a plain timestamp and passing that into the from_tz() function; and then specify the target timezone with at:
-- CTE for your sample data
with course (start_dte) as (
select cast(timestamp '2018-10-17 14:00:00' as date) from dual
)
-- actual query
select from_tz(cast(start_dte as timestamp), 'UTC')
at time zone 'Europe/Paris' as start_timestamp_tz
from course;
START_TIMESTAMP_TZ
------------------------------------------
2018-10-17 16:00:00.000000000 EUROPE/PARIS
Since you're querying this via JDBC you probably want to retrieve that as its native data type, and then choose how to display it locally. (But you could presumably query it as a date and adjust it in Java too...)
If you want to convert it to a string on the DB side, though, just use the format model you already used:
select to_char(from_tz(cast(start_dte as timestamp), 'UTC')
at time zone 'Europe/Paris', 'DD.MM.YYYY HH24:MI') as start_date
from course;
START_DATE
----------------
17.10.2018 16:00
Getting it into two fields is also simple, and if you don't want to repeat the conversion you can use a CTE or an inline view:
select to_char(start_timestamp_tz, 'DD.MM.YYYY') as start_date,
to_char(start_timestamp_tz, 'HH24:MI') as start_time
from (
select from_tz(cast(start_dte as timestamp), 'UTC')
at time zone 'Europe/Paris' as start_timestamp_tz
from course
);
START_DATE START_TIME
---------- ----------
17.10.2018 16:00
But it sounds like Java should be doing that conversion to strings for display.
What i would actually like:
| START_DATE |
|------------------|
| 17.10.2018 16:00 |
Or even better:
| START_DATE | START_TIME |
|------------|------------|
| 17.10.2018 | 16:00 |
Use:
SELECT to_Char(start_dte,'DD.MM.YYYY') AS start_date,
to_Char(start_dte,'HH24:MI') AS start_time
FROM Course
If your data is stored in the table in one time zone (i.e. UTC) and you want it in another time zone then use:
CAST( date_value AS TIMESTAMP ) to convert it from a DATE data type to a TIMESTAMP data type.
FROM_TZ( timestamp_value, timezone_string ) to convert it from a TIMESTAMP data type to a TIMESTAMP WITH TIME ZONE data type at the given time zone.
timestamp_with_timezone_value AT TIME ZONE timezone_string to convert it from one time zone to another time zone.
Like this:
SELECT TO_CHAR(
FROM_TZ( CAST( start_dte AS TIMESTAMP ), 'UTC' ) AT TIME ZONE 'Europe/Berlin',
'DD.MM.YYYY'
) AS start_date,
TO_CHAR(
FROM_TZ( CAST( start_dte AS TIMESTAMP ), 'UTC' ) AT TIME ZONE 'Europe/Berlin',
'HH24:MI'
) AS start_time
FROM Course
start_dte is a DATE value.
DATE does neither have fractional seconds (i.e. XFF) nor time zone information (i.e. TZH:TZM)
Use TIMESTAMP WITH TIME ZONE is you like to get such output.

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;

Oracle UTC Time

I'm trying to read an Oracle TIMESTAMP WITH TIMEZONE from a database in UTC-5 (NY) as UTC.
Oracle is driving me crazy:
SELECT
from_tz(MAX(TIMESTAMPWITHTIMEZONE),'UTC'),
SYS_EXTRACT_UTC(MAX(TIMESTAMPWITHTIMEZONE)),
SYS_EXTRACT_UTC(systimestamp),
SYSTIMESTAMP AT TIME ZONE 'UTC'
FROM TABLE
Results:
SYS_EXTRACT_UTC(systimestamp) gives me: 2013-02-20 14:59:04, which is probably right.
SYSTIMESTAMP AT TIME ZONE 'UTC' gives me: 2013-02-20 15:59:04 which is my own local Berlin - whatever
Now I want to have TIMESTAMPWITHTIMEZONE (TIMESTAMP(6)) as UTC
SYS_EXTRACT_UTC(MAX(TIMESTAMPWITHTIMEZONE)) is 2013-02-20 08:55:01
from_tz(MAX(TIMESTAMPWITHTIMEZONE),'UTC') is 2013-02-20 10:55:01
Srly. Oracle. I want UTC.
Which one is the right one? Or is there a better way?
The functions are different:
SYS_EXTRACT_UTC converts a TIMESTAMP WITH TIMEZONE to a TIMESTAMP (with inferred but absent timezone=UTC).
FROM_TZ converts a TIMESTAMP to a TIMESTAMP WITH TIMEZONE
These functions when applied to a single value will in general return a different result:
SQL> SELECT sys_extract_utc(localtimestamp) ext,
2 from_tz(localtimestamp, 'UTC') from_tz
3 FROM dual;
EXT FROM_TZ
--------------------- ------------------------
2013/02/20 15:34:24 2013/02/20 16:34:24 UTC
In the first case the TIMESTAMP is implicitly given the timezone of the server and then transformed into the equivalent timestamp at the UTC timezone. Note that in general you should stay away from implicit conversions.
In the second case there is no computation between timezones: the FROM_TZ function adds the geographical location to a point in time variable.
By the way there is something missing in your example: you can't apply the FROM_TZ function on a variable of type TIMESTAMP WITH TIMEZONE (tested on 9ir2 and 11ir2):
SQL> select from_tz(systimestamp, 'UTC') from dual;
select from_tz(systimestamp, 'UTC') from dual
ORA-00932: inconsistent datatypes:
expected TIMESTAMP got TIMESTAMP WITH TIME ZONE
Edit following comment:
In your case assuming that your column are of time TIMESTAMP, and knowing that they refer to the NY timezone, you could use the AT TIME ZONE expression to convert to UTC:
SQL> SELECT localtimestamp,
2 from_tz(localtimestamp, 'America/New_York') AT TIME ZONE 'UTC' utc
3 FROM dual;
LOCALTIMESTAMP UTC
--------------------- ------------------------
2013/02/20 17:09:09 2013/02/20 22:09:09 UTC
From Oracle 18c you could use TO_UTC_TIMESTAMP_TZ function:
The TO_UTC_TIMESTAMP_TZ function converts any valid ISO 8601 date represented as a string into a TIMESTAMP WITH TIMEZONE, which can optionally be used as input to the SYS_EXTRACT_UTC function.
SELECT TO_UTC_TIMESTAMP_TZ(col_name)
FROM tab_name;