Convert to datetime to specified format - sql

I have been searching around for a while now, but I can't seem to find the answer to this small problem.
How to convert string 08-JUL-06 to datetime 08/07/1906? I've tried with code like the following:
select to_char(to_date('08-jul-06', 'dd-mon-rr'), 'dd/mm/yyyy') result from dual;
However, the result shows wrong: to be 08/07/2006? How do I solve the problem?

RR format is not suitable for this(*). How would Oracle know that you meant 1900s not 2000s? You know it because you know the context in which they occur. I suggest converting it to text and add 19 in front of the year yourself, like:
select to_date('19'||to_char(to_date('08-jul-06', 'dd-mon-yy'), 'yymmdd'),'yyyymmdd') result
from dual;
It is now a date, you can view it in the format you mentioned using to_char(...'dd-mon-yyyy'); or it might readily display like that if your Oracle installation date-format is that.
(*) If the specified two-digit year is 00 to 49, then
If the last two digits of the current year are 00 to 49, then the returned year has the same first two digits as the current year.
If the last two digits of the current year are 50 to 99, then the first 2 digits of the returned year are 1 greater than the first 2 digits of the current year.
If the specified two-digit year is 50 to 99, then
If the last two digits of the current year are 00 to 49, then the first 2 digits of the returned year are 1 less than the first 2 digits of the current year.
If the last two digits of the current year are 50 to 99, then the returned year has the same first two digits as the current year.

How to convert string 08-JUL-06 to datetime 08/07/1906?
You cannot, Oracle has two format models for handling two-digit years: YY and RR.
YY will assume the century is the current century.
RR will assume the century is:
The previous century if the specified year is 50-99 and the current year is 0-49
The current century if the specified year is 50-99 and the current year is 50-99
The current century if the specified year is 0-49 and the current year is 0-49
The next century if the specified year is 0-49 and the current year is 50-99
In none of these options will 06 assume that the full 4-digit year is 1906.
What you need to do is use a 4-digit year:
SELECT TO_CHAR(DATE '1906-07-08', 'dd/mm/yyyy') AS result FROM DUAL;
or, change your string to insert the century:
SELECT TO_CHAR(
TO_DATE(
SUBSTR(value, 1, 7) || '19' || SUBSTR(value, 8),
'DD-MON-YYYY',
'NLS_DATE_LANGUAGE=English'
),
'dd/mm/yyyy'
) AS result
FROM (SELECT '08-JUL-06' AS value FROM DUAL);
Which both output:
RESULT
08/07/1906
db<>fiddle here

Related

HOW TO FETCH DATA BETWEEN 2 DATES IN ORACLE SQL DEVELOPER

I'm new to oracle sql . I want to fetch data between 2 dates .
Date is in this format in db : 13-DEC-10
This is the query I have written but its giving me error . How to proceed next
select sum(TOTAL_AMOUNT) from table a
where trn_date between
TO_DATE(01-APR-17, 'DD-MON-YYYY') AND TO_DATE(31-MAR-17, 'DD-MON-YYYY') ;
A date does not have a format - it is stored internally to the database as 7-bytes (representing year, month, day, hour, minute and second) and it is not until whatever user interface you are using (i.e. SQL/Plus, SQL Developer, Java, etc) tries to display it to you, the user, and converts it into something you would find meaningful (usually a string) that the date has a format.
To fix your query you just need to surround the date string in single quotes and to use YY to match the 2-digit year format (otherwise Oracle will assume that 17 in the format YYYY is the year 0017 and the century will not be as you expect):
select sum(TOTAL_AMOUNT)
from table a
where trn_date between TO_DATE('01-APR-17', 'DD-MON-YY')
AND TO_DATE('31-MAR-17', 'DD-MON-YY');
However, you can also use date literals (and skip having to match the date format model):
select sum(TOTAL_AMOUNT)
from table a
where trn_date between DATE '2017-04-01'
AND DATE '2017-05-31';
Alternatively you may use the year format of RR format against centurial problems, Don't forget to keep quotes for date values, and you may prefer calling sql with bind variables :
select sum(TOTAL_AMOUNT)
from table a
where trn_date between
TO_DATE('&date_1', 'DD-MON-RR') AND TO_DATE('&date_2', 'DD-MON-RR') ; -- where date_1 is 31-MAR-17 and date_2 is 01-APR-17, in your case.
What I mentioned by centurial problems :
The RR Datetime Format Element
The RR datetime format element is similar to the YY datetime format
element, but it provides additional flexibility for storing date
values in other centuries. The RR datetime format element lets you
store 20th century dates in the 21st century by specifying only the
last two digits of the year.
If you use the TO_DATE function with the YY datetime format element,
then the year returned always has the same first 2 digits as the
current year. If you use the RR datetime format element instead, then
the century of the return value varies according to the specified
two-digit year and the last two digits of the current year.
That is:
If the specified two-digit year is 00 to 49, then
If the last two digits of the current year are 00 to 49, then the
returned year has the same first two digits as the current year.
If the last two digits of the current year are 50 to 99, then the
first 2 digits of the returned year are 1 greater than the first 2
digits of the current year.
If the specified two-digit year is 50 to 99, then
If the last two digits of the current year are 00 to 49, then the
first 2 digits of the returned year are 1 less than the first 2 digits
of the current year.
If the last two digits of the current year are 50 to 99, then the
returned year has the same first two digits as the current year.

What will be the output datatype of calculating difference in hours between two dates?

I want to know the datatype of below code.... number or date?
select round(24*(sysdate - to_date('18-09-18 06:30','YYYY-MM-DD HH24 Mi'))) as Hours_Diff
from Dual;
[TL;DR] Subtracting one DATE from another gives the number of days difference as a number. Multiplying by 24 and rounding it does not change the data type.
(and subtracting one TIMESTAMP from another give the difference as a INTERVAL DAY TO SECOND data type)
Longer answer:
SELECT DUMP( round(24*(sysdate - to_date('18-09-18 06:30','YYYY-MM-DD HH24 Mi'))) )
FROM DUAL
(Note: to_date('18-09-18 06:30','YYYY-MM-DD HH24 Mi') will give a year of 0018 not 2018!)
Outputs:
Typ=2, Len=5: 196, 18, 53, 30, 33
Typ=2 signifies a number.

Oracle date conversion outputs 2000s instead of required 1990s

During execution of below query, the output is 2090.
select to_char(to_date('10-OCT-90', 'DD-MON-YY'), 'YYYY') from dual;
Required output is 1990, the date in the snippet is Birth Date.
EDIT:
The input date is fetched from a table, so the date is in DD-MON-YY format.
For alternative resolving the double-digit years in strings, Oracle has the RR format element. Thus, your query would be
select to_char(to_date('10-OCT-90', 'DD-MON-RR'), 'YYYY') from dual;
From the referenced Oracle doc:
The RR datetime format element is similar to the YY datetime format element, but it provides additional flexibility for storing date values in other centuries. The RR datetime format element lets you store 20th century dates in the 21st century by specifying only the last two digits of the year.
Use RR instead of YY. See here: https://docs.oracle.com/cd/B28359_01/server.111/b28286/sql_elements004.htm#SQLRF00215
Try to use RR like this:
select to_char(to_date('10-OCT-90', 'DD-MON-RR'), 'YYYY') from dual;
SQL FIDDLE DEMO
Also note that you should store the dates in YYYY format as it will be confusing to make the correct assumption as to the birth year is in 2000 or 1900 or 1800 or... ;)
The Oracle docs says:
YY allows you to retrieve just two digits of a year, for example, the
99 in 1999. The other digits (19) are automatically assigned to the
current century. RR converts two-digit years into four-digit years by
rounding.
50-99 are stored as 1950-1999, and dates ending in 00-49 are stored as
2000-2049. RRRR accepts a four-digit input (although not required),
and converts two-digit dates as RR does. YYYY accepts 4-digit inputs
butdoesn't do any date converting
I had the same issue for my Date column (DOB) if DOB is 27-08-1947. I use below conversion format to get correct year to calculate age.
TO_DATE(TO_CHAR(DOB,'DD-MON-YYYY'), 'DD-MON-YYYY')
TO_DATE(TO_CHAR(DOB,'DD-MM-YYYY'), 'DD-MM-YYYY')
SELECT TRUNC(SYSDATE) TODAY,
TO_DATE(BIRTH_DATE,'DD-MON-RR') DOB,
BIRTH_DATE
FROM TM_DM_CLIENT
WHERE CLIENT_NO IN ('22943179')
output
Today DOB BIRTH_DATE
10/1/2019 1/1/2041 1/1/1941

How to extract week number in sql

I have a transdate column of varchar2 type which has the following entrees
01/02/2012
01/03/2012
etc.
I converted it in to date format in another column using to_date function. This is the format i got.
01-JAN-2012
03-APR-2012
When I'm trying to extract the weekno, i'm getting all null values.
select to_char(to_date(TRANSDATE), 'w') as weekno from tablename.
null
null
How to get weekno from date in the above format?
After converting your varchar2 date to a true date datatype, then convert back to varchar2 with the desired mask:
to_char(to_date('01/02/2012','MM/DD/YYYY'),'WW')
If you want the week number in a number datatype, you can wrap the statement in to_number():
to_number(to_char(to_date('01/02/2012','MM/DD/YYYY'),'WW'))
However, you have several week number options to consider:
WW Week of year (1-53) where week 1 starts on the first day of the year and continues to the seventh day of the year.
W Week of month (1-5) where week 1 starts on the first day of the month and ends on the seventh.
IW Week of year (1-52 or 1-53) based on the ISO standard.
Try to replace 'w' for 'iw'.
For example:
SELECT to_char(to_date(TRANSDATE, 'dd-mm-yyyy'), 'iw') as weeknumber from YOUR_TABLE;
Select last_name, round (sysdate-hire_date)/7,0) as tuner
from employees
Where department_id = 90
order by last_name;
Use 'dd-mon-yyyy' if you are using the 2nd date format specified in your answer. Ex:
to_date(<column name>,'dd-mon-yyyy')

Oracle to_date function with quarter-format

I need to find some records created in a range of quarters. For example, I'm looking for all records created between the 4th quarter of 2008 and the 1st quarter of 2010. I have this in my WHERE-clause:
...and r.record_create_date between to_date('2008 4','YYYY Q')
and to_date('2010 1','YYYY Q')
but Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened. Is this even a valid way to find values in between calender quarters, or is there a better way?
Also interesting, and possibly related, if I execute this:
select to_date('2009','YYYY') from dual;
The value displayed in my IDE is 2009-08-01. I would have expected 2009-08-04, since today is 2010-08-04.
This:
select to_date('2009 1','YYYY Q') from dual;
of course, fails.
(Oracle 10g)
Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened.
See the second column of table 2.15 at http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34948. Not all format elements are allowed when converting to dates, timestamps, etc.
I recommend against using between for date range checks. People often will miss values within the ending day that the expect to be included. So I would translate:
and r.record_create_date between to_date('2008 4','YYYY Q')
and to_date('2010 1','YYYY Q')
To
and to_date('2008-10-01', 'YYYY-MM-DD') <= r.record_create_date
and record_create_date < to_date('2010-04-01', 'YYYY-MM-DD') -- < beginning of 2Q2010.
Someone asked the same question on OTN: http://forums.oracle.com/forums/thread.jspa?threadID=1081398&tstart=255
The crux of the issue is that you can not specify "Q" in the TO_DATE function.
Given that you're already specifying a portion of the date, why not provide the entire date? Mind too that to_date('2010 1','YYYY Q') would give you Jan 1st, 2010 when you really want March 31st, 2010... at a second to midnight.
Since the relationship between quarters to months is one-to-many, it doesn't make sense to do TO_DATE('2008 1', 'yyyy q'); what date should be returned? The first of the quarter, the end of the quarter, ...? (On the other hand, converting a date to a quarter - like TO_CHAR(SYSDATE, 'yyyy q') makes sense because a specific date only exists in one quarter.)
So, if you do want a query that looks for a date that falls between two quarters, you will have to "rolll your own" (explicitly stating the dates of the start/end of a quarter.)
As a side note, in case anyone is considering not using TO_DATE please do not use things like: WHERE date_value BETWEEN 'date string1' and 'date string2' without the TO_DATE function. It assumes a default date format and under certain situations can avoid potentially useful indexes altogether.
Below is one example where the same query can have a different result.
select sysdate from dual where sysdate between '1-Jan-10' and '31-Dec-10';
SYSDATE
---------
04-AUG-10
SQL> alter session set nls_date_format = 'YYYY-MM-DD';
Session altered.
SQL> select * from dual where sysdate between '1-Jan-10' and '31-Dec-10';
no rows selected
(Notice that in the second instance no error is returned. It just assumes Jan 10, 0001 and Dec. 10th, 0031.)
I think the best way is to just input the quarter start date and quarter end dates without even bothering with to_date. I think if you use
between '1-Jan-10' and '31-Dec-10'
for example, then you don't (in Oracle I believe) need to_date and it isn't much more difficult than typing in the quarter number
To calculate in Oracle the first day of a quarter and the last day of a quarter from the year and quarter:
I Use the fact
start_month= -2 + 3 * quarter
last_month = 3 * quarter
variable v_year number
variable v_quarter number
exec :v_year :=2017
exec :v_quarter:=4
select :v_year as year,
:v_quarter as quarter,
to_date(:v_year||to_char(-2+3*:v_quarter,'fm00'),'yyyymm') as quarter_start,
last_day(to_date(:v_year||to_char(3*:v_quarter,'fm00')||'01 23:59:59','yyyymmdd hh24:mi:ss')) as quarter_end
from dual a;
YEAR|QUARTER|QUARTER_START |QUARTER_END
2017| 4|2017-10-01 00:00:00|2017-12-31 23:59:59