-
JavaScript でも当然ですが配列が使えます。VB.NETの配列と似たところがありますので、VB.NETを使っている方は特に問題無く理解ができます。 先ずは、配列の枠だけを宣言してみます。以下のソースでは空の配列を宣言し値を追加しています。
以下の処理で console.log を使用していますが、これによりブラウザのコンソール画面に変数の内容が表示されます。空の配列を宣言と値の追加
//空の配列宣言 var arrNum = new Array(); //未定義の指標での代入で追加 arrNum[0] = 100; arrNum[1] = 200; arrNum[2] = 300; console.log(arrNum); // Array(3) [ 100, 200, 300 ] と表示される上の例では Array クラスを宣言し生成していますが以下の方法でも同じことが出来ます。
値の追加は配列のメソッド push を使って最後尾に追加しています。
空の配列を宣言と値の追加その2
//空の配列宣言 var arrNum = []; //配列のpush メソッドで配列の最後尾に追加する arrNum.push(100); arrNum.push(200); arrNum.push(300); console.log(arrNum); // Array(3) [ 100, 200, 300 ] と表示される
そこで、配列の初期化の方法ですが、上記の例の Array の括弧の中に要素をカンマ区切りで行えます。Arrayでの配列の初期化
//空の配列宣言 var arrNum = new Array(100, 200, 300); console.log(arrNum); // Array(3) [ 100, 200, 300 ] と表示される[] の中にも同様にできます。こちらの方が簡単です。
配列の初期化では要素の指標が0(ゼロ)から振られますので注意が必要です。以下の例では配列の参照を行って合計の計算を行っています。[]での配列の初期化
//空の配列宣言 var arrNum = [100, 200, 300]; console.log(arrNum); // Array(3) [ 100, 200, 300 ] と表示される //配列の参照例としての合計計算 var sum = 0; for(i = 0; i < arrNum2.length; i++) { sum += arrNum2[i]; //指標は[0]から振られている } console.log(sum); // 600 と表示される
配列の要素は数値データとは限らず文字列を宣言することもできます。 以下の例は [] で要素が4個の文字列配列の初期化宣言を行っています。要素が文字列の配列の初期化
//配列宣言(単純な文字列配列) var arrStr = ["aaaa", "bb", "ccccc", "ddd"]; console.log(arrStr); // Array(4) [ "aaaa", "bb", "ccccc", "ddd" ] と表示される //配列内の文字列連結 var str = ""; for(i = 0; i < arrStr.length; i++) { str += arrStr[i]; } console.log(str); // aaaabbcccccddd と表示される
尚、配列の宣言で文字列と数値を混在させたものを宣言することが出来ます。 以下の例がそれですが、 arrMix の中身は混在していることが分かります。 文字列連結において文字列と数値の + 処理では数値が文字列に暗黙の変換が行われるため、結果が文字列になります。要素が文字列の配列の初期化
//配列宣言 var arrMix = ["aaaa", 222, "ccccc", 4444]; console.log(arrMix); // Array(4) [ "aaaa", 222, "ccccc", 4444 ] と表示される //配列内の文字列連結 var str = ""; for(i = 0; i < arrMix.length; i++) { str += arrMix[i]; // 数値が文字列に暗黙の変換 } console.log(str); // aaaa222ccccc4444 と表示される
PR -
前回はWord文書の中で文字列を検索しその部分を指定された文字列で置き換える処理を作成しましたが、 カーソルの位置が最後の置換処理を行った場所に移動します。
⇒access vba でのWord文書の文字列検索と置換について(Word処理用クラスに文字列置換関数を追加)
これでは文書の途中にカーソルがありますので、文書の先頭が見たい場合には都合が悪いので、カーソルを先頭に移動する関数を作成します。
カーソルの移動には Bookmark オブジェクトを利用します。 このオブジェクトには以下の様に定義済みのものがありますのでこれを利用します。Bookmark 説明 \Sel 現在の選択範囲またはカーソル位置 \PrevSel1 最後に編集を行った範囲 \PrevSel2 2個前に編集を行った範囲 \StartOfSel 現在の選択範囲の開始位置 \EndOfSel 現在の選択範囲の終了位置 \Line 現在の行または現在の選択範囲の最初の行 \Char 現在の文字。範囲が選択されていない場合はカーソルの右側の文字、また範囲が選択されている場合はその範囲の先頭文字を指します \Para カーソルのある段落か、複数の段落を選択すると、選択範囲の最初の段落 \Section 現在のカーソルのあるセクションか、選択範囲に複数のセクションが含まれている場合、最初のセクション \Doc 作業中の文書全体 \Page 現在のページ \StartOfDoc 文書の先頭 \EndOfDoc 文書の末尾 \Cell テーブル内の現在のセル、複数のセルが選択されている場合は先頭のセル \Table 現在のテーブル、複数のテーブルが選択されている場合は先頭テーブル
この Bookmark オブジェクトに \StartOfDoc を指定し、そのオブジェクトを選択することでカーソル移動が行えます。 関数としては以下の通りで、特に難しくはありません。
尚、 Range オブジェクトを使ってもできます。
Word文書カーソル先頭移動関数
'****************************************************************************** ' Word文書・カーソル先頭移動 '****************************************************************************** ' 関数名 : WordMoveToTop() '****************************************************************************** Public Sub WordMoveToTop() On Error GoTo Err_WordMoveToTop 'カーソル先頭移動 WordDoc.Bookmarks("\StartOfDoc").Select '以下の方法でもOK 'WordDoc.Range(0, 0).Select Exit_WordMoveToTop: Exit Sub Err_WordMoveToTop: MsgBox Err.Description Resume Exit_WordMoveToTop End Subこの関数をWord処理用クラスに追加して、それを使用する例として、access フォームに1個のボタンのみを設置し、そのクリックイベントでワード文書を開く様にしてみます。
Word文書文字列置換関数の使用例
Option Compare Database ' クラスWordオブジェクト Private mclsWord As clsWord '------------------------------------------------------------------------------ ' フォームロード時イベント '------------------------------------------------------------------------------ Private Sub Form_Load() Set mclsWord = New clsWord End Sub '------------------------------------------------------------------------------ ' フォームクローズイベント '------------------------------------------------------------------------------ Private Sub Form_Close() 'ワードクローズ mclsWord.WordClose 'Wordオブジェクト解放 Set mclsWord = Nothing End Sub '------------------------------------------------------------------------------ ' コマンド1開始 '------------------------------------------------------------------------------ Private Sub コマンド1_Click() Dim strPath As String 'ワードファイルを読み込み、文章を編集する状態にする strPath = Application.CurrentProject.path & "\" & "find-test.docx" 'ワードファイルを読込み編集状態で表示する mclsWord.WordOpen strPath, True ' Dim arrVaKey() As Variant Dim arrVaVal() As Variant arrVaKey = Array("@test1@", _ "@test2@", _ "@test3@", _ "@test4@") arrVaVal = Array("テスト1", _ "テスト2", _ "テスト3", _ "テスト4-1" & vbCrLf & "テスト4-2" & vbCrLf & "テスト4-3") '置換処理(左揃えを行わない) mclsWord.WordReplace arrVaKey, arrVaVal, False arrVaKey = Array("@test5@") arrVaVal = Array("テスト5-1" & vbCrLf & "テスト5-2" & vbCrLf & "テスト5-3") '置換処理(左揃えを行う) mclsWord.WordReplace arrVaKey, arrVaVal, True '先頭へ移動 mclsWord.WordMoveToTop End Sub
「コマンド1開始」を押下することで、ワードが起動し置換処理の結果が表示され、カーソルが先頭に移動していることが分かります。
関連する記事
⇒access vba でWordを起動しファイル保存時のファイル名変更について(DocumentBeforeSave : Word保存前イベント)
⇒access vba でExcelを起動しファイル保存でのファイル名変更について(WorkbookBeforeSave : Excel保存前イベント)
⇒access vba でのWord処理用クラスの作成(ファイル保存時イベント[DocumentBeforeSave]でのファイル名変更処理を含む)
⇒access vba でのWord文書の文字列検索と置換について(Word処理用クラスに文字列置換関数を追加)
-
access vba でのWord用クラス作成について以下のページで説明しましたが、 今回はWord文書の中で文字列を検索し、その部分を指定された文字列で置き換える処理をクラスに追加してみます。
⇒access vba でのWord処理用クラスの作成(ファイル保存時イベント[DocumentBeforeSave]でのファイル名変更処理を含む)
関数を追加する前に今回使用する Find オブジェクトについてみてみます。
以下の例は抜粋なのですが、Word.Documentオブジェクトとして WrdDoc が既に生成されているものとします。
Word.Documentオブジェクトの Content プロパティの Find オブジェクトを利用します。
(この Content プロパティは「文書全体を示す Range オブジェクト」のことで、 Range オブジェクトの中の Find オブジェクトとなります)
Find オブジェクトの以下の各プロパティをそれぞれ指定し Execute メソッドで処理種別を指定し、 置換処理等を行います。この処理種別には多くのものがありますので、それぞれについては正規の説明書を見て下さい。
今回は文字列の置換ですので、Replace を指定しています。 Replace の引数としては置換する文字列の個数 (1つだけ:wdReplaceOne、すべて:wdReplaceAll、または置換しない:wdReplaceNone) を指定します。
この例は、文書全体の「Word」を「ワード」に置き換える処理を行います。Find オブジェクトの実行例
With WrdDoc.Content.Find .Text = "Word" '置換元の文字列 .Replacement.Text = "ワード" '置換後の文字列 .Wrap = wdFindContinue '検索折返し指定 '(文書の途中から検索し、最後に到達した時に先頭から続行) .Execute Replace:=wdReplaceAll End With基本的にはこの処理を複数の文字列の置換が出来る様に以下の様な関数にしました。
関数の引数としては置換される文字列と、置換文字列を配列で指定します。 尚、vba では配列の場合は Variant 型として宣言します。 置換される文字列配列と、置換文字列配列の個数のチェックは行っていないので、同じにする必要があります。
この関数での注意点は、置換文字列の中に改行を含んでいる場合に、そのまま一括で置換してしまうと 1個目の改行以降が左側に寄ってしまいます。そのため、改行で文字列を分解し、1個ごとに文字列と改行を出力する様にしています。
こうすることで、置換元の位置の左端に合わせた感じで改行後の文字列が揃うはずです。
Word文書文字列置換関数
'****************************************************************************** ' Word文書文字列置換処理 '****************************************************************************** ' 関数名 : WordReplace() ' 引数 : P_vaKey() '差替KEY文字列の配列 ' : P_vaVal() '差替文字列の配列 ' : P_blnCrLfLeftAlign '左揃えフラグ '****************************************************************************** Public Sub WordReplace(P_vaKey() As Variant, P_vaVal() As Variant, P_blnCrLfLeftAlign As Boolean) On Error GoTo Err_WordReplace Dim i As Integer For i = 0 To UBound(P_vaKey) If InStr(P_vaVal(i), vbCrLf) = 0 Or P_blnCrLfLeftAlign = False Then '置換文字列に改行コードが無い場合、または左揃えフラグがOFF '文字を検索、置換する With WordDoc.Content.Find .Text = P_vaKey(i) .Forward = True .Replacement.Text = Replace(CStr(P_vaVal(i)), vbCrLf, vbCr) .Wrap = wdFindContinue .MatchFuzzy = True .Execute Replace:=wdReplaceAll End With Else '置換文字列に改行コードがある場合 '検索文字列の先頭にカーソル移動 With WordApp.Selection.Find .Text = P_vaKey(i) .Execute End With '文字を検索、置換する With WordDoc.Content.Find .Text = P_vaKey(i) .Forward = True .Replacement.Text = "" '取り敢えずクリア .Wrap = wdFindContinue .MatchFuzzy = True .Execute Replace:=wdReplaceAll End With Dim arr() As String arr = Split(P_vaVal(i), vbCrLf) '改行で分解した文字列をカーソル位置から順次出力 Dim ix As Integer For ix = 0 To UBound(arr) If ix > 0 Then '2個目以降は改行出力 WordApp.Selection.TypeText vbCr End If WordApp.Selection.TypeText arr(ix) Next ix End If Next i Exit_WordReplace: Exit Sub Err_WordReplace: MsgBox Err.Description Resume Exit_WordReplace End Subこの関数をWord処理用クラスに追加して、それを使用する例として、access フォームに1個のボタンのみを設置し、そのクリックイベントでワード文書を開く様にしてみます。
Word文書文字列置換関数の使用例
Option Compare Database ' クラスWordオブジェクト Private mclsWord As clsWord '------------------------------------------------------------------------------ ' フォームロード時イベント '------------------------------------------------------------------------------ Private Sub Form_Load() Set mclsWord = New clsWord End Sub '------------------------------------------------------------------------------ ' フォームクローズイベント '------------------------------------------------------------------------------ Private Sub Form_Close() 'ワードクローズ mclsWord.WordClose 'Wordオブジェクト解放 Set mclsWord = Nothing End Sub '------------------------------------------------------------------------------ ' コマンド1開始 '------------------------------------------------------------------------------ Private Sub コマンド1_Click() Dim strPath As String 'ワードファイルを読み込み、文章を編集する状態にする strPath = Application.CurrentProject.path & "\" & "find-test.docx" 'ワードファイルを読込み編集状態で表示する mclsWord.WordOpen strPath, True ' Dim arrVaKey() As Variant Dim arrVaVal() As Variant arrVaKey = Array("@test1@", _ "@test2@", _ "@test3@", _ "@test4@") arrVaVal = Array("テスト1", _ "テスト2", _ "テスト3", _ "テスト4-1" & vbCrLf & "テスト4-2" & vbCrLf & "テスト4-3") '置換処理(左揃えを行わない) mclsWord.WordReplace arrVaKey, arrVaVal, False arrVaKey = Array("@test5@") arrVaVal = Array("テスト5-1" & vbCrLf & "テスト5-2" & vbCrLf & "テスト5-3") '置換処理(左揃えを行う) mclsWord.WordReplace arrVaKey, arrVaVal, True End Sub
「コマンド1開始」を押下することで、ワードが起動し置換処理の結果が表示されます。 元のワードの文書と結果の文書は以下の画像の様になります。 左揃え処理を行う個所と、行わない個所の違いが分かると思います。
関連する記事
⇒access vba でWordを起動しファイル保存時のファイル名変更について(DocumentBeforeSave : Word保存前イベント)
⇒access vba でExcelを起動しファイル保存でのファイル名変更について(WorkbookBeforeSave : Excel保存前イベント)
⇒access vba でのWord処理用クラスの作成(ファイル保存時イベント[DocumentBeforeSave]でのファイル名変更処理を含む)
⇒access vba Bookmarkオブジェクトで文書の先頭にカーソルを移動する(Word処理用クラスに文書の先頭移動関数を追加)
-
今回は日付型データの比較を取り扱います。日付型データはその変数名を使って直接 If 文等で比較することが出来ます。 数値データの様に日付型データを比較演算子で結んで値の大小等が比較できます。
以下の例は2個の日付型データに 2019/02/01 と 2019/02/02 の異なる日付を設定し、等しいかどうかの判定を If 文で行っています。簡単な日付型(Date)データの比較
Dim dateWK1 As Date = New DateTime(2019, 2, 1) '2019/02/01 Dim dateWK2 As Date = New DateTime(2019, 2, 2) '2019/02/02 '2個の日時の比較 If dateWK1 = dateWK2 Then Console.WriteLine("dateWK1 と dateWK2 は等しい") Else Console.WriteLine("dateWK1 と dateWK2 は異なる") End If Console.WriteLine("dateWK1:" & dateWK1.ToString("yyyy/MM/dd HH:mm:ss")) Console.WriteLine("dateWK2:" & dateWK2.ToString("yyyy/MM/dd HH:mm:ss"))実行結果は当然、2個の日付型データが異なっていることが分かります。
dateWK1 と dateWK2 は異なる dateWK1:2019/02/01 00:00:00 dateWK2:2019/02/02 00:00:00
上の例では問題無く判定が行われましたが、以下の例を見て下さい。 上の例の様に、2個の日付型データは変数を Date として宣言し、 最初の変数に Now() 関数で現在日時を設定し、さらにその後10秒待った後で、2個目の変数に現在日時を設定しています。 その後、2個の変数を比較すると、やはり異なっているとの判定がされます。
2個の変数の中身を表示してやれば、最初の時刻から10秒経過した時刻が設定されていることが分かります。
これは当然と言えばそうなのですが、変数が Date 型だからと勘違いして、日付部分しか比較しないのかと考えると ミスが発生します。お恥ずかしい話、私自身も以前にはこれでバグを発生させたことがありました。日付型(Date)データの比較
'現在日時を取得 Dim dateWK1 As Date = Now() '10秒待つ System.Threading.Thread.Sleep(10 * 1000) '現在日時を取得 Dim dateWK2 As Date = Now() '2個の日時の比較 If dateWK1 = dateWK2 Then Console.WriteLine("dateWK1 と dateWK2 は等しい") Else Console.WriteLine("dateWK1 と dateWK2 は異なる") End If Console.WriteLine("dateWK1:" & dateWK1.ToString("yyyy/MM/dd HH:mm:ss")) Console.WriteLine("dateWK2:" & dateWK2.ToString("yyyy/MM/dd HH:mm:ss")) '日付型データの比較関数 Console.WriteLine("Date.Compare(dateWK1, dateWK2):" & Date.Compare(dateWK1, dateWK2))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK1 と dateWK2 は異なる dateWK1:2019/02/06 18:16:49 dateWK2:2019/02/06 18:16:59 Date.Compare(dateWK1, dateWK2):-1
Date 型にはプロパティとして Date というものを持っているのでこれを使うと 純然たる日付のみのデータが取得できますので、日付のみの比較が正しく行えます。 以下のソースは If 文の部分を変更して実行してみました。日付型(Date)データの日付のみの比較
'現在日時を取得 Dim dateWK1 As Date = Now() '10秒待つ System.Threading.Thread.Sleep(10 * 1000) '現在日時を取得 Dim dateWK2 As Date = Now() '2個の日時の比較 If dateWK1.Date = dateWK2.Date Then Console.WriteLine("dateWK1 と dateWK2 は等しい") Else Console.WriteLine("dateWK1 と dateWK2 は異なる") End If上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK1 と dateWK2 は等しい
さらに日付型の Compare メソッドを使って比較処理ができます。
Date.Compare( para1, para2 ) ... 「-1, 0, 1」のInteger型の値を返します。 結果の値の意味は以下の通りです。 ・[-1] : para1 < para2 ・[ 0] : para1 = para2 ・[ 1] : para1 > para2
日付型の Compare メソッドでの比較
Dim date0 As New DateTime(2019, 8, 27, 10, 30, 0) Dim date1 As New DateTime(2019, 8, 26, 10, 10, 0) Dim date2 As New DateTime(2019, 8, 27, 10, 15, 0) Dim date3 As New DateTime(2019, 8, 28, 10, 20, 0) ' 日付のみで比較 Console.WriteLine("Compare(date1.Date, date0.Date) = {0}", Date.Compare(date1.Date, date0.Date)) Console.WriteLine("Compare(date2.Date, date0.Date) = {0}", Date.Compare(date2.Date, date0.Date)) Console.WriteLine("Compare(date3.Date, date0.Date) = {0}", Date.Compare(date3.Date, date0.Date)) ' 日付の時刻を含み比較 Console.WriteLine("Compare(date1, date0) = {0}", Date.Compare(date1, date0)) Console.WriteLine("Compare(date2, date0) = {0}", Date.Compare(date2, date0)) Console.WriteLine("Compare(date3, date0) = {0}", Date.Compare(date3, date0))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
Compare(date1.Date, date0.Date) = -1 Compare(date2.Date, date0.Date) = 0 Compare(date3.Date, date0.Date) = 1 Compare(date1, date0) = -1 Compare(date2, date0) = -1 Compare(date3, date0) = 1
DateTime型の CompareTo メソッドを使っても同様の結果が得られます。
DateTime型の CompareTo メソッドでの比較
Dim date0 As New DateTime(2019, 8, 27, 10, 30, 0) Dim date1 As New DateTime(2019, 8, 26, 10, 10, 0) Dim date2 As New DateTime(2019, 8, 27, 10, 15, 0) Dim date3 As New DateTime(2019, 8, 28, 10, 20, 0) ' さらにDateTime型のCompareToを利用 Console.WriteLine("date1.Date.CompareTo(date0.Date) = {0}", date1.Date.CompareTo(date0.Date)) Console.WriteLine("date2.Date.CompareTo(date0.Date) = {0}", date2.Date.CompareTo(date0.Date)) Console.WriteLine("date3.Date.CompareTo(date0.Date) = {0}", date3.Date.CompareTo(date0.Date))
関連する記事
⇒日付型データの使い方 :[Date,DateTime]
⇒日付型データの使い方(月末、年末の日付取得)
-
日付を取り扱う処理は多いもので、請求処理や月締処理などで対象月の月末の日付を計算する場合があります。 また、年度の替わりが年末である場合には年末の日付を計算する必要があります。
月末で問題なのは2月で、うるう年の月末は29日のため、そのことを考える必要があります。
月末の取得は、私がよく使っているのですが指定された年月での月の最初日から翌月の最初日を計算し、 その結果の1日前を計算しています。 また、DateTime の DaysInMonth メソッドを使って該当年月の日数を使うことでも可能です。
それでは、指定された年月日に対応する月末の日付を取得する方法と、年末の日付を取得する方法を以下に示します。月末、年末の日付取得
'月末の取得 Dim dateWK As DateTime dateWK = Now() Console.WriteLine("現在:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(dateWK.Year, dateWK.Month, 1) '指定年月での最初日 dateWK = dateWK.AddMonths(1).AddDays(-1) '次月の1日前を計算 Console.WriteLine("月末の取得1:" & dateWK.ToString("yyyy/MM/dd")) '[DateTime.DaysInMonth]メソッドによる方法 dateWK = Me.DateTimePicker1.Value dateWK = New Date(dateWK.Year, dateWK.Month, DateTime.DaysInMonth(dateWK.Year, dateWK.Month)) Console.WriteLine("月末の取得2:" & dateWK.ToString("yyyy/MM/dd")) '年末の取得(直接12月31日指定) dateWK = Me.DateTimePicker1.Value dateWK = New Date(dateWK.Year, 12, 31) '指定年での最終日(直接12月31日指定) Console.WriteLine("年末の取得の取得1:" & dateWK.ToString("yyyy/MM/dd")) '年末の取得(指定年の翌年の元旦の1日前の計算) dateWK = Me.DateTimePicker1.Value dateWK = New Date(dateWK.Year, 1, 1) '指定年での最初日(元旦) dateWK = dateWK.AddYears(1).AddDays(-1) '次年の1日前を計算 Console.WriteLine("年末の取得の取得1:" & dateWK.ToString("yyyy/MM/dd"))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
現在:2019/02/01 月末の取得1:2019/02/28 月末の取得2:2019/02/28 年末の取得の取得1:2019/12/31 年末の取得の取得1:2019/12/31
ところで、年の加算を行った場合にうるう年を跨いだ場合にどうなるのかが少し気になりましたので、 以下の様なソースで実行してみました。1年後の計算(AddYearsメソッド)
'1年後の計算(AddYearsメソッド) dateWK = New Date(2019, 2, 28) '指定年月:2019/02/28 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう年では無い年の1年後:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(2020, 2, 28) '指定年月:2020/02/28 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう年の場合の年の1年後:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(2020, 2, 29) '指定年月:2020/02/29 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう日からの1年後:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(2019, 3, 1) '指定年月:2019/03/01 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう日を挟む場合の1年後:" & dateWK.ToString("yyyy/MM/dd"))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
2019/02/28 を指定した場合、2019年はうるう年では無いので、1年後の計算は当然 2020/02/28 になります。
2020/02/28 を指定した場合、2020年はうるう年なので 2020/02/29 があるので1日ずれるかと思いきや、1年後の計算は当然 2021/02/28 になります。 これはうるう日を特別に計算しているのか、もしくはただ単に年の数値を1加算しているのかはわかりません。
2020/02/29 を指定した場合でも結果は 1年後の計算は当然 2021/02/28 になります。やはり、うるう日を加味されている様です。
このことは、ある指定日からの1年間を計算する場合に問題になるかもしれません。 1年は365日とするのかそれともうるう日を含めるのかはその時々の仕様によると思います。うるう年では無い年の1年後:2020/02/28 うるう年の場合の年の1年後:2021/02/28 うるう日からの1年後:2021/02/28 うるう日を挟む場合の1年後:2020/03/01
関連する記事
⇒日付型データの使い方 :[Date,DateTime]
⇒日付型データの比較について
⇒文字列から数値型への変換(parse - tryparse)
⇒オブジェクト型から数値型への変換(TryParse)