レガシーは消えず

リタイアしたプログラマのコーヒータイム

年月計算用クラスを作ろう

業務システムでは不可欠の年月計算用のクラスを作りませんか。もちろんDate型の変数でもDateDiffやDateAddで年月計算をすることができますが、このクラスは数式として使用することを前提にしてオペレーション(+,-などの演算子)を定義しています。月初や月末の計算もつけています。
大まかな構成はこのようになります。

Public Class YearMonth
   (1) 変数定義
   (2) コンストラクタ
   (3) プロパティ
   (4) メソッド
End Class

(1)変数定義の部分は次のようにYear,Monthを定義します

   Public Property Year As Integer
   Public Property Month As Integer

   Sub Initialize(Year As Integer, Month As Integer)
      Me.Year = Year
      Me.Month = Month
   End Sub

(2)コンストラクタはいくつか用意すると便利です

   '日付を直接指定(大かっこをつけるとDateのような予約語でも使えます)
   Sub New([Date] As Date)
      Initialize([Date].Year, [Date].Month)
   End Sub

   'yyyy/MM形式の文字列を指定(形式は正規表現で厳しくチェックすべきなのでしょうが省略)
   Sub New(YMString As String)
      Dim Spl = Split(YMString, "/")
      Initialize(CInt(Spl(0)), CInt(Spl(1)))
   End Sub

   '他のインスタンスをクローン(月前進/月後退オプションあり)
   Sub New(Clone As YearMonth, Optional Forward As Integer = 0, Optional Backward As Integer = 0)
      CopyFrom(Clone)
      Me.Forward(Forward - Backward)
   End Sub

(3)プロパティもいくつか用意します

   '西暦下2桁
   Public ReadOnly Property Year2 As Integer
      Get
         Return Year Mod 100
      End Get
   End Property

   'yyyy/MM形式の文字列(Shadowsをつけるとカスタムクラスにも標準文字列(toString)を定義できる
   Public Shadows ReadOnly Property toString As String
      Get
         Return Format(Year, "0000") & "/" & Format(Month, "00")
      End Get
   End Property

   '月初
   Public ReadOnly Property FirstDate As Date
      Get
         Return DateValue(CStr(Year) & "/" & CStr(Month) & "/01")
      End Get
   End Property

   '月末
   Public ReadOnly Property LastDate As Date
      Get
         Return DateAdd(DateInterval.Day, -1, DateAdd(DateInterval.Month, 1, FirstDate))
      End Get
   End Property

(4)メソッドは月前進/月後退と演算子の定義です

   '月前進
   Public Sub Forward(Months As Integer)
      Dim Years As Integer = (Months ¥ 12)
      Months = Months Mod 12
      With Me
         .Year += Years
         .Month += Months
         Select Case .Month
            Case Is < 1
               .Year -= 1
               .Month += 12
            Case Is > 12
               .Year += 1
               .Month -= 12
         End Select
      End With
   End Sub

   '月後退
   Public Sub Backward(Months As Integer)
      Forward(0 - Months)
   End Sub

   '演算子(=)
   Public Shared Operator =(c1 As YearMonth, c2 As YearMonth) As Boolean
      If c1.Year = c2.Year AndAlso c1.Month = c2.Month Then
         Return True
      Else
         Return False
      End If
   End Operator

   '演算子(<>)
   Public Shared Operator <>(c1 As YearMonth, c2 As YearMonth) As Boolean
      Return Not (c1 = c2)
   End Operator

   '演算子(-)
   Public Shared Operator -(c1 As YearMonth, c2 As YearMonth) As Integer
      Return (c1.Year - c2.Year) * 12 + (c1.Month - c2.Month)
   End Operator

   '演算子(<)
   Public Shared Operator <(c1 As YearMonth, c2 As YearMonth) As Boolean
      Return (c1 - c2) < 0
   End Operator

   '演算子(<=)
   Public Shared Operator <=(c1 As YearMonth, c2 As YearMonth) As Boolean
      Return (c1 - c2) <= 0
   End Operator

   '演算子(>)
   Public Shared Operator >(c1 As YearMonth, c2 As YearMonth) As Boolean
      Return (c1 - c2) > 0
   End Operator

   '演算子(>=)
   Public Shared Operator >=(c1 As YearMonth, c2 As YearMonth) As Boolean
      Return (c1 - c2) >= 0
   End Operator

以上でクラスができました。使い方の例として今日から10か月後の月末を表示してみましょう。

   Dim YM = new YearMonth(Today)
   YM.Forward(10)
   Dim Target As Date = YM.LastDate()
   Msgbox(Format(Target, "yyyy/MM/dd")

2つの日付の月差を計算するときはこのようになります。

   Dim YM1 = new YearMonth(Date1)
   Dim YM2 = new YearMonth(Date2)
   Msgbox("月差=" & YM1-YM2)

というように便利に使用できます。実際の業務システムで使用するクラスは西暦と会計期を連動させるようなちょっと複雑なものになります。