TSQL函数用于查找考虑周末和假日的两个日期之间的差异

时间:2010-08-17 14:52:12

标签: sql-server tsql datetime

Tomalak对现有的SO问题发表了出色的回应:

SQL DateDiff advanced usage?

它几乎对我有用,但我需要计算两个日期之间的营业时间差异,不包括周末,即使不到一周的时间。我的解决方案添加了一个while循环(可能有点天真,对建议开放!)并在假日查找表中添加假期检查。

修改 我不是在寻找传统意义上的答案,只是想轻松回到它并打开它来批评未来可能遇到它的人。

ALTER FUNCTION dbo.udfDateDiffBusinessHours (
  @date1 DATETIME,
  @date2 DATETIME
) RETURNS DATETIME AS
BEGIN
  DECLARE @sat INT
  DECLARE @sun INT
  DECLARE @workday_s INT
  DECLARE @workday_e INT
  DECLARE @basedate1 DATETIME
  DECLARE @basedate2 DATETIME
  DECLARE @calcdate1 DATETIME
  DECLARE @calcdate2 DATETIME
  DECLARE @iteratordate DATETIME
  DECLARE @cworkdays INT
  DECLARE @coffdays INT
  DECLARE @returnval INT

  SET @workday_s = 480 -- work day start:  8 hours
  SET @workday_e = 1080 -- work day end:   18 hours

    -- calculate Saturday and Sunday dependent on SET DATEFIRST option
  SET @sat = CASE @@DATEFIRST WHEN 7 THEN 7 ELSE 7 - @@DATEFIRST END 
  SET @sun = CASE @@DATEFIRST WHEN 7 THEN 1 ELSE @sat + 1 END 

  SET @calcdate1 = @date1
  SET @calcdate2 = @date2

  -- @date1: assume next day if start was after end of workday
  SET @basedate1 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate1))
  SET @calcdate1 = CASE WHEN DATEDIFF(mi, @basedate1, @calcdate1) > @workday_e
                   THEN @basedate1 + 1
                   ELSE @calcdate1
                   END

  -- @date1: if Saturday or Sunday, make it next Monday
  SET @basedate1 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate1))
  SET @calcdate1 = CASE DATEPART(dw, @basedate1)
                   WHEN @sat THEN @basedate1 + 2
                   WHEN @sun THEN @basedate1 + 1
                   ELSE @calcdate1
                   END

  -- @date1: assume @workday_s as the minimum start time
  SET @basedate1 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate1))
  SET @calcdate1 = CASE WHEN DATEDIFF(mi, @basedate1, @calcdate1) < @workday_s 
                   THEN DATEADD(mi, @workday_s, @basedate1)
                   ELSE @calcdate1
                   END

  -- @date2: assume previous day if end was before start of workday
  SET @basedate2 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate2))
  SET @calcdate2 = CASE WHEN DATEDIFF(mi, @basedate2, @calcdate2) < @workday_s
                   THEN DATEADD(mi, @workday_e, @basedate2 - 1)
                   ELSE @calcdate2
                   END

  -- @date2: if Saturday or Sunday, make it previous Friday
  SET @basedate2 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate2))
  SET @calcdate2 = CASE DATEPART(dw, @calcdate2)
                   WHEN @sat THEN @basedate2 - 0.00001
                   WHEN @sun THEN @basedate2 - 1.00001
                   ELSE @date2
                   END

  -- @date2: assume @workday_e as the maximum end time
  SET @basedate2 = DATEADD(dd, 0, DATEDIFF(dd, 0, @calcdate2))
  SET @calcdate2 = CASE WHEN DATEDIFF(mi, @basedate2, @calcdate2) > @workday_e
                   THEN DATEADD(mi, @workday_e, @basedate2)
                   ELSE @calcdate2
                   END

  -- count full work days (subtract Saturdays, Sundays and holidays)
  SET @cworkdays = DATEDIFF(dd, @basedate1, @basedate2)
  SET @iteratordate = @basedate1
  SET @coffdays = 0

  WHILE DATEDIFF(dd, @iteratordate, @basedate2) > 0
  BEGIN
    IF DATEPART(dw, @iteratordate) = @sat OR DATEPART(dw, @iteratordate) = @sun OR EXISTS (SELECT holidaydate FROM dbo.holidays_lu (NOLOCK) WHERE holidaydate = @iteratordate)
        SET @coffdays = @coffdays + 1
    SET @iteratordate = DATEADD(dd, 1, @iteratordate)
  END
  SET @cworkdays = @cworkdays - @coffdays

  -- calculate effective duration in minutes
  SET @returnval = @cworkdays * (@workday_e - @workday_s)
                   + @workday_e - DATEDIFF(mi, @basedate1, @calcdate1) 
                   + DATEDIFF(mi, @basedate2, @calcdate2) - @workday_e

  -- return duration as an offset in minutes from date 0
  RETURN DATEADD(mi, @returnval, 0)
END

1 个答案:

答案 0 :(得分:1)

在互联网上搜索时,我遇到了这个解决方案:

How do I count the number of business days between two dates?

它使用工作日历表,您可以根据需要调整工作日(包括节假日)。

计算营业时间将查询:

  • 开始日期和结束日期之间的工作日,不包括开始日期和结束日期 *(工作时间/日
  • +开始日的工作时间(如果是工作日)
  • +结束日的工作时间(如果是工作日)

也许可以将营业时间编码到工作日历表中,但我还没有尝试过。