ORA-01841 , (full) year must be between - oracle

I want to convert the epoch date from table into timestamp.
But it results in an error when I run it in Oracle.
But the year showing into "Rabu, 22 April 2465 pukul 15.35.06.289 GMT+07:00" when i run from this link "https://www.epochconverter.com/'
15630394456289509085900
this the epoch time from table
select to_char(
cast(
to_date('01/01/1970 00:00:00','DD/MM/YYYY HH24:MI:SS')+15630394456289509085900/86400
as timestamp with local time zone)
,'YYYY-MM-DD HH24:MI:SS')
from dual

Epoch times are in the UTC time zone. Just use a TIMESTAMP literal and add the correct amount of seconds:
SELECT TIMESTAMP '1970-01-01 00:00:00 UTC'
+ NUMTODSINTERVAL(15630394456289509085900 / 86400e12, 'DAY') AS epoch_time_12,
TIMESTAMP '1970-01-01 00:00:00 UTC'
+ NUMTODSINTERVAL(15630394456289509085900 / 86400e13, 'DAY') AS epoch_time_13
FROM DUAL
Which outputs:
EPOCH_TIME_12 | EPOCH_TIME_13
:-------------------------------- | :--------------------------------
2465-04-22 08:14:16.289509086 UTC | 2019-07-13 17:37:25.628950909 UTC
db<>fiddle here

Related

epoch conversion off by 1 second

select from_tz(
cast(
to_date('1970-01-01 00','yyyy-mm-dd hh24')
+ (1536698971759)/1000/60/60/24
as timestamp
),
'GMT'
) at time zone 'US/Eastern'
from dual;
yields:
11-SEP-18 04.49.32.000000 PM US/EASTERN
Yet if you plug 1536698971759 into any online converter you will see the # of seconds is actually 31, not 32.
What is wrong?
It's a problem with implicit rounding. DATE data type (due to TO_DATE()) does not support fractional seconds, thus 31.759 is rounded to 32.
Try this one:
SELECT
(TIMESTAMP '1970-01-01 00:00:00 UTC' + 1536698971759/1000 * INTERVAL '1' SECOND) AT TIME ZONE 'US/Eastern'
FROM dual;
or (TIMESTAMP '1970-01-01 00:00:00 UTC' + NUMTODSINTERVAL(1536698971759/1000, 'SECOND')) AT TIME ZONE 'US/Eastern' if you prefer
yields: 11.09.2018 16:49:31.759000000 -04:00

Oracle - Converting a UTC timestamp to a localtime (GMT/BST) timestamp

I have to create convert a datetime as UTC into localtime (GMT/BST)
The dates in the database are UTC AND the database is set to UTC.
I believe I can get the offset between UTC and (say) BST using TZ_OFFSET, but how can I then use that to convert the UTC datetime into a BST datetime?
So, for example, if the database (UTC) datetime is
'2018-04-03 14:30:00'
And the offset is '+01:00'
I would expect the result to be
'2018-04-03 15:30:00'
If there an elegant way of doing this? Rather than using grungy arithmetic (which then has to take into account midnight, end of month, end of year etc.)
Thanks
Use FROM_TZ to create a TIMSTAMP WITH TIME ZONE, then the conversion is very simple, for example:
FROM_TZ({your column}, 'UTC') AT TIME ZONE 'Europe/London'
As you can see in the documentation:
Example of Converting Time Zones With the AT TIME ZONE Clause:
SELECT FROM_TZ(CAST(TO_DATE('1999-12-01 11:00:00',
'YYYY-MM-DD HH:MI:SS') AS TIMESTAMP), 'America/New_York')
AT TIME ZONE 'America/Los_Angeles' "West Coast Time"
FROM DUAL;
West Coast Time
----------------------------------------------------------
01-DEC-99 08.00.00.000000 AM AMERICA/LOS_ANGELES
So, applied to your case scenario:
SELECT FROM_TZ(CAST(TO_DATE('2018-04-03 14:30:00',
'YYYY-MM-DD HH24:MI:SS') AS TIMESTAMP), 'UTC')
AT TIME ZONE 'GMT' "Greenwich Mean Time"
FROM DUAL;
Greenwich Mean Time
----------------------------------------------------------
03-APR-18 02.30.00.000000 PM GMT
You can get the list of available timezones with:
SELECT tzname, tzabbrev FROM V$TIMEZONE_NAMES;
If you have a table YOUR_TABLE with a column some_timestamp:
create table YOUR_TABLE (
some_timestamp timestamp
);
/
insert into YOUR_TABLE (
SOME_TIMESTAMP
) values (
CAST(TO_DATE('2018-04-03 14:30:00', 'YYYY-MM-DD HH24:MI:SS') AS TIMESTAMP)
);
Then you can run:
select
SOME_TIMESTAMP,
FROM_TZ(SOME_TIMESTAMP, 'UTC') AT TIME ZONE 'GMT' "Greenwich Mean Time",
FROM_TZ(SOME_TIMESTAMP, 'UTC') AT TIME ZONE 'Europe/London' "London Time"
from YOUR_TABLE;
SOME_TIMESTAMP Greenwich Mean Time London Time
----------------------------------------------------------
03-APR-18 02.30.00.000000 PM 03-APR-18 02.30.00.000000 PM GMT 03-APR-18 03.30.00.000000 PM EUROPE/LONDON
And if you are absolutely sure that your server is in UTC, as well as the timestamps introduced, then you can skip that UTC conversion part:
select
SOME_TIMESTAMP,
SOME_TIMESTAMP AT TIME ZONE 'GMT' "Greenwich Mean Time",
SOME_TIMESTAMP AT TIME ZONE 'Europe/London' "London Time"
from YOUR_TABLE;
it should be pretty simple::
select datetimecolumn + INTERVAL '1' HOUR from mytable
hope i understand your requirement correctly.

Unix Timestamp to ISO-8601 String

I have an Oracle table called ACQDATA with a field READDATETIME where I store a Unix timestamp in milliseconds as an INTEGER (NUMBER(38)) type.
SQL> select READDATETIME from ACQDATA where ID=1000;
READDATETIME
____________
1.4793E+12
I need to select that value as a ISO-8601 string (YYYY-MM-DDTHH:MM:SS.mmm):
SQL> select READDATETIME from ACQDATA where ID=1000;
READDATETIME
-------------------
1.4793E+12
I´ve tried to convert it using TO_CHAR, but the result is messy:
SQL> select TO_CHAR(TO_DATE('1970-01-01','YYYY-MM-DD') + NUMTODSINTERVAL(READDATETIME, 'SECOND'), 'YYYY-MM-DD HH24:MI:SS') from ACQDATA where ID=1000;
Error at line 1:
ORA-01873: the leading precision of the interval is too small
Help appreciated.
Alex's answer is not fully correct. Unix timestamp is always based on 1970-01-01 00:00:00 UTC
Unless your session runs on UTC time zone the precise solution would be like this:
select
TO_CHAR((TIMESTAMP '1970-01-01 00:00:00 UTC' + readdatetime/1000 * INTERVAL '1' SECOND) AT LOCAL, 'YYYY-MM-DD"T"HH24:MI:SS.FF3')
from ACQDATA where ID=1000;
or
select
TO_CHAR((TIMESTAMP '1970-01-01 00:00:00' AT TIME ZONE 'UTC' + readdatetime/1000 * INTERVAL '1' SECOND) AT LOCAL, 'YYYY-MM-DD"T"HH24:MI:SS.FF3')
from ACQDATA where ID=1000;
or if you prefer functions instead of literals:
select
TO_CHAR((TO_TIMESTAMP_TZ('1970-01-01 00:00:00 UTC', 'YYYY-MM-DD HH24:MI:SS TZR') + numtodsinterval(readdatetime/1000, 'SECOND')) AT LOCAL, 'YYYY-MM-DD"T"HH24:MI:SS.FF3')
from ACQDATA where ID=1000;
Your readdatetime seems to be in milliseconds. Oracle date arithmetic works on the basis of days, so you need to convert that number to the number of days it represents; one day is 86400 seconds, so it's 86400000 milliseconds:
with acqdata (id, readdatetime) as (
select 1000, 1479318995000 from dual
)
select to_char(date '1970-01-01' + (READDATETIME/86400000), 'YYYY-MM-DD"T"HH24:MI:SS')
from ACQDATA where ID=1000;
TO_CHAR(DATE'1970-0
-------------------
2016-11-16T17:56:35
The T is added as a character literal.
SQL Developer defaults to show numbers that large in scientific notation. You can change that default with set numformat, or use to_char() to show the whole value:
select readdatetime, to_char(readdatetime, '9999999999999') as string
from ACQDATA where ID=1000;
READDATETIME STRING
------------ --------------
1.4793E+12 1479318995000
If your value has fractional seconds, so the last three digits are not zeros, you can convert the date to a timestamp and add on the fractional leftovers; this also adds the UTC 'Z' indicator for fun:
with acqdata (id, readdatetime) as (
select 1000, 1479300462063 from dual
)
select to_char(cast(date '1970-01-01' + (readdatetime/86400000) as timestamp)
+ numtodsinterval(remainder(readdatetime, 1000)/1000, 'SECOND'),
'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')
from acqdata where id=1000;
TO_CHAR(CAST(DATE'1970-01-01'+
------------------------------
2016-11-16T12:47:42.063Z
Or without the intermediate date value, starting from a timestamp literal:
with acqdata (id, readdatetime) as (
select 1000, 1479300462063 from dual
)
select to_char(timestamp '1970-01-01 00:00:00'
+ numtodsinterval(readdatetime/1000, 'SECOND'),
'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"')
from acqdata where id=1000;
TO_CHAR(TIMESTAMP'1970-0
------------------------
2016-11-16T12:47:42.063Z
As #Wernfried ponted out, it's better to explicitly show that the epoch time is starting from UTC:
alter session set time_zone='America/New_York';
with acqdata (readdatetime) as (
select 1479300462063 from dual
union all select 1467331200000 from dual
union all select 1467648000000 from dual
)
select readdatetime,
to_char(timestamp '1970-01-01 00:00:00' + numtodsinterval(readdatetime/1000, 'SECOND'),
'YYYY-MM-DD"T"HH24:MI:SS.FF3') as implicit,
to_char(cast(timestamp '1970-01-01 00:00:00' as timestamp with time zone)
+ numtodsinterval(readdatetime/1000, 'SECOND'),
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM') as local_offset,
to_char(timestamp '1970-01-01 00:00:00 UTC' + numtodsinterval(readdatetime/1000, 'SECOND'),
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM') as utc_offset,
to_char(timestamp '1970-01-01 00:00:00 UTC' + numtodsinterval(readdatetime/1000, 'SECOND'),
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZR') as utc
from acqdata;
READDATETIME IMPLICIT LOCAL_OFFSET UTC_OFFSET UTC
-------------- ----------------------- ----------------------------- ----------------------------- --------------------------
1479300462063 2016-11-16T12:47:42.063 2016-11-16T12:47:42.063-05:00 2016-11-16T12:47:42.063+00:00 2016-11-16T12:47:42.063UTC
1467331200000 2016-07-01T00:00:00.000 2016-07-01T01:00:00.000-04:00 2016-07-01T00:00:00.000+00:00 2016-07-01T00:00:00.000UTC
1467648000000 2016-07-04T16:00:00.000 2016-07-04T17:00:00.000-04:00 2016-07-04T16:00:00.000+00:00 2016-07-04T16:00:00.000UTC

Previous Date getting fetched in pacific time zone in oracle

In my code when I save one date as todays date in Eastern Time zone, It gets displayed correctly when we view it.But In Pacific time zone, It is showing previous date ie of yesterdays date. We are storing date in Oracle as a date field.
Any special coding required to fecth corect date in Pacific time?
Dates do not have a time zone - so when you store dates from different time zone you need to make sure that you convert them to be in the same time zone.
CREATE TABLE table_name( id INT, value DATE );
INSERT INTO table_name VALUES( 1, TIMESTAMP '2016-07-07 23:00:00 PST' AT TIME ZONE 'UTC' );
INSERT INTO table_name VALUES( 1, TIMESTAMP '2016-07-07 23:00:00 EST' AT TIME ZONE 'UTC' );
Then:
SELECT id,
value AS utc,
CAST(
FROM_TZ( CAST( value AS TIMESTAMP ), 'UTC' ) AT TIME ZONE 'EST'
AS DATE
) AS EST,
CAST(
FROM_TZ( CAST( value AS TIMESTAMP ), 'UTC' ) AT TIME ZONE 'PST'
AS DATE
) AS PST
FROM table_name t;
Outputs:
ID UTC EST PST
-- ------------------- ------------------- -------------------
1 2016-07-08 06:00:00 2016-07-08 01:00:00 2016-07-07 23:00:00
2 2016-07-08 04:00:00 2016-07-07 23:00:00 2016-07-07 21:00:00

oracle convert unix epoch time to date

The context is that there is an existing application in our product which generates and sends the EPOCH number to an existing oracle procedure & vice versa. It works in that procedure using something like this
SELECT UTC_TO_DATE (1463533832) FROM DUAL
SELECT date_to_utc(creation_date) FROM mytable
When I tried these queries it does work for me as well with Oracle 10g server (and oracle sql developer 4.x if that matters).
In the existing procedure the requirement was to save the value as date itself (time component was irrelevant), however in the new requirement I have to convert unix EPOCH value to datetime (at the hours/mins/seconds level, or better in a specific format such as dd-MMM-yyyy hh:mm:ss) in an oracle query. Strangely I am unable to find any documentation around the UTC_TO_DATE and DATE_TO_UTC functions with Google. I have looked around at all different questions on stackoverflow, but most of them are specific to programming languages such as php, java etc.
Bottom line, how to convert EPOCH to that level of time using these functions (or any other functions) in Oracle query? Additionally are those functions I am referring could be custom or specific somewhere, as I don't see any documentation or reference to this.
To convert from milliseconds from epoch (assume epoch is Jan 1st 1970):
select to_date('19700101', 'YYYYMMDD') + ( 1 / 24 / 60 / 60 / 1000) * 1322629200000
from dual;
11/30/2011 5:00:00 AM
To convert that date back to milliseconds:
select (to_date('11/30/2011 05:00:00', 'MM/DD/YYYY HH24:MI:SS') - to_date('19700101', 'YYYYMMDD')) * 24 * 60 * 60 * 1000
from dual;
1322629200000
If its seconds instead of milliseconds, just omit the 1000 part of the equation:
select to_date('19700101', 'YYYYMMDD') + ( 1 / 24 / 60 / 60 ) * 1322629200
from dual;
select (to_date('11/30/2011 05:00:00', 'MM/DD/YYYY HH24:MI:SS') - to_date('19700101', 'YYYYMMDD')) * 24 * 60 * 60
from dual;
Hope that helps.
Another option is to use an interval type:
SELECT TO_TIMESTAMP('1970-01-01 00:00:00.0'
,'YYYY-MM-DD HH24:MI:SS.FF'
) + NUMTODSINTERVAL(1493963084212/1000, 'SECOND')
FROM dual;
It has this advantage that milliseconds won't be cut.
If your epoch time is stored as an integer.....
And you desire the conversion to Oracle date format.
Step 1-->
Add your epoch date (1462086000) to standard 01-jan-1970. 86400 is seconds in a 24 hour period.
*Select TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 from dual*
**output is 5/1/2016 7:00:00 AM**
Step 2--> Convert it to a CHAR . This is needed for formatting before additional functions can be applied.
*Select TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss') from dual*
output is 2016-05-01 07:00:00
Step 3--> Now onto Timestamp conversion
Select to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss') from dual
output is 5/1/2016 7:00:00.000000000 AM
Step 4--> Now need the TimeZone, usage of UTC
Select from_tz(to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss'),'UTC') from dual
output is 5/1/2016 7:00:00.000000000 AM +00:00
Step 5--> If your timezone need is PST
Select from_tz(to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss'),'UTC') at time zone 'America/Los_Angeles' TZ from dual
output is 5/1/2016 12:00:00.000000000 AM -07:00
Step 6--> Format the PST Timezone timestamp.
Select to_Char(from_tz(to_timestamp(TO_CHAR(TO_DATE('01-jan-1970', 'dd-mon-yyyy') + 1462086000/86400 ,'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss'),'UTC') at time zone 'America/Los_Angeles' ,'DD-MON-YYYY HH24:MI:SS') TZ from dual
output is 01-MAY-2016 00:00:00
Step 7--> And finally, if your column is date datatype
Add to_DATE to the whole above Select.
Here it is for both UTC/GMT and EST;
GMT select (to_date('1970-01-01 00','yyyy-mm-dd hh24') +
(1519232926891)/1000/60/60/24) from dual;
EST select new_time(to_date('1970-01-01 00','yyyy-mm-dd hh24') +
(1519232926891)/1000/60/60/24, 'GMT', 'EST') from dual;
I thought somebody would be interested in seeing an Oracle function version of this:
CREATE OR REPLACE FUNCTION unix_to_date(unix_sec NUMBER)
RETURN date
IS
ret_date DATE;
BEGIN
ret_date:=TO_DATE('19700101','YYYYMMDD')+( 1/ 24/ 60/ 60)*unix_sec;
RETURN ret_date;
END;
/
I had a bunch of records I needed dates for so I updated my table with:
update bobfirst set entered=unix_to_date(1500000000+a);
where a is a number between 1 and 10,000,000.
A shorter method to convert timestamp to nanoseconds.
SELECT (EXTRACT(DAY FROM (
SYSTIMESTAMP --Replace line with desired timestamp --Maximum value: TIMESTAMP '3871-04-29 10:39:59.999999999 UTC'
- TIMESTAMP '1970-01-01 00:00:00 UTC') * 24 * 60) * 60 + EXTRACT(SECOND FROM
SYSTIMESTAMP --Replace line with desired timestamp
)) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to timestamp.
SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' + numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS TIMESTAMP FROM dual;
TIMESTAMP
26/08/20 09:33:47,263027000 UTC
As expected, above methods' results are not affected by time zones.
A shorter method to convert interval to nanoseconds.
SELECT (EXTRACT(DAY FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval --Maximum value: INTERVAL '+694444 10:39:59.999999999' DAY(6) TO SECOND(9) or up to 3871 year
) * 24 * 60) * 60 + EXTRACT(SECOND FROM (
INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval
))) * 1000000000 AS NANOS FROM DUAL;
NANOS
1598434427263027000
A method to convert nanoseconds to interval.
SELECT numtodsinterval(
1598434427263027000 --Replace line with desired nanoseconds
/ 1000000000, 'SECOND') AS INTERVAL FROM dual;
INTERVAL
+18500 09:33:47.263027
As expected, millis, micros and nanos are converted and reverted, dispite of SYSTIMESTAMP doesn't have nanosecounds information.
Replace 1000000000 by 1000, for example, if you'd like to work with milliseconds instead of nanoseconds.
I've tried some of posted methods, but almost of them are affected by the time zone or result on data loss after revertion, so I've decided do post the methods that works for me.

Resources