I am working in Oracle RightNow and I was using the below to calculate the Workdays between two dates but with the new year, the logic seems to no longer work. This would be the code used in a custom report column for a client.
((DATE_DIFF(sysdate(),incidents.created)/86400) + 1) - ((to_number(date_format(sysdate(),'WW')) - to_number(date_format(incidents.created,'WW')))* 2) - IF(Date_format(incidents.created,'DAY') = 'Sunday',1,0) - IF(Date_format(sysdate(),'DAY') = 'Saturday',1,0)
Is there different code I could use to accomplish this without it breaking with a new new year?
Thanks,
B
You can use this Oracle answer:
SELECT ( TRUNC( SYSDATE, 'IW' ) - TRUNC( created, 'IW' ) ) * 5 / 7
+ LEAST( SYSDATE - TRUNC( SYSDATE, 'IW' ) + 1, 5 )
- LEAST( created - TRUNC( created, 'IW' ) + 1, 5 )
AS WeekDaysDifference
FROM incidents;
Which, for the sample data:
CREATE TABLE incidents ( created ) AS
SELECT DATE '2020-12-31' FROM DUAL UNION ALL
SELECT DATE '2021-01-01' FROM DUAL UNION ALL
SELECT SYSDATE FROM DUAL
Outputs:
| WEEKDAYSDIFFERENCE |
| ---------------------------------------: |
| 2.83034722222222222222222222222222222222 |
| 1.83034722222222222222222222222222222222 |
| 0 |
db<>fiddle here
Looking at the Oracle RightNow CRM documentation, you can convert the Oracle solution to:
DATE_DIFF(
DATE_TRUNC( SYSDATE(), 'IWEEKS' ),
DATE_TRUNC( incidents.created, 'IWEEKS' )
) / 86400 * 5 / 7
+
IF (
DATE_DIFF(SYSDATE(), DATE_TRUNC(SYSDATE(), 'IWEEKS')) / 86400 < 5,
DATE_DIFF(SYSDATE(), DATE_TRUNC(SYSDATE(), 'IWEEKS')) / 86400,
5
)
-
IF (
DATE_DIFF(incidents.created, DATE_TRUNC(incidents.created, 'IWEEKS')) / 86400 < 5,
DATE_DIFF(incidents.created, DATE_TRUNC(incidents.created, 'IWEEKS')) / 86400,
5
)
(Note: I do not have access to an Oracle RightNow environment so the conversion is untested and based solely on the documentation and there may be errors or possible improvements; however, it is hopefully sufficient to give you the solution.)
Oracle B2C Service (F.K.A. RightNow) doesn't use Oracle DB currently. And, the syntax for the analytics tool is unique to the application, not the underlying DB. So, the suggestions regarding a SQL-based solution won't work.
Your logic appears to be impacted by your use of the transformation of dates using WW in the date_format function, which is the week number for the date in the first parameter of the function. This resets to 1 at the new year, which is why you're likely getting the behavior that you are; you're not accounting for the significance of multiple years in the equation.
Does the organization that you built the report for have working hours setup in B2C Service? If so, then you might be able to simplify your formula with rel_date_diff, which might handle some of the weekday/weekend logic for you. You could try to "brute force" the equation with a date_diff, then calculate the weeks of the diff, then multiply that times 5 workdays per week. But, you'll get anomalies based on what you consider to be the first day of the week and when the report is run. Use the documentation to find appropriate functions to help you.
The last option, but one that will work in the event you cannot develop an equation using the out-of-the-box functions in analytics, is to build an analytics report extension. Your custom code for the extension could implement the date processing logic however you require (in C# for the .NET console and JavaScript for the BUI -- hopefully your client has moved to BUI).
Related
I get some data through a OSB Proxy Service and have to transform that using Xquery. Earlier the transformation was done on the database but now it is to be done on the proxy itself. So I have been given the SQL queries which were used and have to generate Xquery expressions corresponding to those.
Here is the SQL query which is supposed to find the difference between 2 dates.
SELECT ROUND((CAST(DATEATTRIBUTE2 AS DATE) -
CAST(DATEATTRIBUTE1 AS DATE) ) * 86400 ) AS result
FROM SONY_TEST_TABLE;
DATEATTRIBUTE1 and DATEATTRIBUTE2 are both of TIMESTAMP type.
As per my understanding this query first casts the TIMESTAMP to DATE so that the time part is stripped then subtracts the dates. That difference in days in multiplied with 86400 to get the duration in seconds.
However, when I take DATEATTRIBUTE2 as 23-02-17 01:17:19.399000000 AM and DATEATTRIBUTE1 as 23-02-17 01:17:18.755000000 AM the result should ideally be 0 as the dates are same and i'm ignoring the time difference but surprisingly the result comes as 1. After checking I found that the ( CAST(DATEATTRIBUTE2 AS DATE) - CAST(DATEATTRIBUTE1 AS DATE) ) part aparently does not give an integer value but a fractional one. How does this work?? o_O
Any help is appreciated. Cheers!
EDIT : So got the problem thanks to all the answers! Even after casting to DATE it still has time so the time difference is also calculated. Now how do I implement this in XQuery? See this other question.
Oracle DATE datatype is actually a datetime. So casting something as a date doesn't remove the time element. To do that we need to truncate the value:
( trunc(DATEATTRIBUTE2) - trunc(DATEATTRIBUTE1) )
you should try this to find difference by day
SELECT (trunc(DATEATTRIBUTE2) -
trunc(DATEATTRIBUTE1) ) AS result
FROM SONY_TEST_TABLE;
alternative 2
you can use extract like below:
SELECT ROUND (
EXTRACT (MINUTE FROM INTERVAL_DIFFERENCE) / (24 * 60)
+ EXTRACT (HOUR FROM INTERVAL_DIFFERENCE) / 24
+ EXTRACT (DAY FROM INTERVAL_DIFFERENCE))
FROM (SELECT ( TO_TIMESTAMP ('23-02-17 01:17:19', 'dd-mm-yy hh24:mi:ss')
- TO_TIMESTAMP ('23-02-17 01:17:17', 'dd-mm-yy hh24:mi:ss'))
INTERVAL_DIFFERENCE
FROM DUAL)
Can someone help me understand the working of Oracle Months_Between Function?
If I query select MONTHS_BETWEEN('02-28-2015', '01-28-2015')
I get an integer value of 1 but if I query
select MONTHS_BETWEEN('02-28-2015', '01-29-2015') I get 0.96.
Refer to the documentation. https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions089.htm
Note - the "31 day month" convention may cause weird results around month-ends. Consider:
select months_between(date '2016-07-02', date '2016-07-01') as one_day,
months_between(date '2016-07-01', date '2016-06-30') as another_day
from dual;
ONE_DAY ANOTHER_DAY
---------- -----------
.032258065 .064516129
1 row selected.
As if June had 31 days. It doesn't, but months_between treats it as though it did.
If you're working with just trying to determine the number of months in a set of months and don't care about the days. I find myself in this situation often... You can do a bit of date manipulation which is rather reliable for determining the number of months in a set of months. Say for instance Jul - Sep while starting with dates.
Thusly:
WITH MONTHS AS (
SELECT
SYSDATE DATE_ONE
, SYSDATE+57 DATE_TWO
FROM DUAL
)
SELECT
m.*
,TO_CHAR(m.DATE_ONE,'MON') START_MONTH
,TO_CHAR(m.DATE_TWO,'MON') END_MONTH
,MONTHS_BETWEEN(m.DATE_TWO,m.DATE_ONE) UNEXPECTED_RESULT
,MONTHS_BETWEEN(LAST_DAY(m.DATE_TWO),LAST_DAY(ADD_MONTHS(m.DATE_ONE,-1))) EXPECTED_RESULT
FROM MONTHS m
;
I have an Oracle database table with 5 digit Julian dates that I need to convert to date time format.
Sample data
Source Actual date
40786 -> 2015-09-01 |
40785 -> 2015-08-31 |
First I tried the following
SELECT to_char(to_date(to_char(40786), 'J'),'DD-MM-YYYY'),
to_char(to_date(to_char(40785), 'J'),'DD-MM-YYYY')
FROM dual;
40786 -> 4601-09-01 |
40785 -> 4601-08-31 |
Since it is wrong I calculated the difference in days (2416481) and formulated the following query
SELECT to_char(to_date(to_char(40786 + 2416481 ), 'J'),'DD-MM-YYYY'),
to_char(to_date(to_char(40785 + 2416481), 'J'),'DD-MM-YYYY')
FROM dual;
40786 -> 2015-09-01 |
40785 -> 2015-08-31 |
It is correct for above two days but the table has a history since 2010. Will the above adjustment hold correct for the full history. i.e. weekends, leap years etc ...
Many thanks.
V
Your problem is that the column is not stored in Julian date. So you can't ask if the conversion will work or not.
It seems that the dates are based on 1.1.1904 (= day zero)
So the conversion is as follows:
select to_date('1904-01-01','yyyy-mm-dd') + 40786 as dt from dual;
DT
----------
01.09.2015
select to_date('1904-01-01','yyyy-mm-dd') + 40785 as dt from dual;
DT
----------
31.08.2015
If it will realy work, can answer only the code in your GUI conversion routine.
And yes, if you trust in rational software development, you could expect it will work (for dates say in +/- 100 years range).
I need a bit of help but I'm very close to achieving what I'm looking for thanks to this post. However, it's not exactly the output that I need.
Using LAG to check for differences in dates, I need LAG to ignore differences in time and consider only dates. In other words, it should consider only calendar days, not 24-hour days. Is there any way to achieve that?
Here's what I got so far:
SELECT EMPLOYEE_ID,
SUM(DIFFERENCE) AS DAYS,
DATE_IN,
DATE_OUT
FROM (
SELECT *
FROM (
SELECT EMPLOYEE_ID, DATE_IN, DATE_OUT, ACTIVE,
NVL(DATE_IN - LAG(DATE_IN) OVER (PARTITION BY EMPLOYEE_ID, ACTIVE ORDER BY DATE_IN), 1) AS DIFFERENCE
FROM MY_TABLE
WHERE MY_TABLE.ACTIVE = 1
AND TO_CHAR(DATE_IN, 'YYYY-MM-DD') >= 'user-selected date'
AND TO_CHAR(DATE_OUT, 'YYYY-MM-DD') <= 'other user-selected date'
)
)
GROUP BY EMPLOYEE_ID, DATE_IN, DATE_OUT
ORDER BY DATE_IN
The ouput I get:
EMPLOYEE_ID | DIFFERENCE | DATE_IN | DATE_OUT
000199 1 2013/11/25 08:41:00 2013/11/25 16:41:00
000199 0.970833333333333 2013/11/26 07:59:00 2013/11/26 15:59:00
000199 1 2013/11/27 07:59:00 2013/11/27 15:59:00
000199 1.00069444444444 2013/11/28 08:00:00 2013/11/28 16:00:00
000199 1 2013/11/29 08:00:00 2013/11/29 16:00:00
What I'm looking for:
Differences here should all be 1, time should be ignored. I've tried TO_CHAR(DATE_IN .......) in LAG to get rid of the time part but of course that didn't work :p
Thanks for your help.
For the sake of Googlers:
TRUNC(DATE_IN) - LAG(TRUNC(DATE_IN)
Is the key to ignoring time and getting a result only for days.
Thanks to a_horse_with_no_name for the answer and for teaching me about TRUNC in the process :)
I have a database of start and stop times that have previously all had fairly recent data (1960s through present day) which i've been able to store as long integers. This is very simialr to unix timestamps, only with millisecond precision, so a function like java.util.Date.getTime() would be the value of the current time.
This has worked well so far, but we recently got data from the 1860s, and the following code no longer works when values that result in times < 1901 (give or take):
to_timestamp('1-JAN-1970 00:00:00', 'dd-mon-yyyy hh24:mi:ss') + numtodsinterval(int_to_convert/(1000),'SECOND' );
trying this with a value in milliseconds such as -2177452800000 causes some issues, such as returning the date with a timestamp in the year 2038. Is there a way around this issue? All of the documentation i've looked at the documentation and timestamps should be able to handle years all the way back to the -4000 (BC), so i'm suspecting an issue with the numtodsinterval.
Any ideas suggestions would be greatly appreciated.
How about something like this :
select to_timestamp('1-JAN-1970 00:00:00', 'dd-mon-yyyy hh24:mi:ss') +
numtodsinterval(val /(1000*60*60*24),'DAY' ) +
numtodsinterval(
((val /(1000*60*60*24)) - (trunc(val /(1000*60*60*24))) ) * 60*60*24,'SECOND')
from (select -2177452812340 val from dual);
Separate out the DAY component and add the whole days, then take the remainder and add that at the higher precision
SELECT to_date('01011970', 'DDMMYYYY')+1586707435919/86400/1000 FROM dual
How about
select to_date('01-JAN-1970','DD-MON-YYYY') + ( -1111111111 / (60 * 60 * 24*1000) ) from dual;
That's what I use to convert Java milliseconds to dates.