2012-04-18 46 views
1

我有一些关于某些时间段的信息作为开始和结束周(定义为ISO周,因此它们不仅可以在星期一开始)。VBA - 检查一些时间段是否完全包含在某个月

关于ISO定义,一个月可能包含4或5周description on wikip。 我想检查一些时间段是否完全包含在几个月内,并在它执行下一个命令之后。

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

+0

能否请您提供您的数据的样本? – Gaffi 2012-04-18 13:32:39

回答

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 
+0

好的代码,但是当我开始阅读时...我意识到这不是我想要实现的...对不起,这是我的错 - 我错误地描述了这个问题。 – matandked 2012-04-19 10:58:28

+0

好的代码,但是当我开始阅读时...我意识到这不是我想要实现的......对不起,这是我的错 - 我错误地描述了这个问题。我们来考虑几个月2010/2010和06/2010 05/2010月开始于18-2010周,结束于21-2010 月06/2012开始于22-2010周,结束于25-2010。 让我举一些例子 Start_wk \t End_wk \t \t结果(月) 18-2010 \t \t 20-2010 \t \t NULL 17-2010 \t \t 20-2010 \t \t NULL 17-2010 \t \t 21- 2010 \t 05/2010 18-2010 \t \t 25-2010 \t \t 05/2010,06/2010 19-2010 \t \t 26-2010 \t \t 06/2010 – matandked 2012-04-19 11:28:09

+0

我已经更新了上面的代码。请在这里使用评论进行交流,因为为自己的问题添加新答案的做法不正确。它不仅从该网站的Q + A格式转移,但我不会看到您以这种方式发布的任何更新。评论我的回答将确保我收到通知。 – Gaffi 2012-05-21 19:48:47

相关问题