14778


お世話になります。

C#のDateTimeやTimeSpanを使用して、指定した期間の中に、冬季(12/1から3/31)が  
何日間含まれているかを求めたいのですが、そういったメソッドなどはありますでしょうか。

一つ一つ計算していけば、力技でもできる気はしますが、要は、すでに用意されている  
メソッドなどでシンプルに作成したいと思っています。

うるう年が入っていたり、期間が二年以上またがっていたりすると、  
なかなか複雑になってきます。

極力シンプルにできる方法がありましたら、教えてください。

よろしくお願いいたします。


CC BY-SA 3.0
2015-08-24T01:21:42.230
14765
2015-08-27T01:20:48.220
2015-08-24T09:59:46.027
2238
9374
question
c# .net
C# 指定した期間内に冬季は何日間含まれるか


簡略な回答で失礼します。

> うるう年が入っていたり、期間が二年以上

[DaysInMonthメソッド](https://msdn.microsoft.com/ja-
jp/library/system.datetime.daysinmonth.aspx)を利用すれば、少なくとも上記の考慮は必要無いかと思います。

「From年月～To年月」をひと月ずつ繰り返し処理、冬季月なら上記メソッドで日数を求めて加算。  
あとはFrom年月日とTo年月日の日割り考慮くらいかな。

コード追記

[code]

        private static int CountWinterDays(DateTime start, DateTime end)
        {
            var count = 0;
            int[] winter = { 12, 1, 2, 3 };
            DateTime firstDayOfStartMonth = new DateTime(start.Year, start.Month, 1);
            DateTime lastDayOfEndMonth = new DateTime(end.Year, end.Month, 1).AddMonths(1).AddDays(-1);
    
            for (DateTime d = start; d < end; d = d.AddMonths(1))
            {
                if (Array.IndexOf(winter, d.Month) > -1)
                {
                    count = count + DateTime.DaysInMonth(d.Year, d.Month);
                    if (d.Year == start.Year && d.Month == start.Month)
                    {
                        count = count - (d - firstDayOfStartMonth).Days;
                    }
                    else if (d.Year == end.Year && d.Month == end.Month)
                    {
                        count = count - (lastDayOfEndMonth - d).Days;
                    }                   
    
                }
            }
            return count;            
        }
    
[/code]


CC BY-SA 3.0
2015-08-24T02:21:59.357
14769
2015-08-27T01:20:48.220
2015-08-27T01:20:48.220
5133
5133
14765
answer


真面目に数えるしかないように思いました。  
siamcatsさんが提案するようにひと月ずつ数えることもできるかもしれませんが、期間が何千年も含まれるのでなければ１日ずつ確認しても現実的な計算時間に収まるのではないでしょうか？
（１年で365回ループですし。）

[code]

    public static int GetWinterDays(DateTime start, DateTime end) {
        var count = 0;
        for (var date = start; date < end; date = date.AddTicks(TimeSpan.TicksPerDay)) {
            var month = date.Month;
            if (month <= 3 || 12 <= month)
                count++;
        }
        return count;
    }
    
[/code]

冬季の定義とか、境界を含む含まないとか問題に合わせて調整してください。

補足：  
kaorunさんの回答はLINQらしくするために`Enumerable.Range()`を使っていますが、そのせいで開始日終了日を含むかどうかがコードから読み取り辛くなっています。forループは泥臭いですが、ループの開始値と終了条件が分かりやすいため、このコードを提案しました。  
ちなみに [EnumerableEx.Generate()](https://github.com/Reactive-
Extensions/Rx.NET/blob/a13e3ff05bdded5cef2bf40bface22f8fa4ae316/Ix.NET/Source/System.Interactive/EnumerableEx.Creation.cs#L123)
を使うことで

[code]

    public static int GetWinterDays(DateTime start, DateTime end) {
        return EnumerableEx.Generate(start,
                                     date => date < end,
                                     date => date.AddTicks(TimeSpan.TicksPerDay),
                                     date => date.Month)
            .Count(month => month <= 3 || 12 <= month);
    }
    
[/code]

と書くこともできます。


CC BY-SA 3.0
2015-08-24T04:46:04.683
14778
2015-08-27T01:16:17.557
2015-08-27T01:16:17.557
4236
4236
14765
answer


「既に用意されているメソッドを出来るだけ利用」すると、年単位で日数を計算することは簡単です。  
さすがに、.NETの用意されているメソッドだけで2年以上のまたがった計算をする術はないです。

**TimeSpan.TotalDaysを利用した例**

[code]

    public static void Main()
    {
        int year = 2015;
        DateTime stdate = new DateTime(year - 1, 12,  1);  // 2014-12-01
        DateTime eddate = new DateTime(year    ,  3, 31);  // 2015-03-31
        TimeSpan sp = eddate - stdate;
        Console.WriteLine(sp.TotalDays + 1 + " 日間");
    }
    
[/code]

**ちなみに、うるう年の判定**

[code]

    Boolean leapYear = DateTime.IsLeapYear(2016);
    
[/code]

なお、余談ですが今年話題になった、`うるう秒`は.NETでは定義されていません。


CC BY-SA 3.0
2015-08-24T05:23:50.040
14780
2015-08-24T07:46:27.910
2015-08-24T07:46:27.910
2238
2238
14765
answer


やってることはsayuriさんの回答と全く同じですが、よりC#らしくLinqで練ってみました。

[code]

    private static int CountWinterDays(DateTime start, DateTime end)
    {
        return Enumerable.Range(0, (end - start).Days)
            .Select(c => start.AddDays(c))
            .Count(d => d.Month <= 3 || d.Month >= 12);
    }
    
[/code]

ワンライナーにもできますし、多少すっきりすると思います。


CC BY-SA 3.0
2015-08-24T07:18:25.350
14793
2015-08-24T07:42:05.460
2015-08-24T07:42:05.460
4236
2947
14765
answer