1

我有一些关于开始和结束周的时间段的信息(定义为 ISO 周,因此它们可能不仅从星期一开始)。

关于 ISO 定义,一个月可能在 wikip 上包含 4 或 5 周的描述。我想检查某些时间段是否完全包含在几个月中以及在它执行下一个命令之后。

如何在 Excel VBA 中执行此操作?是否有任何特殊功能可以帮助我实现上述检查?

4

1 回答 1

1

这有帮助吗?

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
于 2012-04-18T14:25:34.573 回答