【问题标题】:Check if a date range has a weekend检查日期范围是否有周末
【发布时间】:2013-06-11 06:07:19
【问题描述】:

如果我有 2 个日期,我知道我可以使用 datediff 计算出两个日期之间的天数、小时数、分钟数等,例如:

declare @start datetime;
set @start = '2013-06-14';

declare @end datetime;
set @end = '2013-06-15';

select datediff( hour, @start, @end );

如何确定日期范围是否包括周末?

我想知道日期范围是否包括周末的原因是因为我想从天数或小时数中减去周末。即如果开始日期是星期五,结束日期是星期一,我应该只有 1 天或 24 小时。

我的服务器上的日期部分 1 = 星期日,日期部分 7 = 星期六。

【问题讨论】:

  • 什么定义了“周末”?假期(“三天的周末”)算数吗?
  • 在这种情况下,周末仅表示周六和/或周日。我稍后会尝试公共假期,但对于这个问题并不重要。 :)
  • 首先,如果 diff 为 6 或更多,那么其中一天是周末。如果 diff 为零,则必须检查星期几的 sat 或 sun。然后假设星期天是第 0 天。如果开始日期的星期几大于结束日期的星期几,则该范围内至少有一个周末。
  • 如果结束日期的星期几(作为整数)小于开始日期,如果任一天本身是周末,或者范围大于一周,则您有周末。
  • @LeeMeador,但如果开始日是星期五,结束日是星期一,那只有 3 天,包括 2 个周末...

标签: sql-server sql-server-2008 tsql


【解决方案1】:

我有一个计算两个日期之间工作日的函数,基本查询是

declare @start datetime;
set @start = '2013-06-14';

declare @end datetime;
set @end = '2013-06-17';
SELECT 
   (DATEDIFF(dd, @Start, @end) +1)  -- total number of days (inclusive)
  -(DATEDIFF(wk, @Start, @end) * 2) -- number of complete weekends in period
  -- remove partial weekend days, ie if starts on sunday or ends on saturday
  -(CASE WHEN DATENAME(dw, @Start) = 'Sunday' THEN 1 ELSE 0 END) 
  -(CASE WHEN DATENAME(dw, @end) = 'Saturday' THEN 1 ELSE 0 END) 

如果工作日与 datediff 的天数不同,您可以计算出日期是否包括周末

  SELECT case when  (DATEDIFF(dd, @Start, @end) +1) <>
   (DATEDIFF(dd, @Start, @end) +1)  -- total number of days (inclusive)
  -(DATEDIFF(wk, @Start, @end) * 2) -- number of complete weekends in period
  -- remove partial weekend days, ie if starts on sunday or ends on saturday
  -(CASE WHEN DATENAME(dw, @Start) = 'Sunday' THEN 1 ELSE 0 END) 
  -(CASE WHEN DATENAME(dw, @end) = 'Saturday' THEN 1 ELSE 0 END) then 'Yes' else 'No' end as IncludesWeekends

或更简单

SELECT   (DATEDIFF(wk, @Start, @end) * 2) +(CASE WHEN DATENAME(dw, @Start) = 'Sunday' THEN 1 ELSE 0 END)      +(CASE WHEN DATENAME(dw, @end) = 'Saturday' THEN 1 ELSE 0 END)  as weekendDays

【讨论】:

  • 看起来最后 3 个词的总和给了他他真正想要的东西,即日期范围内包含的周末天数。
  • 确实,SELECT (DATEDIFF(wk, @Start, @end) * 2) +(CASE WHEN DATENAME(dw, @Start) = 'Sunday' THEN 1 ELSE 0 END) +(CASE WHEN DATENAME(dw, @end) = 'Saturday' THEN 1 ELSE 0 END) as weekendDays 应该这样做 - 我会更新答案
【解决方案2】:

如果满足以下三个条件中的任何一个,您就有一个周末:

  1. 结束日期的星期几(整数形式)小于开始日期的星期几

  2. 每天都是周末

  3. 范围包括至少六天

.

select 
    Coalesce(
    --rule 1
    case when datepart(dw,@end) - datepart(dw,@start) < 0 then 'Weekend' else null end,
    -- rule 2
    -- depends on server rules for when the week starts
    -- I think this code uses sql server defaults
    case when datepart(dw,@end) in (1,7) or datepart(dw,@start) in (1,7) then 'Weekend' else null end,
    --rule 3
    -- six days is long enough
    case when datediff(d, @start, @end) >= 6 then 'Weekend' Else null end,
    -- default
    'Weekday')

【讨论】:

  • @LeeMeador 规则 3 为真,周三至周三为 8 天
  • 我服务器上的 datepart 1 是星期日,7 是星期六。
  • @oshirowanen 如果您在一周中的几天使用 1-7,他的规则仍然有效。真的,任何连续运行的 7 个整数。例如,Sun 到 Sat 的 100 到 106 可以。只要星期天是它的低数字。
【解决方案3】:

一种方法,只是向您展示如何使用数字表来实现此目的

declare @start datetime;
set @start = '2013-06-14';

declare @end datetime;
set @end = '2013-06-15'; -- play around by making this 2013-06-14 and other dates


IF EXISTS (SELECT * FROM(
SELECT DATEADD(dd,number,@start) AS SomeDAte 
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(dd,number,@start) BETWEEN @start AND @end) x
WHERE DATEPART(dw,SomeDate) IN(1,7))  -- US assumed here
SELECT 'Yes'
ELSE
SELECT 'No'

返回两个日期之间所有周末的示例

declare @start datetime;
set @start = '2013-06-14';

declare @end datetime;
set @end = '2013-06-30';



SELECT DATEADD(dd,number,@start) AS SomeDAte 
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(dd,number,@start) BETWEEN @start AND @end
AND DATEPART(dw,DATEADD(dd,number,@start)) IN(1,7)

结果

2013-06-15 00:00:00.000
2013-06-16 00:00:00.000
2013-06-22 00:00:00.000
2013-06-23 00:00:00.000
2013-06-29 00:00:00.000
2013-06-30 00:00:00.000

【讨论】:

    【解决方案4】:

    您可以使用以下功能。如果它从周末开始,第一个将给定的开始日期或结束日期移动到星期一(如果向后,则为星期五)。第二个计算没有周末的两个日期之间的秒数。然后你只需要检查总天数是否等于没有 weksends 的天数(下面的演示)。

    CREATE FUNCTION [dbo].[__CorrectDate](
        @date DATETIME,
        @forward INT
    )
    
    RETURNS DATETIME AS BEGIN
        IF (DATEPART(dw, @date) > 5) BEGIN
    
            IF (@forward = 1) BEGIN
                SET @date = @date + (8 - DATEPART(dw, @date))
                SET @date = DateAdd(Hour, (8 - DatePart(Hour, @date)), @date)
            END ELSE BEGIN
                SET @date = @date - (DATEPART(dw, @date)- 5)
                SET @date = DateAdd(Hour, (18 - DatePart(Hour, @date)), @date)
            END
            SET @date = DateAdd(Minute, -DatePart(Minute, @date), @date)
            SET @date = DateAdd(Second, -DatePart(Second, @date), @date)
        END
    
        RETURN @date
    END
    
    GO
    
    CREATE FUNCTION [dbo].[__DateDiff_NoWeekends](
        @date1 DATETIME,
        @date2 DATETIME
    )
    
    RETURNS INT AS BEGIN
        DECLARE @retValue INT
    
        SET @date1 = dbo.__CorrectDate(@date1, 1)
        SET @date2 = dbo.__CorrectDate(@date2, 0)
    
        IF (@date1 >= @date2)
            SET @retValue = 0
        ELSE BEGIN
            DECLARE @days INT, @weekday INT
            SET @days = DATEDIFF(d, @date1, @date2)
            SET @weekday = DATEPART(dw, @date1) - 1
    
            SET @retValue = DATEDIFF(s, @date1, @date2) - 2 * 24 * 3600 * ((@days + @weekday) / 7) 
        END
    
        RETURN @retValue
    END
    

    那么你可以通过这种方式获取信息:

    declare @start datetime
    set @start = '20130614'
    
    declare @end datetime
    set @end = '20130615'
    
    declare @daysTotal int
    declare @daysWoWeekends int
    
    SET @daysTotal = DATEDIFF(dd, @start, @end)
    SET @daysWoWeekends = dbo.__DateDiff_NoWeekends(@start, @end) / (24 * 3600)
    
    SELECT CASE WHEN @daysTotal = @daysWoWeekends
           THEN 'No weekend between'
           ELSE 'There are weeksends' END,
           @daysTotal,
           @daysWoWeekends,@start,@end
    

    这是一个演示:http://sqlfiddle.com/#!6/7cda7/11

    There are weeksends 1   0   June, 14 2013 00:00:00+0000 June, 15 2013 00:00:00+0000
    

    【讨论】:

      【解决方案5】:

      这里是简单而概括的查询。您可以通过递归查询获得结果。检查以下查询

      with mycte as
      (
        select cast('2013-06-14' as datetime) DateValue
        union all
        select DateValue + 1 from mycte where DateValue + 1 < '2013-06-17'
      )
      
      select count(*) as days , count(*)*24 as hours
      from    mycte
      WHERE DATENAME(weekday ,DateValue) != 'SATURDAY' AND 
      DATENAME(weekday ,DateValue) != 'SUNDAY'
      OPTION (MAXRECURSION 0)
      

      它肯定对你有用。

      【讨论】:

        【解决方案6】:

        您可以使用递归 CTE 来获取范围之间的日期

             WITH    CTE_DatesTable
                  AS ( SELECT   @MinDate AS [EffectiveDate]
                       UNION ALL
                       SELECT   DATEADD(dd, 1, [EffectiveDate])
                       FROM     CTE_DatesTable
                       WHERE    DATEADD(dd, 1, [EffectiveDate]) <= @MaxDate )
        
                    SELECT  [EffectiveDate]
                    FROM    CTE_DatesTable
            OPTION  ( MAXRECURSION 0 );
        

        然后使用..过滤掉周末

        ((DATEPART(dw, DT.EffectiveDate) + @@DATEFIRST) % 7) NOT IN (0, 1) 
        

        【讨论】:

          【解决方案7】:

          类似的问题,尽管在我的情况下,我想要一个使用 Impala SQL 的两个日期是否包含该 DOW 的 onehot 编码的二进制列列表(周一至周日)。

          select ...
          
          max(case when tripduration > 6 then 1
                   when 2 >= dayofweek(tripstartdate) and 2 <= dayofweek(tripstartdate) + tripduration then 1
                   when 2 <= dayofweek(tripenddate)   and 2 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '2 mon',
          
          max(case when tripduration > 6 then 1
                   when 3 >= dayofweek(tripstartdate) and 3 <= dayofweek(tripstartdate) + tripduration then 1
                   when 3 <= dayofweek(tripenddate)   and 3 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '3 tue',
          
          max(case when tripduration > 6 then 1
                   when 4 >= dayofweek(tripstartdate) and 4 <= dayofweek(tripstartdate) + tripduration then 1
                   when 4 <= dayofweek(tripenddate)   and 4 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '4 wed',
          
          max(case when tripduration > 6 then 1
                   when 5 >= dayofweek(tripstartdate) and 5 <= dayofweek(tripstartdate) + tripduration then 1
                   when 5 <= dayofweek(tripenddate)   and 5 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '5 thu',
          
          max(case when tripduration > 6 then 1
                   when 6 >= dayofweek(tripstartdate) and 6 <= dayofweek(tripstartdate) + tripduration then 1
                   when 6 <= dayofweek(tripenddate)   and 6 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '6 fri',
          
          max(case when tripduration > 6 then 1
                   when 7 >= dayofweek(tripstartdate) and 7 <= dayofweek(tripstartdate) + tripduration then 1
                   when 7 <= dayofweek(tripenddate)   and 7 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '7 sat',
          
          max(case when tripduration > 6 then 1
                   when 1 >= dayofweek(tripstartdate) and 1 <= dayofweek(tripstartdate) + tripduration then 1
                   when 1 <= dayofweek(tripenddate)   and 1 >= dayofweek(tripenddate) - tripduration   then 1 else 0 end) as '1 sun'
          
          from ....
          

          【讨论】:

            猜你喜欢
            • 2014-07-25
            • 1970-01-01
            • 2023-03-16
            • 1970-01-01
            • 1970-01-01
            • 2023-02-08
            • 2017-01-28
            • 1970-01-01
            相关资源
            最近更新 更多