简体   繁体   中英

Date difference in date in Oracle and Java

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

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM