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

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;

Related

Convert 17 digit with decimal point timestamp in SQL to date

Trying to convert 43439.961377314816 into date. Currently I am using this code:
SELECT
(timestamp '1970-01-01 00:00:00 GMT' +
numtodsinterval(WRITETIMESTAMP, 'SECOND')) at time zone 'CST',
WRITETIMESTAMP
FROM
t.table
but I am getting this result:
01-JAN-70 06.03.59.961377315 AM CST
Date should be:
12/05/2018
This produces the date that you want:
select date '1899-12-30' + 43439.961377314816
from dual;
It looks like you are using Excel dates or something similar.
You have two problems in your query. First, you used the wrong base time. As pointed out by #GordonLinoff, the base time for an Excel date is actually 1900-01-01, and Excel treats 1900 as a leap year. This is not an error in Excel, per se, but a conscious design decision which was made to copy the (buggy) behavior of Lotus 1-2-3, which did have this bug. So - in Lotus 1-2-3 it's a bug, but in Excel it's a feature. :-) Secondly, in Excel dates the integer portion represents the number of days since the base date, and the fractional portion represent fraction of a day. In your NUMTODSINTERVAL call, however, you specified the interval_unit argument as 'SECOND'; it should have been 'DAY'.
Putting these things together we get
WITH cte AS (SELECT 43439.961377314816 AS WRITETIMESTAMP FROM DUAL)
SELECT
(timestamp '1899-12-30 00:00:00 GMT' + numtodsinterval(WRITETIMESTAMP, 'DAY')) at time zone 'CST',
WRITETIMESTAMP
FROM
cte
dbfiddle here
Best of luck.
This looks like expected behavior to me. 43439 seconds/60/60 = 12 hours and you're getting about 12 hours from the starting timestamp.
SELECT numtodsinterval('43439.961377314816', 'SECOND') as i FROM dual;
I
----------------------
+00 12:03:59.961377315
Why would you think that would give you a date in 2018?
Here is a working formula to put in Excel that works for Chromium browsers.
Chrome/Edge: =((Cell/1000000-11644473600)*1000000)/86400000000+DATE(1970,1,1)

Sysdate Oracle to Run Based on Day

This is probably an easy question for most of you but how can I get this mask to run based on just the day?
If anyone knows Crystal Reports syntax, we have this and it works {PO_RECEIPTS.DATE_RECEIVED} = currentdate
However, when converting to Oracle SQL, how can I the standard: TO_CHAR
(SYSDATE, 'MM-DD-YYYY HH24:MI:SS') to become range so we can selected everything during the day, not just what matched the second in which the report was ran which it never will.
So something like Today from 00:00:00 to 23:59:59 ?
Thank you!
If PO_RECEIPTS.DATE_RECEIVED is a date column where all the times are set to midnight then you can do:
WHERE PO_RECEIPTS.DATE_RECEIVED = TRUNC(sysdate)
If the values have other times then you can use a range:
WHERE PO_RECEIPTS.DATE_RECEIVED >= TRUNC(sysdate)
AND PO_RECEIPTS.DATE_RECEIVED < TRUNC(sysdate) + 1
Truncating a date sets the time to midnight, by default, so TRUNC(sysdate) is midnight this morning. For the range you get all records equal to or later than midnight this morning, and less than midnight tomorrow - which is what TRUNC(sysdate) + 1 gives you, using normal Oracle datetime arithmetic.
You don't really want to convert it to a string with TO_CHAR(); you'd either have to convert all the column values to strings too (which is inefficient and prevents an index being used), or let the string be (implicitly) converted back to a date anyway. It's better to compare a column value with the same data type to reduce or avoid confusion.

How to Round up timestamp to number of Days?

PreparedStatement psnmt=con.prepareStatement("SELECT (?)-(?) as DiffDate FROM dual");
psnmt.setTimestamp(1,ctenderdate);
psnmt.setTimestamp(2,btenderdate);
ResultSet resrt=psnmt.executeQuery();
if(!resrt.next())
{
out.println("No Records Found");
}
else
{
do
{
datediff=resrt.getString("DiffDate");
}
while(resrt.next());
System.out.println("the no of days Difference"+datediff);
}
ctenderdate=2015-06-27 00:00:00.0
btenderdate=2015-06-29 00:00:00.0
datediff=1 10:18:51.940000000
Expected datediff=2
How to round it off datediff to number of days
EDIT
Subtract TIMESTAMP values
If we really want to subtract two TIMESTAMP values, then we have to work with the INTERVAL DAY TO SECOND datatype that's returned. The easiest way to work with that is to use the EXTRACT function.
If want to return integer number of days (emulating the CEIL function) then we could test whether any part of the time (HOUR, MINUTE, SECOND) was non-zero. If they are all zero, we can use just the DAY portion. Otherwise, we have to add 1 to the DAY portion, and return that.
For example:
SELECT EXTRACT(DAY FROM diff.idts)
+ CASE
WHEN EXTRACT(HOUR FROM diff.idts) > 0
OR EXTRACT(MINUTE FROM diff.idts) > 0
OR EXTRACT(SECOND FROM diff.idts) > 0
THEN 1
ELSE 0
END
AS days_diff
FROM ( SELECT ? - ? AS idts FROM dual ) diff
ORIGINAL ANSWER
For Oracle database, you can perform this operation in the database:
SELECT CEIL(TO_DATE(?,'YYYY-MM-DD HH24:MI:SS.F')-TO_DATE(?,'YYYY-MM-DD HH24:MI:SS.F'))
FROM dual
This assumes the bind parameters are passed as strings, in format that matches the format model specified in the TO_DATE function, for example:
'2015-06-27 14:45:21.0'
(I'm assuming Oracle because of the use of the dual table, and because you are using subtraction operation between two dates. You would need a different statement for a different database.)
To unpack that expression a little bit...
The Oracle TO_DATE function converts a character string into an Oracle DATE value. The second argument is the format model, specifies that format of the first argument.
A subtraction operation between two DATE values returns the difference as a number of days (integer days plus fractional days.)
The CEIL function rounds a non-integer value up to the next higher integer.
FOLLOWUP
Q: how to use it with timestamp?
A: A subtraction of two TIMESTAMP values gets returned as an INTERVAL DAY TO SECOND datatype. And I'd prefer to avoid working with that.
In Oracle, when we do a subtraction of two DATE values, we get a decimal number. That's much easier to work with.
And in terms of "rounding" up a difference in days, I'm fine with disregarding fractional seconds.
If I had to pass in TIMESTAMP values, I would convert them to DATE values. The expressions above are already expecting string values, so I would just replace the ? with
TO_CHAR(?,'YYYY-MM-DD HH24:MI:SS')
If I had a requirement to pass in TIMESTAMP datatype, and return integer days difference rounded up, I would use a query like this:
SELECT CEIL( TO_DATE(TO_CHAR( ? ,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')
- TO_DATE(TO_CHAR( ? ,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')
) AS days_diff
FROM dual
Check out this link. There are answers for results in hours or minutes. What you are looking for should be similar.
My bad. I should not post just-a-link-answers. What you can do, as described there is:
SELECT TRUNC (SYSDATE) - TO_DATE ('10/20/2012', 'mm/dd/yyyy') FROM DUAL;
Notice the following details:
ENDDATE - STARTDATE will give you a number that corresponds to the
number of days between the two dates.
If you want the result in hours, multiply by 24; if minutes, multiply
by 24*60 and so forth.
You can also convert the result to an INTERVAL. There are two type of
intervals: NUMTODSINTERVAL(ENDDATE - STARTDATE, 'DAY') or
NUMTOYMINTERVAL(ENDDATE - STARTDATE, 'DAY')

trunc / sysdate... funny syntax need explanation

I believe this outputs the date of Sunday of the current week but I don't know why.
Can someone please break down what's going on here.
SELECT trunc(sysdate+1,'DAY') FROM DUAL;
The TRUNC function will return the starting day of the week when the second parameter is DAY. Today Sysdate returns Thursday, +1 returns Friday etc. So when you add 3 it gives you next Sunday which marks the start of a new week.
Run this to understand what trunc does
SELECT to_char(trunc(sysdate+1, 'DAY'),'dd/mon/yyyy hh:mi:ss') FROM DUAL;
DAY does returns starting day of the week but trunc will also cut off the hours, minutes, seconds of that date. Sysdate will have some hours and minutes, but after trunc it is defaulted to 00.00.00.000
By calling this
trunc(sysdate+1,'DAY')
you may see 16-FEB-14. You can't see the real result because Oracle doesn't display the minutes for you. If you call this
SELECT to_char(sysdate+1,'dd/mon/yyyy hh:mi:ss') FROM DUAL;
you will see all the time details. Trunk takes that off.
In other words, you have 3 effects here - sysdate + 1 - next date, Day - first day of the week, Trunc - hours, minutes, seconds, etc. off
see http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions230.htm
'DAY' truncates the date provided to the starting day of the week.
Note that depending on your locale this can also be Monday instead of Sunday.
sysdate + 1 just adds one day to the current date, this is probably done to make sure if you are sunday you get back the current sunday and not the previous one.
select to_char(sysdate+1,'dd-mon-yy hh24:mi:ss') from dual;----22-feb-14 02:10:03
select to_char(trunc(sysdate+1,'year'),'dd-mon-yy hh24:mi:ss') from dual;-----01-jan-14 00:00:00
select to_char(trunc(sysdate+1,'month'),'dd-mon-yy hh24:mi:ss') from dual; ---01-feb-14 00:00:00
select to_char(trunc(sysdate+1,'day'),'dd-mon-yy hh24:mi:ss') from dual; ---16-feb-14 00:00:00
Compare above queries outputs, hope this will help you.

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