既に訂正済みですが、更に訂正します。
C#ショートコードプログラミングのp.43 2.11を以下の内容に差し替えます。
月末の日付は? §
依存バージョン: C# 1.0
月末の日付を知りたい!
OLD CODE:
using System;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var now = new DateTime(2011, 2, 1);
int[] smallMonths = { 4,6,9,11 };
int day = 31;
if (now.Month == 2)
{
day = 28;
if (DateTime.IsLeapYear(now.Year)) day++;
}
else if (smallMonths.Contains(now.Month))
{
day = 30;
}
var lastDay = new DateTime(now.Year, now.Month, day);
Console.WriteLine("{0:d}", lastDay);
}
}
NEW CODE1:
using System;
class Program
{
static void Main(string[] args)
{
var now = new DateTime(2011, 2, 1);
var lastDay = new DateTime(now.Year, now.Month, 1).AddMonths(1).AddDays(-1);
Console.WriteLine("{0:d}", lastDay);
}
}
実行結果:
「月末」はややこしい計算を要します。毎月、最後の日付は変化するからです。一見、OLD CASEはLINQを使い、閏年の判定を行うIsLeapYearメソッドも使いこなして上手くやっているように見えますが、実はもっと簡単にできます。
NEW CODEは、以下のアイデアでもっと簡単に処理ができています。
・月末とは、来月1日の1日手前である
つまり、今月1日の日付さえ作成できれば、あとは「来月に進める」「1日戻す」という2手順で計算終了です。DateTime構造体の場合、メソッドチェーンで1行に書くこともできます。この場合のポイントは、AddDaysメソッドに負数を渡すことで、1日前に戻している点です。
しかし、NEW CODE1には以下の問題があります。
・9999年12月の最終日を計算できない (計算途中でオーバーフローしてしまうから)
・AddMonthsとAddDaysの組み合わせが最終日を得ているという意図が分かりにくい
この2つの問題を解消するためにモアベターな書き換えを行ってみましょう。
NEW CODE2:
var lastDay = new DateTime(now.Year, now.Month, 1).AddMonths(1).AddDays(-1);
↓
var lastDay = new DateTime(now.Year,now.Month,DateTime.DaysInMonth(now.Year, now.Month));
ポイントは、DateTime.DaysInMonthメソッドです。このメソッドは、最終日を得ることが目的のメソッドではなく、その月の日数を得るメソッドです。しかし、結果的に得られる値は同じです。そのものではないものの、意図も分かりやすくなりました。
しかし、このコードには不満もあります。
・やや長さが伸びてしまったこと
・"var now = new DateTime(2011, 2, 1);"の", 1"が何の役割も持たず、冗長であること
そこで、DateTime型にこだわることをやめ、同じ出力が得られればそれで良いとして書き直してみましょう。
NEW CODE3:
using System;
class Program
{
static void Main(string[] args)
{
var year = 2011; var month = 2;
Console.WriteLine("{0:0000}/{1:00}/{2:00}", year, month, DateTime.DaysInMonth(year, month));
}
}
出力結果は同じですが、C#ソースコードのサイズは284バイトから235バイトに減りました。ちなみに、桁を揃えて桁が足りないときに頭に0が付く機能が不要なら書式指定の":0000"や":00"は不要になり、もっと短くなります。
もし、あくまでDateTime型の値が欲しいのであれば以下の書き換えもありです。
NEW CODE4:
using System;
class Program
{
static void Main(string[] args)
{
var year = 2011; var month = 2;
var lastDay = new DateTime(year, month, DateTime.DaysInMonth(year, month));
Console.WriteLine("{0:d}", lastDay);
}
}