ある日が含まれる月の最初と最後の日付を求める処理と、それをライブラリ化して拡張メソッドの形で再利用できるようにする方法を説明する。
対象:.NET 2.0以降
ただし、DateTime構造体を使う方法は.NET 1.0から可能。
拡張メソッドを使ってライブラリ化できるのはVisual Studio 2008以降。
日付処理で頻繁に登場するのが、月の最初と最後の日の算出である。標準で備わっていてもおかしくないのに、.NET Frameworkには用意されていないのだ。そこで自前で実装することになるのだが、月初はともかくとして、月末の日付はどのようにして求めたらよいのだろうか? 本稿では、月初/月末の日付を求める方法を説明し、さらにライブラリ化する方法も紹介する。
本稿では、.NET Frameworkの初期から提供されているDateTime構造体(System名前空間)を使った処理だけでなく、.NET 2.0で追加されたDateTimeOffset構造体(System名前空間)を使う場合も紹介する。なお、本稿のコードはVisual Studio(以降、VSと略す)2012で検証しており、VS 2008以降で登場した言語機能も使用していることをお断りしておく。
ある日付が与えられたときに、その日付の月の最初の日(例えば、5月のいずれかの日なら5月1日)を求めるのは簡単だ。与えられた日付から年と月を取り出し、日は1日として新しく日付のオブジェクトを作ればよい。
日付がDateTime構造体で与えられる場合は、次のコードのようになる。
var theDay = new DateTime(2015, 5, 27);
var firstDayOfMonth = new DateTime(theDay.Year, theDay.Month, 1);
Console.WriteLine("{0}月の最初の日は {1}",
theDay.Month, firstDayOfMonth.ToString());
// 出力結果:
// 5月の最初の日は 2015/05/01 0:00:00
Dim theDay = New DateTime(2015, 5, 27)
Dim firstDayOfMonth = New DateTime(theDay.Year, theDay.Month, 1)
Console.WriteLine("{0}月の最初の日は {1}",
theDay.Month, firstDayOfMonth.ToString())
' 出力結果:
' 5月の最初の日は 2015/05/01 0:00:00
.NET 2.0で導入されたDateTimeOffset構造体で日付が与えられる場合は、次のコードのようになる。
var theDay = new DateTimeOffset(2015, 5, 27, 0, 0, 0, TimeSpan.FromHours(9.0));
var firstDayOfMonth = new DateTimeOffset(theDay.Year, theDay.Month, 1,
0, 0, 0, theDay.Offset);
Console.WriteLine("{0}月の最初の日は {1}",
theDay.Month, firstDayOfMonth.ToString());
// 出力結果:
// 5月の最初の日は 2015/05/01 0:00:00 +09:00
Dim theDay = New DateTimeOffset(2015, 5, 27,
0, 0, 0, TimeSpan.FromHours(9.0))
Dim firstDayOfMonth = New DateTimeOffset(theDay.Year, theDay.Month, 1,
0, 0, 0, theDay.Offset)
Console.WriteLine("{0}月の最初の日は {1}",
theDay.Month, firstDayOfMonth.ToString())
' 出力結果:
' 5月の最初の日は 2015/05/01 0:00:00 +09:00
月末の日付を求めるには、ちょっと工夫が必要だ。ここでは二通りの方法を紹介しよう。
一つ目は、その月の日数を使って算出する方法だ。月ごとの日数を配列に持たせて処理しているプログラムを見たこともあるが、.NETではそんなことをする必要はない。DateTime構造体のDaysInMonthメソッドを使えば、ある月の日数が取得できるのである(次のコード)。
var theDay = new DateTime(2016, 2, 14); // うるう年
int days = DateTime.DaysInMonth(theDay.Year, theDay.Month); // その月の日数
// DateTime構造体の場合
var lastDayOfMonth1 = new DateTime(theDay.Year, theDay.Month, days);
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth1.ToString());
// 出力結果:
// 2016年2月の最後の日は 2016/02/29 0:00:00
// DateTimeOffset構造体の場合
var lastDayOfMonth2 = new DateTimeOffset(theDay.Year, theDay.Month, days,
0, 0, 0, TimeSpan.FromHours(9.0));
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth2.ToString());
// 出力結果:
// 2016年2月の最後の日は 2016/02/29 0:00:00 +09:00
Dim theDay = New DateTime(2016, 2, 14) ' うるう年
Dim days As Integer = DateTime.DaysInMonth(theDay.Year, theDay.Month) ' その月の日数
' DateTime構造体の場合
Dim lastDayOfMonth1 = New DateTime(theDay.Year, theDay.Month, days)
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth1.ToString())
' 出力結果:
' 2016年2月の最後の日は 2016/02/29 0:00:00
' DateTimeOffset構造体の場合
Dim lastDayOfMonth2 = New DateTimeOffset(theDay.Year, theDay.Month, days,
0, 0, 0, TimeSpan.FromHours(9.0))
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth2.ToString())
' 出力結果:
' 2016年2月の最後の日は 2016/02/29 0:00:00 +09:00
もう一つは、翌月の1日を求め、そこから1日戻す方法だ。例えば、5月の末日を求めるには、6月1日を作り、そこから1日を引いて5月31日とするのである(次のコード)。
var theDay = new DateTime(2015, 12, 24);
// DateTime構造体の場合
var lastDayOfMonth1 = (new DateTime(theDay.Year, theDay.Month, 1))
.AddMonths(1)
.AddDays(-1.0);
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth1.ToString());
// 出力結果:
// 2015年12月の最後の日は 2015/12/31 0:00:00
// DateTimeOffset構造体の場合
var lastDayOfMonth2 = (new DateTimeOffset(theDay.Year, theDay.Month, 1,
0, 0, 0, TimeSpan.FromHours(9.0)))
.AddMonths(1)
.AddDays(-1.0);
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth2.ToString());
// 出力結果:
// 2015年12月の最後の日は 2015/12/31 0:00:00 +09:00
Dim theDay = New DateTime(2015, 12, 24)
' DateTime構造体の場合
Dim lastDayOfMonth1 = (New DateTime(theDay.Year, theDay.Month, 1)) _
.AddMonths(1) _
.AddDays(-1.0)
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth1.ToString())
' 出力結果:
' 2015年12月の最後の日は 2015/12/31 0:00:00
' DateTimeOffset構造体の場合
Dim lastDayOfMonth2 = (New DateTimeOffset(theDay.Year, theDay.Month,
1, 0, 0, 0, TimeSpan.FromHours(9.0))) _
.AddMonths(1) _
.AddDays(-1.0)
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay.Year, theDay.Month, lastDayOfMonth2.ToString())
' 出力結果:
' 2015年12月の最後の日は 2015/12/31 0:00:00 +09:00
このようなよく使う日付の処理は、拡張メソッドとしてライブラリ化しておくと便利である*1。ただし、この方法が使えるのはVisual Studio 2008からである。
そのライブラリ専用の名前空間を決めて(ここでは「dotNetTips1108VS2012CS/VB.MyExtensions」とした)、静的クラス(VBではモジュール)を作り、そこに拡張メソッドを実装する(次のコード)。
using System;
namespace dotNetTips1108VS2012CS.MyExtensions
{
public static class DateTimeUtil
{
// 月の最初の日を求める(DateTime版)
public static DateTime GetFirstDayOfMonth(this DateTime theDay)
{
return new DateTime(theDay.Year, theDay.Month, 1);
}
// 月の最後の日を求める(DateTime版)
public static DateTime GetLastDayOfMonth(this DateTime theDay)
{
int days = DateTime.DaysInMonth(theDay.Year, theDay.Month);
return new DateTime(theDay.Year, theDay.Month, days);
}
// 月の最初の日を求める(DateTimeOffset版)
public static DateTimeOffset GetFirstDayOfMonth(this DateTimeOffset theDay)
{
return new DateTimeOffset(theDay.Year, theDay.Month, 1,
0, 0, 0, theDay.Offset);
}
// 月の最後の日を求める(DateTimeOffset版)
public static DateTimeOffset GetLastDayOfMonth(this DateTimeOffset theDay)
{
int days = DateTime.DaysInMonth(theDay.Year, theDay.Month);
return new DateTimeOffset(theDay.Year, theDay.Month, days,
0, 0, 0, theDay.Offset);
}
}
}
Imports System.Runtime.CompilerServices
Namespace MyExtensions
' プロジェクト名は「dotNetTips1108VS2012VB」としてある。
' 従って、名前空間のフルネームは「dotNetTips1108VS2012VB. MyExtensions」となる。
Module DateTimeUtil
' 月の最初の日を求める(DateTime版)
<Extension()>
Public Function GetFirstDayOfMonth(theDay As DateTime) As DateTime
Return New DateTime(theDay.Year, theDay.Month, 1)
End Function
' 月の最後の日を求める(DateTime版)
<Extension()>
Public Function GetLastDayOfMonth(theDay As DateTime) As DateTime
Dim days As Integer = DateTime.DaysInMonth(theDay.Year, theDay.Month)
Return New DateTime(theDay.Year, theDay.Month, days)
End Function
' 月の最初の日を求める(DateTimeOffset版)
<Extension()>
Public Function GetFirstDayOfMonth(theDay As DateTimeOffset) As DateTimeOffset
Return New DateTimeOffset(theDay.Year, theDay.Month, 1,
0, 0, 0, theDay.Offset)
End Function
' 月の最後の日を求める(DateTimeOffset版)
<Extension()>
Public Function GetLastDayOfMonth(theDay As DateTimeOffset) As DateTimeOffset
Dim days As Integer = DateTime.DaysInMonth(theDay.Year, theDay.Month)
Return New DateTimeOffset(theDay.Year, theDay.Month, days,
0, 0, 0, theDay.Offset)
End Function
End Module
End Namespace
上の拡張メソッドを利用して月初/月末の日付を求めるコードは次のようになる。拡張メソッドを使うには、その名前空間をファイルの先頭でusing/Importする必要がある。
using System;
using dotNetTips1108VS2012CS.MyExtensions; // 拡張メソッドのある名前空間
……省略……
// DateTime構造体の場合
var theDay1 = new DateTime(2015, 5, 27);
var firstDayOfMonth1 = theDay1.GetFirstDayOfMonth();
Console.WriteLine("{0}年{1}月の最初の日は {2}",
theDay1.Year, theDay1.Month, firstDayOfMonth1.ToString());
var theDay2 = new DateTime(2016, 2, 14); // うるう年
var lastDayOfMonth1 = theDay2.GetLastDayOfMonth();
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay2.Year, theDay2.Month, lastDayOfMonth1.ToString());
// 出力結果:
// 2015年5月の最初の日は 2015/05/01 0:00:00
// 2016年2月の最後の日は 2016/02/29 0:00:00
// DateTimeOffset構造体の場合
var theDay3 = new DateTimeOffset(2015, 5, 27,
0, 0, 0, TimeSpan.FromHours(9.0));
var firstDayOfMonth2 = theDay3.GetFirstDayOfMonth();
Console.WriteLine("{0}年{1}月の最初の日は {2}",
theDay3.Year, theDay3.Month, firstDayOfMonth2.ToString());
var theDay4 = new DateTimeOffset(2016, 2, 14,
0, 0, 0, TimeSpan.FromHours(9.0)); // うるう年
var lastDayOfMonth2 = theDay4.GetLastDayOfMonth();
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay4.Year, theDay4.Month, lastDayOfMonth2.ToString());
// 出力結果:
// 2015年5月の最初の日は 2015/05/01 0:00:00 +09:00
// 2016年2月の最後の日は 2016/02/29 0:00:00 +09:00
Imports dotNetTips1108VS2012VB.MyExtensions ' 拡張メソッドのある名前空間
……省略……
' DateTime構造体の場合
Dim theDay1 = New DateTime(2015, 5, 27)
Dim firstDayOfMonth1 = theDay1.GetFirstDayOfMonth()
Console.WriteLine("{0}年{1}月の最初の日は {2}",
theDay1.Year, theDay1.Month, firstDayOfMonth1.ToString())
Dim theDay2 = New DateTime(2016, 2, 14) ' うるう年
Dim lastDayOfMonth1 = theDay2.GetLastDayOfMonth()
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay2.Year, theDay2.Month, lastDayOfMonth1.ToString())
' 出力結果:
' 2015年5月の最初の日は 2015/05/01 0:00:00
' 2016年2月の最後の日は 2016/02/29 0:00:00
' DateTimeOffset構造体の場合
Dim theDay3 = New DateTimeOffset(2015, 5, 27,
0, 0, 0, TimeSpan.FromHours(9.0))
Dim firstDayOfMonth2 = theDay3.GetFirstDayOfMonth()
Console.WriteLine("{0}年{1}月の最初の日は {2}",
theDay3.Year, theDay3.Month, firstDayOfMonth2.ToString())
Dim theDay4 = New DateTimeOffset(2016, 2, 14,
0, 0, 0, TimeSpan.FromHours(9.0)) ' うるう年
Dim lastDayOfMonth2 = theDay4.GetLastDayOfMonth()
Console.WriteLine("{0}年{1}月の最後の日は {2}",
theDay4.Year, theDay4.Month, lastDayOfMonth2.ToString())
' 出力結果:
' 2015年5月の最初の日は 2015/05/01 0:00:00 +09:00
' 2016年2月の最後の日は 2016/02/29 0:00:00 +09:00
利用可能バージョン:.NET Framework 2.0以降
カテゴリ:クラスライブラリ 処理対象:日付と時刻
使用ライブラリ:DateTime構造体(System名前空間)
使用ライブラリ:DateTimeOffset構造体(System名前空間)
使用ライブラリ:TimeSpan構造体(System名前空間)
関連TIPS:日時や時間間隔の加減算を行うには?
関連TIPS:週の始まりの日付を求めるには?
関連TIPS:西暦年が閏(うるう)年かどうかを判別するには?[C#、VB]
関連TIPS:n日後、nカ月後、n年後の日付を求めるには?[C#、VB]
関連TIPS:UTC(世界協定時)を取得するには?[C#、VB]
関連TIPS:指定した月から特定の曜日の日付を取得するには?[C#、VB]
Copyright© Digital Advantage Corp. All Rights Reserved.