Count work days between two dates

前端 未结 24 2684
失恋的感觉
失恋的感觉 2020-11-22 01:05

How can I calculate the number of work days between two dates in SQL Server?

Monday to Friday and it must be T-SQL.

相关标签:
24条回答
  • 2020-11-22 01:52

    I know this is an old question but I needed a formula for workdays excluding the start date since I have several items and need the days to accumulate correctly.

    None of the non-iterative answers worked for me.

    I used a defintion like

    Number of times midnight to monday, tuesday, wednesday, thursday and friday is passed

    (others might count midnight to saturday instead of monday)

    I ended up with this formula

    SELECT DATEDIFF(day, @StartDate, @EndDate) /* all midnights passed */
         - DATEDIFF(week, @StartDate, @EndDate) /* remove sunday midnights */
         - DATEDIFF(week, DATEADD(day, 1, @StartDate), DATEADD(day, 1, @EndDate)) /* remove saturday midnights */
    
    0 讨论(0)
  • 2020-11-22 01:52

    One approach is to 'walk the dates' from start to finish in conjunction with a case expression which checks if the day is not a Saturday or a Sunday and flagging it(1 for weekday, 0 for weekend). And in the end just sum flags(it would be equal to the count of 1-flags as the other flag is 0) to give you the number of weekdays.

    You can use a GetNums(startNumber,endNumber) type of utility function which generates a series of numbers for 'looping' from start date to end date. Refer http://tsql.solidq.com/SourceCodes/GetNums.txt for an implementation. The logic can also be extended to cater for holidays(say if you have a holidays table)

    declare @date1 as datetime = '19900101'
    declare @date2 as datetime = '19900120'
    
    select  sum(case when DATENAME(DW,currentDate) not in ('Saturday', 'Sunday') then 1 else 0 end) as noOfWorkDays
    from dbo.GetNums(0,DATEDIFF(day,@date1, @date2)-1) as Num
    cross apply (select DATEADD(day,n,@date1)) as Dates(currentDate)
    
    0 讨论(0)
  • 2020-11-22 01:56
     DECLARE @TotalDays INT,@WorkDays INT
     DECLARE @ReducedDayswithEndDate INT
     DECLARE @WeekPart INT
     DECLARE @DatePart INT
    
     SET @TotalDays= DATEDIFF(day, @StartDate, @EndDate) +1
     SELECT @ReducedDayswithEndDate = CASE DATENAME(weekday, @EndDate)
      WHEN 'Saturday' THEN 1
      WHEN 'Sunday' THEN 2
      ELSE 0 END 
     SET @TotalDays=@TotalDays-@ReducedDayswithEndDate
     SET @WeekPart=@TotalDays/7;
     SET @DatePart=@TotalDays%7;
     SET @WorkDays=(@WeekPart*5)+@DatePart
    
     RETURN @WorkDays
    
    0 讨论(0)
  • 2020-11-22 01:57
    CREATE FUNCTION x
    (
        @StartDate DATETIME,
        @EndDate DATETIME
    )
    RETURNS INT
    AS
    BEGIN
        DECLARE @Teller INT
    
        SET @StartDate = DATEADD(dd,1,@StartDate)
    
        SET @Teller = 0
        IF DATEDIFF(dd,@StartDate,@EndDate) <= 0
        BEGIN
            SET @Teller = 0 
        END
        ELSE
        BEGIN
            WHILE
                DATEDIFF(dd,@StartDate,@EndDate) >= 0
            BEGIN
                IF DATEPART(dw,@StartDate) < 6
                BEGIN
                    SET @Teller = @Teller + 1
                END
                SET @StartDate = DATEADD(dd,1,@StartDate)
            END
        END
        RETURN @Teller
    END
    
    0 讨论(0)
  • 2020-11-22 01:58

    As with DATEDIFF, I do not consider the end date to be part of the interval. The number of (for example) Sundays between @StartDate and @EndDate is the number of Sundays between an "initial" Monday and the @EndDate minus the number of Sundays between this "initial" Monday and the @StartDate. Knowing this, we can calculate the number of workdays as follows:

    DECLARE @StartDate DATETIME
    DECLARE @EndDate DATETIME
    SET @StartDate = '2018/01/01'
    SET @EndDate = '2019/01/01'
    
    SELECT DATEDIFF(Day, @StartDate, @EndDate) -- Total Days
      - (DATEDIFF(Day, 0, @EndDate)/7 - DATEDIFF(Day, 0, @StartDate)/7) -- Sundays
      - (DATEDIFF(Day, -1, @EndDate)/7 - DATEDIFF(Day, -1, @StartDate)/7) -- Saturdays
    

    Best regards!

    0 讨论(0)
  • 2020-11-22 01:58

    None of the functions above work for the same week or deal with holidays. I wrote this:

    create FUNCTION [dbo].[ShiftHolidayToWorkday](@date date)
    RETURNS date
    AS
    BEGIN
        IF DATENAME( dw, @Date ) = 'Saturday'
            SET @Date = DATEADD(day, - 1, @Date)
    
        ELSE IF DATENAME( dw, @Date ) = 'Sunday'
            SET @Date = DATEADD(day, 1, @Date)
    
        RETURN @date
    END
    GO
    
    create FUNCTION [dbo].[GetHoliday](@date date)
    RETURNS varchar(50)
    AS
    BEGIN
        declare @s varchar(50)
    
        SELECT @s = CASE
            WHEN dbo.ShiftHolidayToWorkday(CONVERT(varchar, [Year]  ) + '-01-01') = @date THEN 'New Year'
            WHEN dbo.ShiftHolidayToWorkday(CONVERT(varchar, [Year]+1) + '-01-01') = @date THEN 'New Year'
            WHEN dbo.ShiftHolidayToWorkday(CONVERT(varchar, [Year]  ) + '-07-04') = @date THEN 'Independence Day'
            WHEN dbo.ShiftHolidayToWorkday(CONVERT(varchar, [Year]  ) + '-12-25') = @date THEN 'Christmas Day'
            --WHEN dbo.ShiftHolidayToWorkday(CONVERT(varchar, [Year]) + '-12-31') = @date THEN 'New Years Eve'
            --WHEN dbo.ShiftHolidayToWorkday(CONVERT(varchar, [Year]) + '-11-11') = @date THEN 'Veteran''s Day'
    
            WHEN [Month] = 1  AND [DayOfMonth] BETWEEN 15 AND 21 AND [DayName] = 'Monday' THEN 'Martin Luther King Day'
            WHEN [Month] = 5  AND [DayOfMonth] >= 25             AND [DayName] = 'Monday' THEN 'Memorial Day'
            WHEN [Month] = 9  AND [DayOfMonth] <= 7              AND [DayName] = 'Monday' THEN 'Labor Day'
            WHEN [Month] = 11 AND [DayOfMonth] BETWEEN 22 AND 28 AND [DayName] = 'Thursday' THEN 'Thanksgiving Day'
            WHEN [Month] = 11 AND [DayOfMonth] BETWEEN 23 AND 29 AND [DayName] = 'Friday' THEN 'Day After Thanksgiving'
            ELSE NULL END
        FROM (
            SELECT
                [Year] = YEAR(@date),
                [Month] = MONTH(@date),
                [DayOfMonth] = DAY(@date),
                [DayName]   = DATENAME(weekday,@date)
        ) c
    
        RETURN @s
    END
    GO
    
    create FUNCTION [dbo].GetHolidays(@year int)
    RETURNS TABLE 
    AS
    RETURN (  
        select dt, dbo.GetHoliday(dt) as Holiday
        from (
            select dateadd(day, number, convert(varchar,@year) + '-01-01') dt
            from master..spt_values 
            where type='p' 
            ) d
        where year(dt) = @year and dbo.GetHoliday(dt) is not null
    )
    
    create proc UpdateHolidaysTable
    as
    
    if not exists(select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'Holidays')
        create table Holidays(dt date primary key clustered, Holiday varchar(50))
    
    declare @year int
    set @year = 1990
    
    while @year < year(GetDate()) + 20
    begin
        insert into Holidays(dt, Holiday)
        select a.dt, a.Holiday
        from dbo.GetHolidays(@year) a
            left join Holidays b on b.dt = a.dt
        where b.dt is null
    
        set @year = @year + 1
    end
    
    create FUNCTION [dbo].[GetWorkDays](@StartDate DATE = NULL, @EndDate DATE = NULL)
    RETURNS INT 
    AS
    BEGIN
        IF @StartDate IS NULL OR @EndDate IS NULL
            RETURN  0
    
        IF @StartDate >= @EndDate 
            RETURN  0
    
        DECLARE @Days int
        SET @Days = 0
    
        IF year(@StartDate) * 100 + datepart(week, @StartDate) = year(@EndDate) * 100 + datepart(week, @EndDate) 
            --same week
            select @Days = (DATEDIFF(dd, @StartDate, @EndDate))
          - (CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
          - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
          - (select count(*) from Holidays where dt between @StartDate and @EndDate)
        ELSE
            --diff weeks
            select @Days = (DATEDIFF(dd, @StartDate, @EndDate) + 1)
          - (DATEDIFF(wk, @StartDate, @EndDate) * 2)
          - (CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
          - (CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END)
          - (select count(*) from Holidays where dt between @StartDate and @EndDate)
     
        RETURN  @Days
    END
    
    0 讨论(0)
提交回复
热议问题