从日期计算中排除周末和自定义日期(即假期)

Posted

技术标签:

【中文标题】从日期计算中排除周末和自定义日期(即假期)【英文标题】:Exclude weekends and custom days (i.e. Holidays) from date calculations 【发布时间】:2009-10-22 01:49:04 【问题描述】:

目前,我正在根据开始日期 (DateTime) 和持续时间(天数)计算完成日期,但我的计算没有考虑周末或节假日。

所以,我的解决方案不正确。这只是一个起点。

我阅读了一些文章,其中一种方法是创建一个巨大的日历表,其中包含未来 50 年的所有周末和节假日。我想这个想法是根据日历表查询日期范围并减去周末和/或假期的数量。

问题是我正在开发的软件允许用户设置自己的项目日历。 考虑到该软件允许用户管理多个项目,表会不会变得太大而无法维护?

所以,我想我的问题是如何开始以及有哪些可能的方法来解决这个问题?

基本上,对于每个项目任务,我需要根据 START 日期和 DURATION 计算任务完成日期,但要考虑周末和自定义日期(又名假期)。

有什么想法吗?

顺便说一句:我使用的是 SQL Server 2005。

【问题讨论】:

哪个 RDBMS? (SQL Server?Oracle?) 50 天?会不会是笔误?大多数日历表应该有多年的数据。 刚刚从几天编辑到几年 【参考方案1】:

我阅读了一些文章,其中一种方法是创建一个巨大的日历表,其中包含未来 50 年的所有周末和节假日。我想这个想法是根据日历表查询日期范围并减去周末和/或假期的数量。

这是因为有些假期并不总是在同一天。例如劳动节 - 9 月的第一个星期一。在数据库中存储每年的日期比尝试编写规则来计算它更容易且占用更少的空间。

其他考虑因素是周六/周日的假期 - 如果休息日会在周一或周五落下,那就太麻烦了。有些假期是联邦的,而其他的则是地方性的……

【讨论】:

D'oh...直到这个被接受后我才得到答案。 复活节还不错,因为你可以找到它的公式。更大的问题是当有额外的日子投入时,比如英国在 2002 年的禧年有额外的公共假期,或者澳大利亚在 2000 年悉尼奥运会开始时有额外的公共假期。为此,你真的需要列出休息日的表格,或在日历表格中包含“IsHoliday”字段。 或者考虑像斋月这样的事情,它每年因地而异。它也是法令,不是计算的(尽管法令的日期可以通过计算来估计)。 j.mp/GKzaM【参考方案2】:

创建一个包含 [long period] 剩余时间的所有周末和节假日的大型日历,将其设置为仅选择。然后在每次创建新项目时将所需的天数复制到项目的日历中。

【讨论】:

【参考方案3】:

假设您有一个名为 AllDays 的表,其中包含名为 theDay 和 IsPublicHoliday 的列。还假设您的@@DATEFIRST 设置为 1,因此您的周末是第 1 天和第 7 天。您想从@StartDate 中找到@n 天的日期。

WITH NumberedDays AS
(
SELECT theDay, ROW_NUMBER() OVER (ORDER BY theDay) AS DayNum
FROM AllDays
WHERE DATEPART(dw, theDay) NOT IN (1,7)
AND IsPublicHoliday = 0
AND theDay > @StartDate
)
SELECT theDay
FROM NumberedDays
WHERE DayNum = @n
;

如果您没有名为 AllDays 的表,那么您可以轻松使用数字表,其中 theDay 是 DATEADD(day,num,@StartDate)。您可以对不符合条件的日期列表进行 LEFT JOIN(当然应该将其编入索引)。

【讨论】:

【参考方案4】:

@Stuart:是的,50 天是一个类型 =P。我的意思是 50 年。

@rexem: 确实如此。计算将过于复杂且容易出错。

@David:创建一个仅限选择的主日历表,然后复制每个项目所需的日期听起来是个好主意。

(请原谅,我的推特式回复,哈哈)

感谢您的精彩回复!

【讨论】:

你需要一些代表才能离开 cmets :p 我很抱歉。我现在确实看到了评论按钮。我没有完全意识到这种区别。谢谢!【参考方案5】:

您好,我是这样解决的:

首先我创建了一个日历表 (tb_cal),其中包含两个字段 date_day (smalldate)、holiday (bit)

CREATE TABLE [user].[tb_cal](
[date_day] [smalldatetime] NULL,
[holiday] [bit] NULL
) ON [PRIMARY]

那么这个函数:

CREATE FUNCTION [user].[fc_get_labor_days]
(@from datetime, @to datetime)
RETURNS int
AS
BEGIN
return ( 
select count(*) as total 
from tb_cal 
where datepart(dw, date_day) not in (1,7)   
and holiday <> 1 
and date_day > @from and date_day <= @to )
END 

你可以通过传递参数(from,to)来调用这个函数

SELECT user.fc_get_labor_days(my_date_from, my_date_to) as [days]

希望对你有帮助

【讨论】:

【参考方案6】:

在排除周末和节假日后,使用下面的代码获取下一个工作日期

Declare @AddDay as integer = 3
Declare @NextWorkingDate  DateTime
Declare @StartDate  DateTime = Cast(getdate() as date)

While  @AddDay > 0 
    begin

        Select @NextWorkingDate =  @StartDate + @AddDay +
        (datediff(wk, @StartDate, @StartDate+ @AddDay  ) * 2) -- add weekend 

        --Exclude weekend
        If datepart(dw,@NextWorkingDate ) = 1 or datepart(dw,@NextWorkingDate ) = 7  --Add 2 days if target date is either Saturday or Sunday
            set @NextWorkingDate = @NextWorkingDate + 2 

        --Count no of holidays if falling within start date and nextwrking date
        Select @AddDay = Count(*)  from HolidayTable ST --Holiday list
                    where ST.OffDate between @StartDate+1 and @NextWorkingDate
        Set @StartDate = @NextWorkingDate
    End         

Select @NextWorkingDate

【讨论】:

以上是关于从日期计算中排除周末和自定义日期(即假期)的主要内容,如果未能解决你的问题,请参考以下文章

如何在日期中添加天数,仅考虑工作日(即忽略周末)?

从 strtotime 中排除周末

MySQL排除节假日,计算日期差

计算包括节假日在内的工作日

Xamarin/XAML/C# - 构建一个计算 DateTime 数组中所有周末天数的方法

在Python中动态计算不包括假期日历的工作日数