views:

95

answers:

3

Hi all,

following problem is bothering me and I can't find any reasonable explanation and a solution. Perhaps someone could enlighten me.

I have an application that calculates date difference (in days) - in a Search mask and in a Details mask. In the first mask I use days to filter out records with a duration search criteria (Search mask) and I do this with an SQL query:

WHERE...
...
AND DECODE(TRUNC(to_number(SUBSTR((close_date - 
create_date),1,instr(close_date - create_date,' ')))), NULL, 
TRUNC(to_number(SUBSTR((systimestamp - create_date),1,instr(systimestamp - 
create_date,' ')))), TRUNC(to_number(SUBSTR((close_date - 
create_date),1,instr(close_date - create_date,' ')))) ) >=140

AND DECODE(TRUNC(to_number(SUBSTR((close_date - 
create_date),1,instr(close_date - create_date,' ')))), NULL, 
TRUNC(to_number(SUBSTR((systimestamp - create_date),1,instr(systimestamp - 
create_date,' ')))), TRUNC(to_number(SUBSTR((close_date - 
create_date),1,instr(close_date - create_date,' ')))) ) <=140

In this special case I try to find out all the records that have a duration of 140 days.

In the second mask (Details) I show the record details, including its duration. This is done with the following java code:

public static Integer getDuration(Date caseDate, Date closeDate) {
    Date beginDate = caseDate;
    Date endDate = (closeDate != null)? closeDate: new Date();

    long difference = endDate.getTime() - beginDate.getTime();   
    int daysDiff = (int) (difference / (24.0 * 60 * 60 * 1000));  
    return new Integer(daysDiff);
}

My problem is that when I search, I find some records that do correspond to the search criteria. For instance I find 4 records and all of them have a duration of 140 days. That's according to Oracle. But when I open the Details mask for some of them I get a duration of 139 days for example. So somehow Java and Oracle calculate date differences in a different way. It seems that in Oracle some rounding is being made, but I can't find where this happens. So any suggestions would be helpful. Thanks!

Greets, almaak

+4  A: 

The date could be the same but the time could be different. Resulting in 139 days when you calculate through milliseconds. (java)

I suggest not using millis but use the days to calculate.

Something like

public long daysBetween(Calendar startDate, Calendar endDate) {
   Calendar date = (Calendar) startDate.clone();
   long daysBetween = 0;
   while (date.before(endDate)) {
      date.add(Calendar.DAY_OF_MONTH, 1);
      daysBetween++;
   }}

or

/**
     *
     * @param c1 from
     * @param c2 to
     * @return amount of days between from and to
     */
    public int diff(Calendar c1, Calendar c2) {
        int years = c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
        if (years == 0) {
            return c2.get(Calendar.DAY_OF_YEAR) - c1.get(Calendar.DAY_OF_YEAR);
        } else {
            Calendar endOfYear =  Calendar.getInstance();
            endOfYear.set(Calendar.YEAR, c1.get(Calendar.YEAR));
            endOfYear.set(Calendar.MONTH, 11);
            endOfYear.set(Calendar.DAY_OF_MONTH, 31);
            int days = endOfYear.get(Calendar.DAY_OF_YEAR) - c1.get(Calendar.DAY_OF_YEAR);
            for (int i=1;i <years;i++) {
                endOfYear.add(Calendar.YEAR, 1);
                days += endOfYear.get(Calendar.DAY_OF_YEAR);
            }
            days += c2.get(Calendar.DAY_OF_YEAR);
            return days;
        }
    }

Side note: The first example is slightly slower then the second, but if it's only for small differences it's neglectible.

Redlab
Do you suggest some rounding in Java?
al
+1  A: 

I calculated this same way but I used dayDiff as long not as integer. So try it also, don't cast it. It should work fine.

Xorty
I didn't quite understand your comment. How would that affect "difference"?
al
it would not, only if the result of the calculation was higher then Integer.MAX_VALUE
Redlab
well I am not sure. I just checked my sources and it worked that way :OAnyway, Calendar solution wins!
Xorty
I haven't tried the Calendar solution yet. What I've tried however is to put the days into double in Java. The result was - 139.95833333333334. So what happens, in my opinion is, that the casting is to blame. What do you think?
al
Have you tried it with long as I suggested?
Xorty
yep. didn't work.
al
those milliseconds are just treacherous :D But still, 139.9583 isn't 140. So java shouldn't be wrong, Oracle should be. Or give a try to that Calendar solution, it's much cleaner and you'll see the results.
Xorty
The calendar solution seems to be working.
al
Yep, I'd use it myself
Xorty
I have a curious problem now, though. Now I get records in the result list, that has a duration of 141 days. Stupid time! :-/ It's getting round up somewhere.
al
+1  A: 

The problem here is that you think both columns are DATE columns, where at least one of the two is really a TIMESTAMP column. When you extract one date from another, you get a NUMBER. But when you extract a date from a timestamp, or vice versa, you get an INTERVAL.

An example

A table with a DATE and a TIMESTAMP:

SQL> create table mytable (close_date,create_date)
  2  as
  3  select date '2010-11-01', systimestamp from dual union all
  4  select date '2010-11-11', systimestamp from dual union all
  5  select date '2010-12-01', systimestamp from dual
  6  /

Table created.

SQL> desc mytable
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 CLOSE_DATE                                         DATE
 CREATE_DATE                                        TIMESTAMP(6) WITH TIME ZONE

Extract a TIMESTAMP from a DATE column, leads to an INTERVAL:

SQL> select close_date - create_date
  2    from mytable
  3  /

CLOSE_DATE-CREATE_DATE
---------------------------------------------------------------------------
+000000130 11:20:11.672623
+000000140 11:20:11.672623
+000000160 11:20:11.672623

3 rows selected.

And there is no need to fiddle with TO_NUMBER's and SUBSTR's. Just use the EXTRACT function to get the component you want from an interval:

SQL> select extract(day from (close_date - create_date))
  2    from mytable
  3  /

EXTRACT(DAYFROM(CLOSE_DATE-CREATE_DATE))
----------------------------------------
                                     130
                                     140
                                     160

3 rows selected.

Regards, Rob.

And here is an example with two TIMESTAMPS and which shows that INTERVAL's are truncated, not rounded:

SQL> create table mytable (close_date,create_date)
  2  as
  3  select to_timestamp('2010-11-01','yyyy-mm-dd'), trunc(systimestamp,'dd') + interval '6' hour from dual union all
  4  select to_timestamp('2010-11-11','yyyy-mm-dd'), trunc(systimestamp,'dd') + interval '12' hour from dual union all
  5  select to_timestamp('2010-12-01','yyyy-mm-dd'), trunc(systimestamp,'dd') + interval '18' hour from dual
  6  /

Table created.

SQL> desc mytable
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 CLOSE_DATE                                         TIMESTAMP(9)
 CREATE_DATE                                        DATE

SQL> select close_date - create_date
  2    from mytable
  3  /

CLOSE_DATE-CREATE_DATE
---------------------------------------------------------------------------
+000000130 18:00:00.000000000
+000000140 12:00:00.000000000
+000000160 06:00:00.000000000

3 rows selected.

SQL> select extract(day from (close_date - create_date))
  2    from mytable
  3  /

EXTRACT(DAYFROM(CLOSE_DATE-CREATE_DATE))
----------------------------------------
                                     130
                                     140
                                     160

3 rows selected.
Rob van Wijk
I've checked and both are timestamp columns - close_date and create_date. I've also used the extract function. That didn't affect the result. What I suspect is that Oracle makes some rounding of the days.
al
I just checked that with two TIMESTAMPS, the result is still the same. Oracle doesn't round the days, it truncates it when using the EXTRACT function. See the updated answer.
Rob van Wijk
Yep, that was also my result yesterday. and that seems to be also the problem now, because the Calender solution seems to be rounding the days up. So now I get results with 141 days, when I search with <=140 and =>140.
al