2012年05月12日
川俣晶の縁側技術関連執筆情報『C#ショートコードプログラミング』読者サポート total 2873 count

C#ショートコードプログラミング・2.11差し替え

Written By: 川俣 晶連絡先

 既に訂正済みですが、更に訂正します。

 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);

    }

}

実行結果:

2011/02/28

 「月末」はややこしい計算を要します。毎月、最後の日付は変化するからです。一見、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);

    }

}