我有一些关于开始和结束周的时间段的信息(定义为 ISO 周,因此它们可能不仅从星期一开始)。
关于 ISO 定义,一个月可能在 wikip 上包含 4 或 5 周的描述。我想检查某些时间段是否完全包含在几个月中以及在它执行下一个命令之后。
如何在 Excel VBA 中执行此操作?是否有任何特殊功能可以帮助我实现上述检查?
我有一些关于开始和结束周的时间段的信息(定义为 ISO 周,因此它们可能不仅从星期一开始)。
关于 ISO 定义,一个月可能在 wikip 上包含 4 或 5 周的描述。我想检查某些时间段是否完全包含在几个月中以及在它执行下一个命令之后。
如何在 Excel VBA 中执行此操作?是否有任何特殊功能可以帮助我实现上述检查?
这有帮助吗?
Function ContainedInMonth(OriginalStartDate As String, _
OriginalEndDate As String) As Boolean
Dim MonthSet As Variant
Dim AryCounter As Integer, ISOOffset As Integer
Dim StartYear As Integer, EndYear As Integer
Dim StartWeek As Integer, EndWeek As Integer
Dim StartDay As Integer, EndDay As Integer
Dim FormattedStartDate As Date, FormattedEndDate As Date
' This section may (will) vary, depending on your data.
' I'm assuming "YYYY-WW" is passed...
' Also, error/formatting checking for these values is needed
' and wil differ depending on that format.
StartYear = Val(Left(OriginalStartDate, 4))
StartWeek = Val(Right(OriginalStartDate, 2))
EndYear = Val(Left(OriginalEndDate, 4))
EndWeek = Val(Right(OriginalEndDate, 2))
If StartYear <> EndYear Or StartWeek > EndWeek Then
ContainedInMonth = False
ElseIf StartWeek = EndWeek Then
ContainedInMonth = True
Else
' Using the calculation from wikipedia. Honestly, I'm not sure that
' I understand this bit, but it seemed to work for my test cases.
ISOOffset = Weekday(CDate("1/4/" & StartYear), vbMonday) + 3
StartDay = (StartWeek * 7) - ISOOffset ' Adding 0 for start of week
EndDay = (EndWeek * 7) + 6 - ISOOffset ' Adding 6 for end of week
' Set the starting day for each month, depending on leap year.
If StartYear Mod 4 = 0 Then
MonthSet = Array(0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)
Else
MonthSet = Array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
End If
FormattedStartDate = 0:FormattedEndDate = 0
For AryCounter = 11 To 0 Step -1
If StartDay > MonthSet(AryCounter) And FormattedStartDate = 0 Then
' Using MM/DD/YYYY format - this may be different for you
FormattedStartDate = CDate(AryCounter + 1 & _
"/" & StartDay - MonthSet(AryCounter) & "/" & StartYear)
End If
If EndDay > MonthSet(AryCounter) And FormattedEndDate = 0 Then
FormattedEndDate = CDate(AryCounter + 1 & _
"/" & EndDay - MonthSet(AryCounter) & "/" & EndYear)
End If
Next AryCounter
ContainedInMonth = IIf(Month(FormattedStartDate) = Month(FormattedEndDate), True, False)
End If
End Function
这是基于我自己的一些假设的测试代码(作为工作表函数或通过 VBA 工作)。(需要您的数据来确定测试...)如果您确实有特定的格式示例,我将更改此代码以匹配。
这假设您将正确的变量传递到函数中的正确位置。尽管您至少不应该收到任何错误,但没有对开始/结束日期顺序进行适当的检查。
此外,可能有更有效的方法来执行此操作,而不是循环遍历这些数组,但这是可行的。
简单地说,它的作用是计算给定开始周的第一天和给定结束周的最后一天的日期。如果这两个日期都在同一月份,则该函数返回 true。
通过细微的调整,我们可以报告第一周和上周的开始日期,以防您担心一周的开始而不是整周。
使用的测试用例:
Start End Result
2012-01 2012-05 FALSE
2012-01 2012-04 TRUE
2012-05 2012-07 FALSE
2012-25 2012-26 TRUE
2012-52 2012-01 FALSE
2012-28 2012-25 FALSE
编辑:
按照您提供的示例,这是一个更新的函数。这将作为 VBA 函数按原样工作,返回您正在寻找的格式化日期/月份的数组(变体)。要将其转换为工作表函数,只需进行一些小的调整即可返回字符串(已在函数中创建 - 请参阅注释)。
我假设您的示例是错误的(请参阅我的测试用例),但是如果是我错了,则可以对其进行修改以使其正常工作。
Function ContainsWhatMonths(OriginalStartDate As String, _
OriginalEndDate As String) As Variant
Dim MonthSet As Variant
Dim AryCounter As Integer, ISOOffset As Integer
Dim StartYear As Integer, EndYear As Integer
Dim StartWeek As Integer, EndWeek As Integer
Dim StartDay As Integer, EndDay As Integer
Dim StartWeekStartDate As Date, StartWeekEndDate As Date
Dim EndWeekStartDate As Date, EndWeekEndDate As Date
Dim FormattedStartDate As Date, FormattedEndDate As Date
Dim TotalMonths As Integer, OutputMonths As String
StartYear = Val(Right(OriginalStartDate, 4))
StartWeek = Val(Left(OriginalStartDate, 2))
EndYear = Val(Right(OriginalEndDate, 4))
EndWeek = Val(Left(OriginalEndDate, 2))
If StartYear <= EndYear Then
' Using the calculation from wikipedia. Honestly, I'm not sure that
' I understand this bit, but it seemed to work for my test cases.
ISOOffset = Weekday(CDate("1/4/" & StartYear), vbMonday) + 3
StartDay = (StartWeek * 7) - ISOOffset ' Adding 0 for start of week
EndDay = (EndWeek * 7) + 6 - ISOOffset ' Adding 6 for end of week
' Set the starting day for each month, depending on leap year.
If StartYear Mod 4 = 0 Then
MonthSet = Array(0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)
Else
MonthSet = Array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
End If
For AryCounter = 11 To 0 Step -1
If StartDay > MonthSet(AryCounter) Then
' Using MM/DD/YYYY format - this may be different for you
StartWeekStartDate = CDate(AryCounter + 1 & _
"/" & StartDay - MonthSet(AryCounter) & "/" & StartYear)
StartWeekEndDate = StartWeekStartDate + 6
If Month(StartWeekStartDate) <> Month(StartWeekEndDate) Then
FormattedStartDate = DateSerial(StartYear, Month(StartWeekEndDate), 1)
Else
FormattedStartDate = DateSerial(StartYear, Month(StartWeekEndDate) + 1, 1)
End If
Exit For
End If
Next AryCounter
If EndYear Mod 4 = 0 Then
MonthSet = Array(0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)
Else
MonthSet = Array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
End If
For AryCounter = 11 To 0 Step -1
If EndDay > MonthSet(AryCounter) Then
EndWeekStartDate = CDate(AryCounter + 1 & _
"/" & EndDay - MonthSet(AryCounter) & "/" & EndYear)
EndWeekEndDate = EndWeekStartDate + 6
If Month(EndWeekStartDate) <> Month(EndWeekEndDate) Then
FormattedEndDate = CDate(Month(EndWeekEndDate) & "/1/" & EndYear) - 1
Else
FormattedEndDate = CDate(Month(EndWeekEndDate) & "/1/" & EndYear)
End If
Exit For
End If
Next AryCounter
' Switch the commenting on these two lines to return the string
ContainsWhatMonths = Array()
'ContainsWhatMonths = vbNullString
TotalMonths = (Year(FormattedEndDate) - Year(FormattedStartDate)) * 12 + _
Month(FormattedEndDate) - Month(FormattedStartDate)
If TotalMonths >= 0 Then
For AryCounter = 0 To TotalMonths
OutputMonths = OutputMonths & "," & _
Format(DateAdd("m", AryCounter, FormattedStartDate), "MM/YYYY")
Next
OutputMonths = Right(OutputMonths, Len(OutputMonths) - 1)
' Switch the commenting on these two lines to return the string
ContainsWhatMonths = Split(OutputMonths, ",")
'ContainsWhatMonths = OutputMonths
End If
End If
End Function
测试用例:
"18-2010", "20-2010" 'Null
"17-2010", "20-2010" 'Null
"17-2010", "21-2010" '05/2010
"18-2010", "25-2010" '06/2010
"17-2010", "25-2010" '05/2010,06/2010
"19-2010", "26-2010" '06/2010