SQL date diff无视夜晚

时间:2011-11-01 09:09:56

标签: sql oracle plsql date-arithmetic

使用Oracle数据库,我需要知道任何两个日期之间的差异(以秒为单位),忽略从下午6点到早上8点的间隔。

因此,例如,01.11.2011 1pm和03.11.2011 1pm之间的差异相当于20小时(或分别为72000秒)。我还需要忽略周末和假期,但我已经有了PLSQL功能。

提前谢谢!

此致  亚历


修改
与此同时,我自己提出了一个解决方案,老实说,这个解决方案并不是很复杂,但确实如此:

CREATE OR REPLACE FUNCTION "GET_WORKTIME" 
(
  startdate_in in date,
  enddate_in   in date
)
RETURN NUMBER
AS
  days_between number(4);
  duration number;
  end_of_first_day date;
  start_of_last_day date;
  startdate date;
  enddate date;
  weekday number(1);
  temp_date date;
  holidays day_array;
  is_holiday boolean;
BEGIN
  duration     := 0;
  startdate    := startdate_in;
  enddate      := enddate_in;

IF (startdate IS NULL OR enddate IS NULL OR startdate >= enddate) THEN
  RETURN 0;
END IF;

days_between := trunc(enddate) - trunc(startdate);

end_of_first_day  := to_date(concat(to_char(startdate, 'dd.mm.yyyy'), ' 18:00:00'), 'dd.mm.yyyy hh24:mi:ss');
start_of_last_day := to_date(concat(to_char(enddate,   'dd.mm.yyyy'), ' 08:00:00'), 'dd.mm.yyyy hh24:mi:ss');

temp_date := startdate;

FOR i IN 0..days_between LOOP
    -- if it is the first day, just calculate the time until the end of that day
    IF i = 0 THEN
        duration := duration + greatest((end_of_first_day - startdate), 0) * 24 * 3600;

    -- if it is the last day, just calculate the time until that point in time
    ELSIF i = days_between THEN
        duration := duration + greatest((enddate - start_of_last_day),  0) * 24 * 3600;            

    -- for every other day, simply add 10 hours (6pm - 8am) if it is neither
    -- saturday or sunday nor a holiday
    ELSE
        weekday    := to_number(to_char(temp_date, 'D'));
        is_holiday := false;

        -- weekend?
        IF (NOT weekday IN (6,7)) THEN

            -- holiday?
            FOR j IN extract(year FROM startdate)..extract(year FROM enddate) LOOP
                holidays := get_holidays_for_year(j);

                FOR k IN 1..holidays.count LOOP
                    IF (holidays(k) = trunc(temp_date)) THEN
                        is_holiday := true;
                    END IF;
                END LOOP;
            END LOOP;

            -- add 10 hours to the duration
            IF (NOT is_holiday) THEN
                duration := duration + 10 * 3600;
            END IF;
        END IF;
    END IF;

    temp_date := temp_date + 1;
END LOOP;
RETURN duration;
END;

该功能还忽略了周末和假期,因此它看起来比实际更全面。谢谢你的帮助!

此致  亚历

2 个答案:

答案 0 :(得分:2)

将此分为3部分可能最容易:

  1. 第一个部分日(如果有的话)
  2. 最后一个部分日(如果有的话)
  3. 介入整天(如果有的话)
  4. 第一个部分日是从(开始时间,上午8点)的后期(第一天的last_date,下午6点)开始,所以我们减去那些以获得第一天的时间:

    greatest ( least ( trunc(first_date)+18/24
                            , last_date
                            ) - greatest( first_date
                                        , trunc(first_date)+8/24
                                        )
                    , 0) * 24 as last_day_hours
    

    外部greatest功能可确保我们不会获得负值,例如如果开始时间是在下午6点之后。

    如果最后一天与第一天不同,那么最后一个部分日是从最后一天的上午8点到(结束时间):

       case when trunc(last_date) != trunc(first_date) 
            then greatest ( least( last_date
                                 , trunc(last_date)+18/24
                                 )
                            - (trunc(last_date)+8/24)
                          , 0) * 24
            else 0 end as last_day_hours
    

    整整几天(以小时为单位):

       greatest ( (trunc(last_date) - (trunc(first_date)+1))
                , 0
                ) * 10 as intervening_days_in_hours
    

    10是上午8点到下午6点之间的小时数

    因此,将这3个值加在一起得出该时段的总小时数,并将总数乘以3600得出秒数:

    (
       (greatest ( least ( trunc(first_date)+18/24
                        , last_date
                        ) - greatest( first_date
                                    , trunc(first_date)+8/24
                                    )
                , 0) * 24)
     + case when trunc(last_date) != trunc(first_date) 
            then greatest ( least( last_date
                                 , trunc(last_date)+18/24
                                 )
                            - (trunc(last_date)+8/24)
                          , 0) * 24
            else 0 end 
     + (greatest ( (trunc(last_date) - (trunc(first_date)+1))
                , 0
                ) * 10)
    ) * 3600 as total_seconds
    

    我不禁觉得它应该比这更容易!当然,这并不需要考虑周末。

答案 1 :(得分:0)

在Oracle中,您只需减去两个日期即可。 您可以通过将结果除以(24 * 60 * 60)

来计算秒数
select (last_date - first_date) / (24*60*60) as seconds from table

格式化差异的一个技巧是:

select to_char(trunc(sysdate) + (last_date - first_date), 'hh24:mi:ss') as time_between from table