2012-04-18 4 views
1

開始週および終了週として指定された期間についての情報があります(ISO週として定義されているため、VBA - 一部の期間が一部の月に完全に含まれているかどうかを確認します。

ISO定義に関しては、月には4または5週間が含まれていますdescription on wikip。 いくつかの期間がいくつかの月に完全に含まれているかどうか、そして次のコマンドを実行した後に確認したいと思います。

Excel VBAでどうすればいいですか?上記のチェックを達成するのに役立つ特別な機能はありますか?

+0

あなたはあなたのデータのサンプルを提供していただけますか? – Gaffi

答えて

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を返します。

マイナーな調整を行うことで、週の開始が1週間以上になることを心配している場合に備えて、最初と最後の両方の開始日を報告することができます。

テストケースを使用:

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 

EDIT:

後、あなたの例が提供する、ここで更新機能です。これは、あなたが探しているフォーマットされた日付/月の配列(バリアント)を返す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

+0

良いコードですが、私が読むのを始めたら...私はそれが私が達成したいものではなかったことを認識します...申し訳ありません、それは私のせいでした - 私は間違って問題を記述しました。月を考慮しましょう2010年5月10日と06/2010 月2010年5月20日は18-2010週に開始し、21-2010に終了します 月06/2012週22-2010に開始し、25-2010で終了します。 は結果(ヶ月) 18から2010 \t \t 20から2010 \t \t NULL 17から2010 \t \t 20から2010 \t \t NULL 17から2010 \t \t \t End_wk \t私はいくつかの例に Start_wk \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

+0

私は上記のコードを更新しました。あなた自身の質問に新しい答えを加えることは、適切な形式ではないので、ここで話すためにコメントを使用してください。サイトのQ + Aフォーマットから逸脱するだけでなく、このように投稿した更新情報は表示されません。私の答えにコメントすることで、あなたがするときに通知を受けることが保証されます。 – Gaffi