ORACLE Procedure with TIMESTAMP - oracle

I try to write a procedure to know the datetime from a timezone to another timezone
I can't compile because the format date is not valid and I don't know how to manage the variable.
Can you give me a tip, please
create or replace PROCEDURE DATETIME_FROM_TO
(
VALUE$DATETIME in date,
VALUE$FROM in varchar2,
VALUE$TO in varchar2,
FLAG in number
) AS c1 SYS_REFCURSOR;
begin
VALUE_DATETIME := VALUE$DATETIME;
VALUE_FROM := VALUE$FROM;
VALUE_TO := VALUE$TO;
if FLAG=12 then
open c1 for
select to_char(from_tz(TIMESTAMP 'VALUE_DATETIME', 'VALUE$FROM') at time zone 'VALUE$TO','DD/MM/YYYY HH:MI PM') as localtime into c1 from dual;
else
open c1 for
select to_char(from_tz(TIMESTAMP 'VALUE$DATETIME', 'VALUE$FROM') at time zone 'VALUE$TO','DD/MM/YYYY HH24:MI') as localtime into c1 from dual;
end if;
DBMS_SQL.RETURN_RESULT(c1);
close c1;
end;
thank you

I'm not sure why you want to go down the hugely complicated route of using a cursor and returning a string in a result set.
Just write a simple function that takes a TIMESTAMP argument (if you pass it a DATE it will be implicitly cast to a TIMESTAMP) and if you want to format it as a string then you can call TO_CHAR on the return value as required.
CREATE FUNCTION change_timezone(
i_datetime TIMESTAMP,
i_from_tz VARCHAR2,
i_to_tz VARCHAR2
) RETURN TIMESTAMP WITH TIME ZONE DETERMINISTIC
IS
BEGIN
RETURN FROM_TZ( i_datetime, i_from_tz ) AT TIME ZONE i_to_tz;
END;
/
Then you can call it using:
SELECT change_timezone( DATE '1970-01-01', 'UTC', 'Europe/Berlin' )
FROM DUAL;
Which outputs (depending on your NLS_TIMESTAMP_TZ_FORMAT setting):
| EPOCH_TIME_IN_GERMANY |
| :-------------------------------- |
| 1970-01-01 01:00:00.000000000 CET |
and if you want it with a particular format then:
SELECT TO_CHAR(
change_timezone( DATE '1970-01-01', 'UTC', 'Europe/Berlin' ),
'DD/MM/YYYY HH12:MI PM'
) AS formatted_epoch_time_in_germany
FROM DUAL;
Which outputs:
| FORMATTED_EPOCH_TIME_IN_GERMANY |
| :------------------------------ |
| 01/01/1970 01:00 AM |
If you really want to incorporate the string conversion (don't, do the conversion outside the function) then you could use:
CREATE FUNCTION format_as_timezone(
i_datetime TIMESTAMP,
i_from_tz VARCHAR2,
i_to_tz VARCHAR2,
i_use_24hr NUMBER DEFAULT 1
) RETURN VARCHAR2 DETERMINISTIC
IS
BEGIN
RETURN TO_CHAR(
FROM_TZ( i_datetime, i_from_tz ) AT TIME ZONE i_to_tz,
CASE i_use_24hr
WHEN 1
THEN 'DD/MM/YYYY HH24:MI'
ELSE 'DD/MM/YYYY HH12:MI AM'
END
);
END;
/
db<>fiddle here

Related

How to convert string in 'MMDD' format into a date using the current year

I have this procedure that goes PROCEDURE(monthday varchar2). It receives an varchar2 that represents the month and date concatenated always with the format of MMDD. I then want to create a DATE type variable that uses this month and day, and the year being the current year.
Like: desired_date DATE;
desired_date = ?
I'm using Oracle SQL Developer.
EXTRACT the year from SYSDATE and then combine using string concatenation with your input and use TO_DATE to convert to a date:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
END;
/
then:
DECLARE
dt DATE;
BEGIN
test( '0101', dt );
DBMS_OUTPUT.PUT_LINE( '0101: ' || dt );
test( '1231', dt );
DBMS_OUTPUT.PUT_LINE( '1231: ' || dt );
BEGIN
test( '9876', dt );
DBMS_OUTPUT.PUT_LINE( '9876: ' || dt );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( '9876: ' || SQLERRM );
END;
END;
/
outputs:
0101: 2019-01-01 00:00:00
1231: 2019-12-31 00:00:00
9876: ORA-01843: not a valid month
db<>fiddle here
If you want to return NULL for invalid inputs then:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
EXCEPTION
WHEN OTHERS THEN
-- In general don't catch OTHERS but in this case the only exceptions
-- are going to be from TO_DATE
desired_date := NULL;
END;
/
db<>fiddle here
Update:
You could simplify the code (as suggested by Aleksej and William Robertson) by not specifying the year value in TO_DATE and use the default which appears to be the current year; however this behaviour is not, obviously, documented in any Oracle documentation pages so I would also include inline documentation within the function so future developers reviewing your function know that you are deliberately using this behaviour:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
-- Assumes that TO_DATE will, when not specified, default the year to the
-- current year and the time to midnight.
desired_date := TO_DATE( monthday, 'MMDD' );
END;
/
This could be a way
desired_date := to_date(monthday, 'MMDD');
According to this old post in AskTom, to_date should use the current year, if not given:
default year = current year
default month = current month
default day = 1
default hour = 0
default minute = 0
default second = 0
ops$tkyte%ORA10GR2> select sysdate, to_date( ' ', ' ' ) from dual;
SYSDATE TO_DATE('','')
-------------------- --------------------
17-aug-2012 13:41:06 01-aug-2012 00:00:00
Still unable to find this information in Oracle Docs

How to extract date of particular format in Oracle? [duplicate]

This question already has answers here:
how to extract date format from a string oracle
(1 answer)
Check DATE format in Oracle
(3 answers)
Closed 4 years ago.
I have this table where VARCHAR2(128 CHAR) field could be of the format 2018-01-01 00:00:00 or 01/01/2018 00:00:00 or 01-JAN-2018 00:00:00
How can I pull records that has the VARCHAR2(128 CHAR) field in 2016-01-01 00:00:00 or 01-JAN-2018 00:00:00 format alone?
Not good storing a date in a varchar. But as it is like this, I would propose converting the string to a date and then back to string again. Like so:
declare
d date;
begin
begin
d:=to_date('01.01.2011 23:49:00','dd/mm/yyyy hh24:mi:ss');
exception
when others then
begin
d:=to_date('01.01.2011 23:49:00','dd.mm.yyyy hh24:mi:ss');
exception
when others then
begin
d:=to_date('01.01.2011 23:49:00','dd-mm-yyyy hh24:mi:ss');
exception
when others then
-- any more date format strings?
null;
end;
end;
end;
dbms_output.put_line(to_char(d,'dd-mm-yyyy hh24:mi:ss'));
end;
/
Create a function that converts a date string to date:
create or replace function date_from_string(str varchar2, fmt varchar2) return date is
v date;
begin
return to_date(str,fmt);
exception when others
then return null;
end;
And query the table, for example:
with t as (
select case when regexp_like(field, '^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d$')
then 'YYYY-MM-DD HH24:MI:SS'
when regexp_like(field, '^\d\d-[a-zA-Z]{3}-\d{4} \d\d:\d\d:\d\d$')
then 'DD-MON-YYYY HH24:MI:SS'
end fmt, t.*
from "TABLE" t
)
select t.*, date_from_string(t.field, t.fmt) "DATE" from t
where date_from_string(t.field, t.fmt) is not null;
This way you can easily check that the date in a given record is correct.

How to pass current month and year in input parameters in pl sql?

I'm trying to pass the month and year in a plsql as input parameters. I want the month and year to be the default values as of current month and current year if i'm passing null values.
A DATE should be passed as a date and not in parts. You could EXTRACT the requirement elements as and when required.
Pass the SYSDATE as DEFAULT IN parameter:
p_date DATE default SYSDATE
And then EXTRACT the elements:
For example,
SQL> SELECT SYSDATE ,
2 EXTRACT(YEAR FROM SYSDATE) AS curr_year ,
3 EXTRACT(MONTH FROM SYSDATE) AS curr_month ,
4 EXTRACT(DAY FROM SYSDATE) AS curr_day
5 FROM dual;
SYSDATE CURR_YEAR CURR_MONTH CURR_DAY
--------- ---------- ---------- ----------
03-NOV-15 2015 11 3
Depending on your requirement, you could use TO_CHAR, however, the data type of the result will be string.
For example,
SQL> SELECT SYSDATE ,
2 TO_CHAR(SYSDATE, 'YYYY') AS curr_year ,
3 TO_CHAR(SYSDATE, 'MM') AS curr_month ,
4 TO_CHAR(SYSDATE, 'DD') AS curr_day
5 FROM dual;
SYSDATE CURR_YEAR CURR_MONTH CURR_DAY
--------- --------- ---------- --------
03-NOV-15 2015 11 03
First variant -
CREATE OR REPLACE PROCEDURE test_poc(
year_num IN NUMBER DEFAULT to_number(to_char(sysdate, 'yyyy')),
month_num IN NUMBER DEFAULT to_number(to_char(sysdate, 'mm'))) IS
BEGIN
....
END;
Second (is better, with a date in input parameter) -
CREATE OR REPLACE PROCEDURE test_poc(date_in IN DATE) IS
year_num NUMBER;
month_num NUMBER;
BEGIN
year_num := to_number(to_char(nvl(date_in, sysdate), 'yyyy'));
month_num := to_number(to_char(nvl(date_in, sysdate), 'mm'));
...
END;
You can define a default for your parameters as suggested in the other answers. But those default values will only take effect when you dont pass anything as parameter. Giving null as parameter value will not trigger the default value. For example
create or replace
function TEST_FN( p_month in number default extract( MONTH from sysdate ) )
return varchar2
as
begin
return 'month='||nvl( p_month, -1);
end;
A select TEST_FN() from dual; will result in month=11 for november, and selecting select TEST_FN(null) from dual; gives month=-1.
If you want to replace a null with some default value use nvl(...).
create or replace procedure p1(my_year int default null, my_month integer default null)
as
lyear int := nvl(my_year, to_char(sysdate,'yyyy'));
lmonth int := nvl(my_month, to_char(sysdate,'mm'));
begin
dbms_output.put_line(lyear);
dbms_output.put_line(lmonth);
end;
test
begin
p1;
p1(2010);
p1(null,12);
p1(2009,10);
end;
output
2015
11
2010
11
2015
12
2009
10

Arithmetic operations on Date values in Oracle

I am struggling with some case...
there is table, in which I have employee attendance records, for example:
for empID=1;
empID time Type date
-------------------------------
1 9:22 in sameday
1 11:23 out sameday
1 14:35 in sameday
1 16:21 out sameday
particularly, I want some fn/procedure that will take an EmpID and DATE parameters, and then based on this data if I'll write: select proc(EmployeeID, Date) from dual(or maybe some other table?) it should do such a work:
take first couples in table (table is ordered be ascending as default order), then calculate FROM first OUT (11:23) to first IN(9:22) time, save that time somewhere (int tempResult) and then calculate second couple, and calculate second tempResult and in finalResult, it should count the total time, like finalResult+=finalResult+tempResult (if it has been an iterations in loop);
I think it would be done someway like, in foreach (or whatever it is in pl/sql oracle) take first select with top result, then, second.. and so forth... and on each iteration calculate desire goal.
so.. logics is ok with me I think :), but the problem is that I'm not that familiar with PL/SQL, if it had been written in Java it should have come easy to me.
I will pay lots of Thanks to some one who will help me...
its crucial for me to day.
thanks in advance.
I have Date and Time is separate columns, like:
date time
----------------------
11-09-2013 12:34
so, I made little change like this
FOR rec IN
( SELECT t.EID, to_char(t.devent_date, 'DD.MM.YY') ||' '|| t.RegTime, t.acttype from turnicate_ie t WHERE t.EID = i_emp_id ORDER BY t.EID
)
LOOP
but it states that package or function is in incorrect state...
(t.devent_date is 11.09.2013, t.RegTime is 16:23)
The below will give you some idea of using plsql:
you need to use many logic of calculating total working hours, like multiple inputs within same time, multiple empId etc.
create table my_test ( empId number, log_time date, type varchar2(3));
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 09:22:00 am', 'dd/mon/yyyy hh:mi:ss am'), 'in');
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 11:23:00 am', 'dd/mon/yyyy hh:mi:ss am'), 'out');
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 02:35:00 pm', 'dd/mon/yyyy hh:mi:ss pm'), 'in');
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 04:21:00 pm', 'dd/mon/yyyy hh:mi:ss pm'), 'out');
CREATE OR REPLACE
FUNCTION total_hours(
i_emp_id IN NUMBER)
RETURN VARCHAR2
IS
l_total_seconds NUMBER := 0;
in_time DATE;
l_total_time VARCHAR2(20);
BEGIN
FOR rec IN
( SELECT log_time, type FROM my_test WHERE empid = i_emp_id ORDER BY log_time
)
LOOP
IF rec.TYPE = 'in' AND in_time IS NULL THEN
in_time := rec.log_time;
END IF;
IF rec.TYPE = 'out' AND in_time IS NOT NULL THEN
l_total_seconds := (rec.log_time - in_time)*24*60*60 + l_total_seconds;
in_time := NULL;
END IF;
END LOOP;
SELECT TO_CHAR(TRUNC(l_total_seconds/3600), 'FM999999990')
|| 'hh '
|| TO_CHAR(TRUNC(mod(l_total_seconds,3600)/60), 'FM00')
|| 'mm '
|| TO_CHAR(mod(l_total_seconds,60), 'FM00')
||'ss'
INTO l_total_time
FROM dual;
RETURN l_total_time;
END;
/
SELECT total_hours(1) from dual;

Pl sql date initialization comparsion ++

I have requirement where i have to get data of particular day. So my ideal startdate should be 2013-06-07 00:00:01 AM and end date 2013-06-07 23:59:59 AM
Hence i have written this code.
create or replace
PROCEDURE checkChanges
IS
vc_startDate timestamp;
vc_endDate timestamp;
begin
vc_startDate :=to_timestamp(TO_CHAR(trunc(systimestamp)-40+((24*60*60)-1)/(24*60*60),'yyyy-mm-dd hh24:mi:ss'),'yyyy-mm-dd hh24:mi:ss');
vc_endDate :=to_timestamp(TO_CHAR(trunc(systimestamp)+1/(24*60*60),'yyyy-mm-dd hh24:mi:ss'),'yyyy-mm-dd hh24:mi:ss');
Dbms_Output.Put_Line('vc_startDate ' ||vc_startDate);
Dbms_Output.Put_Line('vc_endDate ' ||vc_endDate);
SELECT EMAIL_ADRESS FROM SOMETABLE A,B
AND A.CREATE_TS BETWEEN vc_startDate AND vc_endDate ORDER BY B.START_DT;
end checkChanges;
But the start date and end date i am getting is quite different.
start date:07-JUN-13 12.00.01.000000 AM
end date: 07-JUN-13 11.59.59.000000 PM
Here's a simple way to do this.
DECLARE
v_start TIMESTAMP;
v_end TIMESTAMP;
BEGIN
v_start := TRUNC (SYSTIMESTAMP) + NUMTODSINTERVAL (1, 'second'); --truncate the timestamp and add one second
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_start, 'yyyy-mm-dd hh24:mi:ss'));
/*alternate way
v_start := TRUNC (SYSTIMESTAMP) + INTERVAL '0 0:0:1' DAY TO SECOND; --truncate the timestamp and add one second
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_start, 'yyyy-mm-dd hh24:mi:ss AM'));*/
v_end :=
TRUNC (SYSTIMESTAMP) --trunacate the timestamp
+ INTERVAL '1 0:0:0.0' DAY TO SECOND --add a day
- NUMTODSINTERVAL (1, 'second'); --substract a second
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_end, 'yyyy-mm-dd hh24:mi:ss'));
/*alternate way
v_end := TRUNC (SYSTIMESTAMP) --trunacate the timestamp
+ INTERVAL '0 23:59:59' DAY TO SECOND; --add hours, mins, s
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_end, 'yyyy-mm-dd hh24:mi:ss AM'));*/
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE (SQLERRM);
END;
Output:
2013-06-07 00:00:01 AM
2013-06-07 23:59:59 PM
UPDATE
I also printed the values without converting to to_char and it displayed similar to what u got.
DBMS_OUTPUT.PUT_LINE (v_start);
DBMS_OUTPUT.PUT_LINE (v_end);
Output:
07-JUN-13 12.00.01.000000 AM
07-JUN-13 11.59.59.000000 PM
So, it seems what you are doing is correct(but, little complicated). When displaying the timestamp it is getting displayed according to the NLS parameters. Check that using
SELECT *
FROM nls_session_parameters
WHERE parameter = 'NLS_TIMESTAMP_FORMAT';
I guess it will return this DD-MON-RR HH.MI.SSXFF AM
Do not worry about how it is being displayed. A date/timestamp variable has no format. It matters only when you want to print it. If don't use to_char function, it'll take the format mask as defined in NLS parameters. If you want to override it, use to_char function and specify the mask.But when used in the query, it'll have the correct value.

Resources