Convert strange 5 digit number to normal Date in Oracle SQL - sql

I have numbers like 42946 in a database column and i want to format it as a normal Date like dd/mm/yyyy.
I was searching a lot but i dont find anything usefull.
I tried to do TO_DATE(<date>,'J') but this doesn't work beacuse i think 42946 is the number of days between today and January 1 of 1900, and this method works only for dates that are between today and January 1 of 4712 BC.
I hope you can help me with this.

i think 42946 is the number of days between today and January 1 of 1900
You mean, number of days since 1st of January 1900?
If so, you don't have to do much - just add that value to date you specified and you'll get the result. Its (result's) datatype is DATE so you can display it any way you want, using the to_char function (or any other option you prefer):
SQL> select date '1900-01-01' + 42946 as result from dual;
RESULT
----------
01.08.2017
SQL>
Example of formatting it:
SQL> select to_char(date '1900-01-01' + 42946, 'dd-mon-yyyy', 'nls_date_language = english') as result from dual;
RESULT
-----------
01-aug-2017
SQL>

Related

What does TO_DATE('235959', 'HH24MISS') mean?

I came across a SQL query with below conditional clause
To_Char(CRTE_TMS, 'YYYYmmddHH24MISS') between To_Char (TO_DATE(:endDtTime,'YYYYmmddHH24MISS')-TO_DATE('235959', 'HH24MISS')) and :endDtTime
My high level understanding is that create time stamp should be between some time before end time and end time.
Not sure what does TO_DATE('235959', 'HH24MISS') mean.
If I run the below query on 5th Feb it returns 1st Feb
SELECT TO_DATE('235959', 'HH24MISS') FROM DUAl
Please help me understand what exactly this condition mean.
TO_DATE('235959', 'HH24MISS') creates a DATE value. Note, in Oracle data type DATE always contains date and time part.
If you don't provide any date value then Oracle defaults it to the first day of current months, so TO_DATE('235959', 'HH24MISS') returns "2018-02-01 23:59:59"
I don't think this condition makes sense:
To_Char(CRTE_TMS, 'YYYYmmddHH24MISS')
between To_Char (TO_DATE(:endDtTime,'YYYYmmddHH24MISS')-TO_DATE('235959', 'HH24MISS'))
and :endDtTime
First, you should compare DATE values, not strings.
I assume TO_DATE(:endDtTime,'YYYYmmddHH24MISS')-TO_DATE('235959', 'HH24MISS')) is wrong. I think you mean TO_DATE(:endDtTime,'YYYYmmddHH24MISS') - 1 + (1/24/60/60)
This will subtract 1 day plus 1 Second (1/24/60/60), i.e. subtract 23:59:59.
Another possibility would be TO_DATE(:endDtTime,'YYYYmmddHH24MISS') - INTERVAL '23:59:59' HOUR TO SECOND.
So, your condition could be
WHERE CRTE_TMS between TO_DATE(:endDtTime,'YYYYmmddHH24MISS') - 1 + (1/24/60/60) AND :endDtTime
This could probably be a comment instead of an answer.. Sorry do not have enough reputation.
HH24 is the 24 hour format of the hours.
235959 is 23 hours 59 minutes 59 second.
In a 12 hour format it means 11:59:59 PM.
The thing you are trying to do is converting date format into character and comparing it with other dates by converting them to character format using To_char. I do not suggest that.
The below would give the first of the month
SELECT TO_DATE('235959', 'HH24MISS') FROM DUAl;
I am not able to understand what you are trying to achieve here.
The below syntax gives in the character format which is the difference between two dates. for example 4 days and 10 hours.
To_Char (TO_DATE(:endDtTime,'YYYYmmddHH24MISS')-TO_DATE('235959', 'HH24MISS'))
and then you are trying to do a comparision like date between (4 days and 10 hours) and :endtime. This is incorrect.
You could use the below to convert to date format.
to_date('01012018 23:59:59','MMDDYYYY HH24:MI:SS')
select case when to_date('01012018 23:59:59','MMDDYYYY HH24:MI:SS') between :begindate and :enddate then 1
else null
from dual;

uisng to_date function still get date format picture ends before converting entire input string error

I have the following code where I want to see if a date is less than a year ago:
select id
from mytable
where id= :p_id
and (to_date(trunc(sysdate), 'yyyy-mm-dd') - to_date(datewhen, 'yyyy-mm-dd')) < 365;
I keep getting the error:
ORA-01830: date format picture ends before converting entire input
string
Looking at other question with the same error on StackOverflow I see the solution usually is to use the to_date function which I am doing so I am unsure why this is occuring. The datewhen field is of type Date.
Do not use to_date() with the columnes of DATE data type. to_date() converts character string to a value of DATE data type. It makes no sense to convert the DATE to DATE. In a first step datewhen column of type DATE will be implicitly converted into a character data type by using the default date format (that's most probably not 'yyyy-mm-dd') and this is the culprit of the ORA-01830 error.
So your statement should look something like this:
select id from mytable where id = :p_id and (trunc(sysdate) - trunc(datewhen)) < 365;
I'd calculate the difference in the months or years instead of days:
... where months_between(sysdate, datewhen) < 12
If your datewhen column is char/varchar formatted as yyyy-mm-dd then you have to do the to_date conversion on datewhen, but not on SYSDATE: it's already a date and doesn't need to be converted.
To filter on a date within the past 365 days, compare it to SYSDATE - 365:
select id
from mytable
where id = :p_id
and to_date(datewhen, 'yyyy-mm-dd') > sysdate - 365;
But a year isn't always 365 days: on leap years it's 366 days. To get a one year ago value that's always correct, subtract an interval of one year from the current date:
select id
from mytable
where id = :p_id
and datewhen > sysdate - interval '1' year;
One more thing: the Oracle DATE type isn't just a date; it's a date and a time. SYSDATE returns the current date and time. Try this query:
select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;
Unless you run this at exactly midnight you'll see a time component as well.
Say your query runs on 2 September 2017 at 10 AM and you're looking for a date within the past year. You'd expect to get the date 3 September 2016, but you wouldn't because at 10 AM SYSDATE is 3 September 2016 at 10:00:00. That's greater than the plain date 3 September 2016, which is 3 September 2016 at 0:00:00, so records with a datewhen of `2016-09-03' won't be included.
To ignore the time component of an Oracle DATE value, use TRUNC. Your final query should look something like this:
select id
from mytable
where id = :p_id
and datewhen > trunc(sysdate) - interval '1' year;
you use TO_DATE function when the value in character format
Syntax
The syntax for the TO_DATE function in Oracle/PLSQL is:
TO_DATE( string1 [, format_mask] [, nls_language] )

Difference of date with SYSDATE - Value give something?

When subtracting today's date like below gives correct output.
select
(TRUNC(to_date('25/05/2016','dd/mm/yyyy'))-TRUNC(to_date('02/01/2016','dd/mm/yyyy')))
from dual;
output : 144 days
When taking todays date ad sysdate below should give 144. but shows some other value? Why?
select
(to_date(SYSDATE,'dd/mm/yyyy'))-(to_date('02/01/2016','dd/mm/yyyy'))
from dual;
output: -730343 (shows some value).
When you do:
to_date(SYSDATE,'dd/mm/yyyy')
you're implicitly converting SYSDATE, which is already a date, to a string - using your NLS_DATE_FORMAT. From the result that seems to be DD-MON-RR. So you're really doing:
to_date(to_char(SYSDATE,'DD-MON-RR'),'dd/mm/yyyy')
The inner part gives you the string '25-MAY-16'. When you convert that back to a date with the yyyy mask you have a two-digit year, 16, which is interpreted as year 0016 rather than 2016. You'd actually get what you expect if you used rrrr instead, but that's a happy side effect, and it'll still break in session with different NLS settings:
select to_date(SYSDATE,'dd/mm/yyyy') as bad_nls,
to_char(to_date(SYSDATE,'dd/mm/yyyy'), 'YYYY-MM-DD') as bad_string,
to_date(SYSDATE,'dd/mm/rrrr') as ok_nls,
to_char(to_date(SYSDATE,'dd/mm/rrrr'), 'YYYY-MM-DD') as ok_string
from dual;
BAD_NLS BAD_STRING OK_NLS OK_STRING
--------- ---------- --------- ----------
25-MAY-16 0016-05-25 25-MAY-16 2016-05-25
Notice that with your current NLS mask and the implicit conversion to a string you can't tell the difference between the first and third result; but it's obvious that it's wrong when shown with a four-digit year in the second and fourth results.
With your implicit conversion you're comparing 0016-05-25 with 2016-01-02, and it is giving you -730343 as that's how many days there are in 2000 years, adjusted for the 144 days you expected the gap to be.
As Praveen already said you don't need to use to_date() for SYSDATE, and if you're trying to set the time portion to midnight you can just truncate it.
select date '2016-05-25' - date '2016-01-02' as diff1,
date '2016-05-25' - date '0016-05-25' as diff2,
date '0016-05-25' - date '2016-01-02' as diff3,
trunc(sysdate) - date '2016-01-02' as diff4
from dual;
DIFF1 DIFF2 DIFF3 DIFF4
---------- ---------- ---------- ----------
144 730487 -730343 144
More generally, though, don't use two-digit years (alas it seems we've already forgotten the lessons of Y2K!), and don't rely on NLS settings.
You don't need to convert a date [sysdate] to date using to_date
Use trunc to strip the time portion of the sysdate.
Try;
select
TRUNC(SYSDATE) - (to_date('02/01/2016','dd/mm/yyyy'))
from dual;

Oracle Julian day of year

how can I select Julian day of year in Oracle database?
I tried:
select to_char(sysdate, 'J') from dual;
Which gives me the number of days since January 1, 4712 BC. But I would need the number of days since 1.1. of current year.
If you check the TO_CHAR (datetime) documentation you get a link to "Format Models" with a comprehensive list of available formats. I guess you want this:
DDD Day of year (1-366)
SELECT TO_CHAR(SYSDATE, 'DDD') from DUAL;
One way would be to use:
select sysdate - trunc(sysdate,'yyyy') from dual
'Trunc' cuts everything except the year and returns 01/01/2014, subtracted by the sysdate returns numbers of days since 1st of january.
Use sql select trunc(sysdate)+1 - trunc(sysdate,'yyyy') from dual. you will get an even number

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