处理业务日期(节假日/周末等)






3.06/5 (17投票s)
2003年12月10日
5分钟阅读

123308

876
本项目旨在简化计算有效业务日的难题。它包含我创建的几个函数,用于确定日期是否为节假日或周末,以及获取下一个或上一个业务日。
引言
本项目旨在简化计算有效业务日的难题。它包含我创建的几个函数,用于确定日期是否为节假日或周末,以及获取下一个或上一个业务日。还有一个函数用于查找给定月份的最后一天。我包含了伪代码,可以帮助您将这些函数翻译成其他语言。
背景
在工作中,您经常会遇到一些意想不到的场景。根据我的经验,业务日期给我带来了最大的麻烦,而且问题层出不穷。当您认为代码已经完美时,一个恰好落在周末的节假日,而那一年碰巧还是闰年,并且太阳恰好移动到火星左侧,我的代码就会崩溃,1200 笔银行交易会认为已完成,但银行却不知道我们在说什么,因为他们关门了,真是傻。:)
因此,我们的目标是编写一套完美的例程来处理日历可能出现的潜在问题。由于我们美丽的星球在自转,我们必须考虑许多有趣的问题,例如闰年以及每个月的天数没有固定规律。幸运的是,除了二月之外,每年都一样,否则我们真的会陷入困境。所以,话不多说,这是我尝试创建的完美业务日期例程,以处理我遇到的大部分问题。
在处理任何业务日期函数时,重要的是要注意,有两种主要类型的日期会影响我们处理数据的方式。
浮动节假日(圣诞节、新年、退伍军人节等)
浮动节假日是指日期固定不变,但星期几总是不固定的节假日。例如,圣诞节总是 12 月 25 日,但可能落在任何一个星期。因此,它是一个浮动节假日。
固定节假日(阵亡将士纪念日、劳动节、感恩节等)
固定节假日是指每年日期不同,但星期几总是固定的节假日。例如,马丁·路德·金纪念日总是 1 月的第三个星期一。
注意 - 请注意,这些函数并非旨在解决您所有的问题。它们需要您根据您所在地的节假日和标准进行一些调整。例如,我们公司处理交易有两天延迟,所以我们会将所有业务日期增加 2 天,因此重要的是要注意您的标准。
根据您公司的运作方式,您使用的节假日可能与其他人不同。例如,有些银行的节假日可能不是所有州都承认,而有些州可能有银行不承认的节假日。在我的例子中,我们将假设世界在我们所在的地区(华盛顿州)根据州和银行的节假日关闭。您需要相应地进行调整。
Using the Code
LastDayOfMonth
:传入您需要获取该月最后一天的月份和年份。我们将它拆分成两个参数,以帮助解决我们在使用定期付款时遇到的一些其他问题,稍后我将解释。
Function LastDayOfMonth(sMonth, sYear)
'Returns the last day of the month/year passed in "mm/dd/yyyy" format
dim tmpMonth, tmpYear
tmpMonth = sMonth
tmpYear = sYear
LastDayOfMonth = dateadd("d", -1, Dateadd("m", 1, tmpMonth & "/1/" & tmpYear))
End Function
IsHoliday
:传入您要验证的日期。如果日期是节假日,则返回 1,如果不是,则返回 0。然后您可以使用 DateAdd
并将返回的值添加到当前日期(您传入的日期)。至于此函数中的节假日,请确保如果您的节假日是浮动节假日,您需要包含前一天和后一天,以防主要节假日落在周末。请参见 12/24、25、26 作为示例。
Function IsHoliday(sDate)
'Checks to see if passed date is a holiday
Dim iDay, iTmpDay, i
IsHoliday = 0
iDay = Day(sDate)
'Check if valid date first
If IsDate(sDate) Then
Select Case Month(sDate)
Case 1 'Jan
If iDay = 1 Then 'New Years
IsHoliday = 1
Else
If iDay = 2 Then 'Make sure new years doesn't fall on sunday.
'If so, today is a holiday.
if Weekday(DateAdd("d", -1, sDate)) = 1 then
IsHoliday = 1
end if
Else
For i = 0 To 30 'Martin Luther King B-Day
If Weekday(DateAdd("d", i, CDate("1/1/" & Year(sDate)))) _
= 2 Then
If CDate(sDate) = CDate(DateAdd("d", i + 14, _
CDate("1/1/" & Year(sDate)))) Then
IsHoliday = 1
End If
Exit For 'PG 1/28
End If
Next
End If
End If
Case 2 'Feb
For i = 0 To 27 'President's Day
If Weekday(DateAdd("d", i, CDate("2/1/" & Year(sDate)))) = 2 _
Then
If CDate(sDate) = CDate(DateAdd("d", i + 14, _
CDate("2/1/" & Year(sDate)))) Then
IsHoliday = 1
End If
Exit For
End If
Next
Case 3 'Mar
Case 4 'Apr
Case 5 'May
For i = 1 To 7 'Memorial Day
If Weekday(DateAdd("d", "-" & i, _
CDate("5/31/" & Year(sDate)))) = 2 Then
If CDate(sDate) = CDate(DateAdd("d", "-" & i, _
CDate("5/31/" & Year(sDate)))) Then
IsHoliday = 1
End If
Exit For
End If
Next
Case 6 'Jun
Case 7 'Jul
If iDay = 4 Then 'Independence Day
IsHoliday = 1
Else
If iDay = 3 Then 'Make sure Independence Day doesn't
'fall on saturday. If so, Friday is a holiday.
if Weekday(DateAdd("d", 1, sDate)) = 7 then
IsHoliday = 1
end if
Else
If iDay = 5 Then 'Make sure Independence
'Day doesn't fall on sunday. If so, Monday is a holiday.
if Weekday(DateAdd("d", -1, sDate)) = 1 then
IsHoliday = 1
end if
End If
End If
End If
Case 8 'Aug
Case 9 'Sep
For i = 0 To 13 'Labor Day
If Weekday(DateAdd("d", i, CDate("9/1/" & _
Year(sDate)))) = 2 Then
If CDate(sDate) = CDate(DateAdd("d", i, _
CDate("9/1/" & Year(sDate)))) Then
IsHoliday = 1
End If
Exit For
End If
Next
Case 10 'Oct
For i = 0 To 13 'Columbus Day
If Weekday(DateAdd("d", i, CDate("10/1/" & _
Year(sDate)))) = 2 Then
If CDate(sDate) = CDate(DateAdd("d", i + 7, CDate("10/1/" & _
Year(sDate)))) Then
IsHoliday = 1
End If
Exit For
End If
Next
Case 11 'Nov
If iDay = 11 Then 'Veteran's Day
IsHoliday = 1
Else
If iDay = 10 Then 'Make sure Veterans Day doesn't fall
'on saturday. If so, Friday is a holiday.
if Weekday(DateAdd("d", 1, sDate)) = 7 then
IsHoliday = 1
end if
Else
If iDay = 12 Then 'Make sure Veterans Day doesn't
'fall on sunday. If so, Monday is a holiday.
if Weekday(DateAdd("d", -1, sDate)) = 1 then
IsHoliday = 1
end if
Else
For i = 0 To 28 'Thanksgiving & the Day After
If Weekday(DateAdd("d", i, CDate("11/1/" & _
Year(sDate)))) = 5 Then 'this is the first
'thursday of the month
if datediff("d", sDate, DateAdd("d", i + 21, _
CDate("11/1/" & Year(sDate)))) = 0 then 'add 3
'weeks to the first to get the 4th (thanksgiving)
IsHoliday = 1
Exit For
End If
End if
If Weekday(DateAdd("d", i, CDate("11/1/" & _
Year(sDate)))) = 6 Then 'this is the day
'after thanksgiving
if datediff("d", sDate, DateAdd("d", i + 21,_
CDate("11/1/" & Year(sDate)))) = 0 then
IsHoliday = 1
Exit For
End If
End if
Next
End If
End If
End If
Case 12 'Dec
If iDay = 25 Then 'Christmas
IsHoliday = 1
Else
If iDay = 24 Then 'Make sure Christmas Day doesn't
'fall on saturday. If so, Friday is a holiday.
if Weekday(DateAdd("d", 1, sDate)) = 7 then
IsHoliday = 1
end if
Else
If iDay = 26 Then 'Make sure Christmas
'Day doesn't fall on sunday. If so, Monday is a holiday.
if Weekday(DateAdd("d", -1, sDate)) = 1 then
IsHoliday = 1
end if
Else
If iDay = 31 Then 'Make sure new years
'doesn't fall on saturday. If so, today is a holiday.
if Weekday(DateAdd("d", 1, sDate)) = 7 then
IsHoliday = 1
End if
End if
End if
End if
End If
Case Else
'Do nothing but return false
End Select
End If
End Function
LastBusinessDay
:此函数将返回基于传入日期的上一个业务日(即非节假日、非周末)。
Function LastBusinessDay(sDate)
Dim iDay, iDaysToAdd, iDate
iDaysToAdd = 0
iDate = sDate
x = 1
Do while iDaysToAdd >= 0
If Weekday(iDate) = 1 or Weekday(iDate) = 7 or _
isHoliday(iDate) <> 0 then
iDay = Weekday(iDate)
Select Case cint(iDay)
Case 1 'Sunday
iDate = DateAdd("d", -1, iDate)
Case 7 'Saturday
iDate = DateAdd("d", -1, iDate)
Case else 'this is a valid day
if isHoliday(iDate) > 0 then
iDate = dateadd("d", -(isHoliday(iDate)), iDate)
else
iDaysToAdd = iDaysToAdd - 1
end if
End Select
end if
Loop
LastBusinessDay = iDate
End Function
AddBusinessDay
:将传入的业务天数加到传入日期上。如果传入 0
,此例程将验证传入的日期是否为业务日,如果不是,则返回下一个业务日。
Function AddBusinessDay(sDate, sAdd)
'Adds given number of business days to date.
Dim iDay, iDaysToAdd, iDate
iDaysToAdd = sAdd
iDate = sDate
x = 1
Do while iDaysToAdd >= 0
If Weekday(iDate) = 1 or Weekday(iDate) = 7 or _
isHoliday(iDate) <> 0 then
iDay = Weekday(iDate)
Select Case cint(iDay)
Case 1 'Sunday
iDate = dateadd("d", 1, iDate)
Case 7 'Saturday
iDate = DateAdd("d", 1, iDate)
Case else 'this is a valid day
if isHoliday(iDate) > 0 then
iDate = dateadd("d", isHoliday(iDate), iDate)
else
iDaysToAdd = iDaysToAdd - 1
end if
End Select
else
if iDaysToAdd > 0 then
iDate = DateAdd("d", 1, iDate)
end if
iDaysToAdd = iDaysToAdd - 1
end if
'Error trap in case of infinite loop, good for testing,
'shouldn't be necessary, but nice just in case
if x > 100 then
response.End()
iDaysToAdd = -1
else
x = x + 1
end if
Loop
AddBusinessDay = iDate
end function
关注点
使用示例
在支付系统中,您可以使用这些函数来确定进行支付的有效日期。如果提交一次性支付于 2005 年 1 月 1 日,我们需要验证:
- 该日期是否是节假日或周末
- 该日期是否临近周末的节假日?
直到以上条件都不成立,我们才能继续。一个常见的错误是先检查周末,然后检查节假日(反之亦然),但问题在于,如果您有一个周五的节假日,在增加 1 天后,您将永远不会再次检查新日期是否为周末。这就是为什么我们将它嵌套在一个循环中,直到返回一个有效日期。
调用日期例程时,我们必须传入日期以及我们希望将交易延迟多少天。在大多数情况下,交易不是即时的,所以您必须传入交易真正发生前的天数,而这个差值必须是业务天数,这说明了为什么我们需要这些例程。如果您不想延迟,只需传入 0,它将返回下一个有效的业务日。
Pseudo Example
AddBusinessDay(“01/01/2005”, 0)
Date = “01/01/2005”, DaysToAdd = 0
Loop while DaysToAdd parameter is equal to, or greater than, 0.
First time through
“01/01/2005” is a Weekend or Holiday then
“01/01/2005” is Saturday
Set Date = “01/02/2005”
Second time through
“01/02/2005” is a weekend or Holiday then
“01/02/2005” is Sunday
Set Date = “01/03/2005”
Third time through
“01/03/2005” is not a weekend or Holiday
There are no Days to Add
Subtract 1 from the DaysToAdd total
DateReturned = “01/03/2005”, DaysToAdd = -1
Return New Date
End AddBusinessDay Function
最后一点说明: 如果交易在您的截止时间之后发出,则在日期上额外增加 1 天。例如,如果您的交易在下午 5 点进行,那么之后提交的任何新付款都将在第二天真正发送。所以我们必须额外增加 1 天。
就是这样!
如果您发现我忘记了任何场景或遗漏了任何数据,请告诉我。正如我之前所说,日期问题可能很棘手,我希望让这些函数尽可能强大。所以如果您有任何改进它们的想法,请随时告诉我。
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。