2つの日付間の月数の違い


334

C#で2つの日付間の月の差を計算するにはどうすればよいですか?

DateDiff()C#にVBのメソッドに相当するものはありますか?年が離れている2つの日付の月の違いを見つける必要があります。ドキュメントは私が次のTimeSpanように使用できると言っています:

TimeSpan ts = date1 - date2;

しかし、これは私に日数でデータを与えます。毎月30日というわけではなく、2つのオペランドの値がかなり離れているため、この数を30で除算したくないのですが、30で除算すると、誤った値になる可能性があります。

助言がありますか?


27
「月の違い」を定義すると、「2010年5月1日」と「2010年6月16日」の月の違いは何ですか?1.5、1、または何か他に?
チェンチェン

7
または、この点をさらに強調するために、2010年12月31日と2011年1月1日の間の月の違いは何ですか?昼間にもよりますが、これはわずか1秒の違いである可能性があります。これを1ヶ月の差として数えますか?
stakx-2011年

ここではケースが単純で短いコードは、あなたはまだ、答えを得る、この見ることができなかったですPOST stackoverflow.com/questions/8820603/...
wirol

11
ダニー:1か月と15日。stakx:0か月と1日。ポイントは、月のコンポーネントを取得することです。これは私にはかなり自明であり、良い質問です。
カークウォル

回答:


462

月の日が無関係であると仮定します(つまり、2011.1.1と2010.12.31の差は1)。date1> date2は正の値を与え、date2> date1は負の値を与えます。

((date1.Year - date2.Year) * 12) + date1.Month - date2.Month

または、2つの日付の間の「平均月」のおおよその数が必要であると仮定すると、非常に大きな日付の違いを除いて、以下はすべて機能するはずです。

date1.Subtract(date2).Days / (365.25 / 12)

後者のソリューションを使用する場合、単体テストでは、アプリケーションが動作するように設計されている最も広い日付範囲を示し、それに応じて計算結果を検証する必要があります。


更新(ゲイリーのおかげで)

「平均月数」方式を使用する場合、「1年あたりの平均日数」に使用するもう少し正確な数値は365.2425です。


3
@Kurru-365/12は、1か月の日数の平均の長さの概算にすぎません。これは不正確な測定です。日付範囲が小さい場合、この不正確さは許容できますが、非常に大きな日付範囲の場合、この不正確さが大きくなる可能性があります。
Adam Ralph、

21
日構成要素を考慮する必要があると思います。このようなもの (date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)
DrunkCoder

2
@DrunkCoderは、特定のシステムの要件によって異なります。場合によっては、ソリューションが実際に最良の選択になることがあります。たとえば、2つの日付が月31日、月30日、2月28日、または2月29日にわたる場合に何が起こるかを考慮することが重要です。数式の結果がシステムに必要なものを提供する場合、それは明らかに正しい選択です。そうでない場合は、別のものが必要です。
アダムラルフ

6
次に、アダムが言ったことに、数年かけてアクチュアリーのコードを書きました。一部の計算では、日数で除算し、30で切り上げて月次の数値を得ました。場合によっては、すべての日付が月の最初から始まると想定される月をカウントし、それに応じて月全体をカウントします。日付の計算に関しては、最善の方法はありません。あなたがコードを書いている顧客でない限り、おそらくこれをチェーンに押し上げて、おそらく顧客の会計士がそれを明確にしてください。
Binary Worrier 2013

1
365.2425は、グレゴリオ暦では、より正確な日数です(使用している場合)。ただし、DateTime.MaxValue(1月1日、10000)では、約59日間しか違いません。また、年の定義は、パースペクティブen.wikipedia.org/wiki/Yearによって大きく異なる場合があります。
ゲイリー

205

ここで返すための包括的なソリューションであるDateTimeSpanと類似し、TimeSpanそれが時間コンポーネントに加えて、すべての日付コンポーネントを含むことを除い。

使用法:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    var dateSpan = DateTimeSpan.CompareDates(compareTo, now);
    Console.WriteLine("Years: " + dateSpan.Years);
    Console.WriteLine("Months: " + dateSpan.Months);
    Console.WriteLine("Days: " + dateSpan.Days);
    Console.WriteLine("Hours: " + dateSpan.Hours);
    Console.WriteLine("Minutes: " + dateSpan.Minutes);
    Console.WriteLine("Seconds: " + dateSpan.Seconds);
    Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}

出力:

年:1か
月:5
日:27
時間:1
分:36
秒:50
ミリ秒:0

便宜上、ロジックをDateTimeSpan構造体にまとめましたが、必要に応じてメソッドを移動CompareDatesできます。また、どちらの日付が前に来るかは関係ありません。

public struct DateTimeSpan
{
    public int Years { get; }
    public int Months { get; }
    public int Days { get; }
    public int Hours { get; }
    public int Minutes { get; }
    public int Seconds { get; }
    public int Milliseconds { get; }

    public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
    {
        Years = years;
        Months = months;
        Days = days;
        Hours = hours;
        Minutes = minutes;
        Seconds = seconds;
        Milliseconds = milliseconds;
    }

    enum Phase { Years, Months, Days, Done }

    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
    {
        if (date2 < date1)
        {
            var sub = date1;
            date1 = date2;
            date2 = sub;
        }

        DateTime current = date1;
        int years = 0;
        int months = 0;
        int days = 0;

        Phase phase = Phase.Years;
        DateTimeSpan span = new DateTimeSpan();
        int officialDay = current.Day;

        while (phase != Phase.Done)
        {
            switch (phase)
            {
                case Phase.Years:
                    if (current.AddYears(years + 1) > date2)
                    {
                        phase = Phase.Months;
                        current = current.AddYears(years);
                    }
                    else
                    {
                        years++;
                    }
                    break;
                case Phase.Months:
                    if (current.AddMonths(months + 1) > date2)
                    {
                        phase = Phase.Days;
                        current = current.AddMonths(months);
                        if (current.Day < officialDay && officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
                            current = current.AddDays(officialDay - current.Day);
                    }
                    else
                    {
                        months++;
                    }
                    break;
                case Phase.Days:
                    if (current.AddDays(days + 1) > date2)
                    {
                        current = current.AddDays(days);
                        var timespan = date2 - current;
                        span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
                        phase = Phase.Done;
                    }
                    else
                    {
                        days++;
                    }
                    break;
            }
        }

        return span;
    }
}

1
@KirkWollありがとう。しかし、なぜDateTimeSpanが34この日付と時刻の差の日数を返すのは、実際には35 timeanddate.com/date/…です
Deeptechtons

@Deeptechtons、いいキャッチ。あなたが私の注意を喚起したいくつかの問題がありました。どちらも開始日で31あり、日付はより少ない月で「通過」します。私はロジックを逆にしました(その結果、それは早いから遅いへと逆になるようになりました)現在の日付を変更せずに月を累積します(したがって、より少ない日で中間の月を通過します)それでも理想的な結果が完全にわからないと比較10/31/2012する必要があります11/30/2012。現在、結果は1月です。
Kirk Woll、

@KirkWollの更新に感謝します。いくつかのテストを行った後、私はそれを確認させてもらうために、もう少しお得になりました。よい仕事:)
Deeptechtons

1
私は、提案された回答をテストした同様の質問に対する回答stackoverflow.com/a/17537472/1737957を書きました(そして、それらのほとんどが機能しないことがわかりました)。この答えは機能する数少ないものの1つです(私のテストスイートによると)。私の回答でgithubにリンクします。
jwg 2013

@KirkWoll-この回答は、開始日の日付の値が終了日の月よりも大きい場合、またはソースの日付がうるう日である場合に機能しないようです。試し2020-02-292021-06-29-それは「1Y 4メートル1D」を返しますが、値が「1Y 4メートル0D」、右すべきですか?
謎解き2015年

37

あなたができる

if ( date1.AddMonths(x) > date2 )

これはとてもシンプルで、私にはぴったりです。1か月の終わりから次の月の終わりの日が少ない日付までの日付を計算すると、意図したとおりに機能することを知って、うれしい驚きでした。たとえば、2018年1月31日+ 1か月= 2月28日218
lucky.expert '26

これは優れたソリューションの1つです。
barnacle.m

本当にシンプルで効率的なソリューション!提案された最良の答え。
セドリックArnould

2
date1 = 2018-10-28およびdate2 = 2018-12-21の場合はどうなりますか?正解は3ですが、答えは2になります。日付範囲が3か月であるためです。日を無視して月のみを数える場合。したがって、この答えは正しくありません。
Tommix

より論理的には、次のようになります。if ( date1.AddMonths(x).Month == date2.Month )月数としてx + 1を使用する
Tommix

34

完全な月の正確な数が必要な場合は、常に正(2000-01-15、2000-02-14は0を返します)。1か月は翌月の同じ日に到達したときと見なされます(年齢計算など)。

public static int GetMonthsBetween(DateTime from, DateTime to)
{
    if (from > to) return GetMonthsBetween(to, from);

    var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));

    if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
    {
        return monthDiff - 1;
    }
    else
    {
        return monthDiff;
    }
}

編集理由:次のような場合には、古いコードが正しくありませんでした。

new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },

Test cases I used to test the function:

var tests = new[]
{
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 1), Result = 0 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 2), Result = 0 },
    new { From = new DateTime(1900, 1, 2), To = new DateTime(1900, 1, 1), Result = 0 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 2, 1), Result = 1 },
    new { From = new DateTime(1900, 2, 1), To = new DateTime(1900, 1, 1), Result = 1 },
    new { From = new DateTime(1900, 1, 31), To = new DateTime(1900, 2, 1), Result = 0 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 9, 30), Result = 0 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 10, 1), Result = 1 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1901, 1, 1), Result = 12 },
    new { From = new DateTime(1900, 1, 1), To = new DateTime(1911, 1, 1), Result = 132 },
    new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },
};

他の人の混乱を避けるために、この解決策は正しくないと思います。テストケースを使用して: new { From = new DateTime(2015, 12, 31), To = new DateTime(2015, 6, 30), Result = 6 } 結果として失敗するテストが5である
クリスティアンBadila


取得するかどうかはわかり
Guillaume86

ここでテストケースを手動でコピーしましたが、誤りがあります。失敗する仕様は次のとおりnew { From = new DateTime(2015, 12, 31), To = new DateTime(2016, 06, 30), Result = 6 }です。「バグ」は、to.Day < from.Day月が異なる「月の日」で終わる可能性があることを考慮に入れていないコードにあります。12月31日2015年からこの場合、2016年6月30日まで、6ヶ月完全には合格しています(6月30日を持っているので)が、あなたのコードは、5返す
クリスティアンBadila

3
私の考えでは、これは予想される動作です。少なくとも、私が予想する動作です。完全な月とは、あなたが同じ日(またはこの場合のように翌月)に達したときです。
Guillaume86

22

私はMSDNを介してVB.NETでこのメソッドの使用法を確認しましたが、多くの使用法があるようです。C#には、そのような組み込みメソッドはありません。(それは良い考えではありません)C#でVBを呼び出すことができます。

  1. Microsoft.VisualBasic.dll参照としてプロジェクトに追加する
  2. Microsoft.VisualBasic.DateAndTime.DateDiff コードで使用する

7
なぜそれが良い考えではないと思いますか?直感的には、ライブラリはランタイムにとって「単なる別の.NETライブラリ」であると思います。ここでは、悪魔の擁護者を演じていることに注意してください。これは、「気分が悪い」(不正行為のような)ため、これを行うのも無理ですが、これを行わない説得力のある技術的な理由があるかどうか疑問に思います。
Adam Ralph

3
@AdamRalph:それをしない理由はまったくありません。これらのライブラリは100%マネージコードで実装されているため、他のすべてと同じです。考えられる唯一の違いは、Microsoft.VisualBasic.dllモジュールをロードする必要があることですが、そのためにかかる時間はごくわずかです。プログラムをC#で作成することを選択したからといって、徹底的にテストされた便利な機能を使用しない理由はありません。(これはMy.Application.SplashScreen同様のものにも
当てはまり

3
C#で記述されていることを知っていれば、気が変わりますか?そうだった。同じロジックで、System.DataとPresentationFrameworkを使用することも不正行為であり、その大部分はC ++ / CLIで記述されています。
ハンスパッサント2011年

3
@AdamRalph:心に浮かぶその「奇妙な手荷物」の特定の例は?それとも純粋に仮説的に言っているのですか?ええ、それは、正しいusingステートメントを使用して1行で実行できる何かを実行するために膨大な量のコードを書いているC#の仲間の心を混乱させるかもしれませんが、深刻な被害はないと思います。
Cody Grey

1
@Cody Grey:あなたが説明するように、例は簡単です。このような異常な(C#POVからの)メソッドを呼び出すことによって導入される追加のコード 'ノイズ'は、避けたいと思います。よく整理されたチームでは、そのようなことはとにかくコードレビューで取り上げられ、簡単に回避できます。ところで-私はVB6 / VB.NETを攻撃しようとはしていません。.NET POVから、プロパティがあるDateAndTime.Year()ことDateTimeを考えると、存在する理由がないため、このようなメソッドを「奇妙な」ものとして説明しYearました。VB.NETをVB6のように見せるためだけに存在します。以前のVB6プログラマーとして、私はこれを高く評価できます;-)
Adam Ralph

10

日付に関係なく、月の差(開始と終了の両方を含む)を取得するには:

DateTime start = new DateTime(2013, 1, 1);
DateTime end = new DateTime(2014, 2, 1);
var diffMonths = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);

4
想像startendて、同じです。次に、1の結果を取得します。それはどうですか。なぜ結果に1を加えるのですか?誰がこの回答に賛成票を投じています:-/?
ポール、2014

同一の日付の場合、出力は1になります。基本的に、すべての月を含む開始月と終了月を含みます。
Chirag

3
2つのアイテムの違いのようには思えません。2と2の違いは何ですか?本当に1ですか?私は差が0になることをお勧め
ポール


7

たとえば、月/年のみを入力する雇用日など、簡単なものが必要だったので、明確な年と月を入力したかったのです。

public static YearsMonths YearMonthDiff(DateTime startDate, DateTime endDate) {
    int monthDiff = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month) + 1;
    int years = (int)Math.Floor((decimal) (monthDiff / 12));
    int months = monthDiff % 12;
    return new YearsMonths {
        TotalMonths = monthDiff,
            Years = years,
            Months = months
    };
}

.NETフィドル


4

.NETの期間ライブラリのDateDiffクラスを使用できます

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4

  // description
  Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
  // > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample

2

ここに私が正確であるとわかった月の違いを得る私の貢献があります:

namespace System
{
     public static class DateTimeExtensions
     {
         public static Int32 DiffMonths( this DateTime start, DateTime end )
         {
             Int32 months = 0;
             DateTime tmp = start;

             while ( tmp < end )
             {
                 months++;
                 tmp = tmp.AddMonths( 1 );
             }

             return months;
        }
    }
}

使用法:

Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );

DiffYearsと呼ばれる別のメソッドを作成し、whileループでAddMonthsの代わりに上記とまったく同じロジックとAddYearsを適用できます。


2

これは私がそれを必要としていたもののために働きました。私の場合、月の日はたまたま月の最終日なので、その日は問題ではありませんでした。

public static int MonthDiff(DateTime d1, DateTime d2){
    int retVal = 0;

    if (d1.Month<d2.Month)
    {
        retVal = (d1.Month + 12) - d2.Month;
        retVal += ((d1.Year - 1) - d2.Year)*12;
    }
    else
    {
        retVal = d1.Month - d2.Month;
        retVal += (d1.Year - d2.Year)*12;
    }
    //// Calculate the number of years represented and multiply by 12
    //// Substract the month number from the total
    //// Substract the difference of the second month and 12 from the total
    //retVal = (d1.Year - d2.Year) * 12;
    //retVal = retVal - d1.Month;
    //retVal = retVal - (12 - d2.Month);

    return retVal;
}

2

最も正確な方法は、月ごとの差を分数で返すこれです。

private double ReturnDiffereceBetweenTwoDatesInMonths(DateTime startDateTime, DateTime endDateTime)
{
    double result = 0;
    double days = 0;
    DateTime currentDateTime = startDateTime;
    while (endDateTime > currentDateTime.AddMonths(1))
    {
        result ++;

        currentDateTime = currentDateTime.AddMonths(1);
    }

    if (endDateTime > currentDateTime)
    {
        days = endDateTime.Subtract(currentDateTime).TotalDays;

    }
    return result + days/endDateTime.GetMonthDays;
}

2

これは、少なくとも私にとっては機能する簡単な解決策です。ただし、ループでクールなDateTimeのAddMonth機能を使用しているため、おそらく最速ではありません。

public static int GetMonthsDiff(DateTime start, DateTime end)
{
    if (start > end)
        return GetMonthsDiff(end, start);

    int months = 0;
    do
    {
        start = start.AddMonths(1);
        if (start > end)
            return months;

        months++;
    }
    while (true);
}

1
Public Class ClassDateOperation
    Private prop_DifferenceInDay As Integer
    Private prop_DifferenceInMonth As Integer
    Private prop_DifferenceInYear As Integer


    Public Function DayMonthYearFromTwoDate(ByVal DateStart As Date, ByVal DateEnd As Date) As ClassDateOperation
        Dim differenceInDay As Integer
        Dim differenceInMonth As Integer
        Dim differenceInYear As Integer
        Dim myDate As Date

        DateEnd = DateEnd.AddDays(1)

        differenceInYear = DateEnd.Year - DateStart.Year

        If DateStart.Month <= DateEnd.Month Then
            differenceInMonth = DateEnd.Month - DateStart.Month
        Else
            differenceInYear -= 1
            differenceInMonth = (12 - DateStart.Month) + DateEnd.Month
        End If


        If DateStart.Day <= DateEnd.Day Then
            differenceInDay = DateEnd.Day - DateStart.Day
        Else

            myDate = CDate("01/" & DateStart.AddMonths(1).Month & "/" & DateStart.Year).AddDays(-1)
            If differenceInMonth <> 0 Then
                differenceInMonth -= 1
            Else
                differenceInMonth = 11
                differenceInYear -= 1
            End If

            differenceInDay = myDate.Day - DateStart.Day + DateEnd.Day

        End If

        prop_DifferenceInDay = differenceInDay
        prop_DifferenceInMonth = differenceInMonth
        prop_DifferenceInYear = differenceInYear

        Return Me
    End Function

    Public ReadOnly Property DifferenceInDay() As Integer
        Get
            Return prop_DifferenceInDay
        End Get
    End Property

    Public ReadOnly Property DifferenceInMonth As Integer
        Get
            Return prop_DifferenceInMonth
        End Get
    End Property

    Public ReadOnly Property DifferenceInYear As Integer
        Get
            Return prop_DifferenceInYear
        End Get
    End Property

End Class

1

これは私自身のライブラリからのもので、2つの日付間の月の差を返します。

public static int MonthDiff(DateTime d1, DateTime d2)
{
    int retVal = 0;

    // Calculate the number of years represented and multiply by 12
    // Substract the month number from the total
    // Substract the difference of the second month and 12 from the total
    retVal = (d1.Year - d2.Year) * 12;
    retVal = retVal - d1.Month;
    retVal = retVal - (12 - d2.Month);

    return retVal;
}

1
これは機能しますか?私はのために紙の上で11を得続けるJan-31-2014Dec-31-2013
デイブCousineau

1

あなたはこのような機能を持つことができます。

たとえば、2012/12/27から2012/12/29は3日になります。同様に、2012/12/15から2013/01/15までは2か月になります。2013/ 01/14までは1か月だからです。15日から2ヶ月目が始まりました。

計算に両方の日を含めたくない場合は、2番目のif条件の「=」を削除できます。つまり、2012/12/15から2013/01/15までは1か月です。

public int GetMonths(DateTime startDate, DateTime endDate)
{
    if (startDate > endDate)
    {
        throw new Exception("Start Date is greater than the End Date");
    }

    int months = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month);

    if (endDate.Day >= startDate.Day)
    {
        months++;
    }

    return months;
}

1

次の拡張子を使用できます: コード

public static class Ext
{
    #region Public Methods

    public static int GetAge(this DateTime @this)
    {
        var today = DateTime.Today;
        return ((((today.Year - @this.Year) * 100) + (today.Month - @this.Month)) * 100 + today.Day - @this.Day) / 10000;
    }

    public static int DiffMonths(this DateTime @from, DateTime @to)
    {
        return (((((@to.Year - @from.Year) * 12) + (@to.Month - @from.Month)) * 100 + @to.Day - @from.Day) / 100);
    }

    public static int DiffYears(this DateTime @from, DateTime @to)
    {
        return ((((@to.Year - @from.Year) * 100) + (@to.Month - @from.Month)) * 100 + @to.Day - @from.Day) / 10000;
    }

    #endregion Public Methods
}

実装!

int Age;
int years;
int Months;
//Replace your own date
var d1 = new DateTime(2000, 10, 22);
var d2 = new DateTime(2003, 10, 20);
//Age
Age = d1.GetAge();
Age = d2.GetAge();
//positive
years = d1.DiffYears(d2);
Months = d1.DiffMonths(d2);
//negative
years = d2.DiffYears(d1);
Months = d2.DiffMonths(d1);
//Or
Months = Ext.DiffMonths(d1, d2);
years = Ext.DiffYears(d1, d2); 

1

ここでは、VB.Net DateDiffを使用して、年、月、日のみのはるかに簡潔なソリューションを示します。DateDiffライブラリをC#にロードすることもできます。

date1は<= date2で​​なければなりません

VB.NET

Dim date1 = Now.AddDays(-2000)
Dim date2 = Now
Dim diffYears = DateDiff(DateInterval.Year, date1, date2) - If(date1.DayOfYear > date2.DayOfYear, 1, 0)
Dim diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - If(date1.Day > date2.Day, 1, 0)
Dim diffDays = If(date2.Day >= date1.Day, date2.Day - date1.Day, date2.Day + (Date.DaysInMonth(date1.Year, date1.Month) - date1.Day))

C#

DateTime date1 = Now.AddDays(-2000);
DateTime date2 = Now;
int diffYears = DateDiff(DateInterval.Year, date1, date2) - date1.DayOfYear > date2.DayOfYear ? 1 : 0;
int diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - date1.Day > date2.Day ? 1 : 0;
int diffDays = date2.Day >= date1.Day ? date2.Day - date1.Day : date2.Day + (System.DateTime.DaysInMonth(date1.Year, date1.Month) - date1.Day);

1

これは、カーク・ウォルの答えに対応しています。コメントに返信するための評判ポイントがまだありません...

私はカークのソリューションが好きで、恥知らずにそれをぼろぼろにして私のコードで使用するつもりでしたが、それを見てみると、それがあまりにも複雑すぎることに気付きました。不要な切り替えとループ、および使用しても意味のないパブリックコンストラクター。

これが私の書き直しです:

public class DateTimeSpan {
    private DateTime _date1;
    private DateTime _date2;
    private int _years;
    private int _months;
    private int _days;
    private int _hours;
    private int _minutes;
    private int _seconds;
    private int _milliseconds;

    public int Years { get { return _years; } }
    public int Months { get { return _months; } }
    public int Days { get { return _days; } }
    public int Hours { get { return _hours; } }
    public int Minutes { get { return _minutes; } }
    public int Seconds { get { return _seconds; } }
    public int Milliseconds { get { return _milliseconds; } }

    public DateTimeSpan(DateTime date1, DateTime date2) {
        _date1 = (date1 > date2) ? date1 : date2;
        _date2 = (date2 < date1) ? date2 : date1;

        _years = _date1.Year - _date2.Year;
        _months = (_years * 12) + _date1.Month - _date2.Month;
        TimeSpan t = (_date2 - _date1);
        _days = t.Days;
        _hours = t.Hours;
        _minutes = t.Minutes;
        _seconds = t.Seconds;
        _milliseconds = t.Milliseconds;

    }

    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
        return new DateTimeSpan(date1, date2);
    }
}

Usage1、ほとんど同じ:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    var dateSpan = new DateTimeSpan(compareTo, now);
    Console.WriteLine("Years: " + dateSpan.Years);
    Console.WriteLine("Months: " + dateSpan.Months);
    Console.WriteLine("Days: " + dateSpan.Days);
    Console.WriteLine("Hours: " + dateSpan.Hours);
    Console.WriteLine("Minutes: " + dateSpan.Minutes);
    Console.WriteLine("Seconds: " + dateSpan.Seconds);
    Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}

Usage2、同様:

void Main()
{
    DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
    DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
    Console.WriteLine("Years: " + DateTimeSpan.CompareDates(compareTo, now).Years);
    Console.WriteLine("Months: " + DateTimeSpan.CompareDates(compareTo, now).Months);
    Console.WriteLine("Days: " + DateTimeSpan.CompareDates(compareTo, now).Days);
    Console.WriteLine("Hours: " + DateTimeSpan.CompareDates(compareTo, now).Hours);
    Console.WriteLine("Minutes: " + DateTimeSpan.CompareDates(compareTo, now).Minutes);
    Console.WriteLine("Seconds: " + DateTimeSpan.CompareDates(compareTo, now).Seconds);
    Console.WriteLine("Milliseconds: " + DateTimeSpan.CompareDates(compareTo, now).Milliseconds);
}

1

私の場合、開始日から翌月の当日の前日まで、または開始から月末までの完全な月を計算する必要があります。


例:2018年1月1日から31/1/2018は完全な月です
Ex2:2018年5 月1日 から2018年4月2日は完全な月です

これに基づいてここに私の解決策があります:

public static DateTime GetMonthEnd(DateTime StartDate, int MonthsCount = 1)
{
    return StartDate.AddMonths(MonthsCount).AddDays(-1);
}
public static Tuple<int, int> CalcPeriod(DateTime StartDate, DateTime EndDate)
{
    int MonthsCount = 0;
    Tuple<int, int> Period;
    while (true)
    {
        if (GetMonthEnd(StartDate) > EndDate)
            break;
        else
        {
            MonthsCount += 1;
            StartDate = StartDate.AddMonths(1);
        }
    }
    int RemainingDays = (EndDate - StartDate).Days + 1;
    Period = new Tuple<int, int>(MonthsCount, RemainingDays);
    return Period;
}

使用法:

Tuple<int, int> Period = CalcPeriod(FromDate, ToDate);

注:私の場合、完全な月の残りの日数を計算する必要があったので、そうでない場合は、日の結果を無視するか、メソッドの戻りをタプルから整数に変更することもできます。


1
public static int PayableMonthsInDuration(DateTime StartDate, DateTime EndDate)
{
    int sy = StartDate.Year; int sm = StartDate.Month; int count = 0;
    do
    {
        count++;if ((sy == EndDate.Year) && (sm >= EndDate.Month)) { break; }
        sm++;if (sm == 13) { sm = 1; sy++; }
    } while ((EndDate.Year >= sy) || (EndDate.Month >= sm));
    return (count);
}

このソリューションは、レンタル/サブスクリプションの計算用です。差は減算ではなく、これらの2つの日付内のスパンを意味します。


1

3つのケースがあります:同じ年、前年、およびその他の年。

月の日が問題ではない場合...

public int GetTotalNumberOfMonths(DateTime start, DateTime end)
{
    // work with dates in the right order
    if (start > end)
    {
        var swapper = start;
        start = end;
        end = swapper;
    }

    switch (end.Year - start.Year)
    {
        case 0: // Same year
            return end.Month - start.Month;

        case 1: // last year
            return (12 - start.Month) + end.Month;

        default:
            return 12 * (3 - (end.Year - start.Year)) + (12 - start.Month) + end.Month;
    }
}

1

他の方法ではうまくいかなかったので、これを実現する関数を作成しました。

public string getEndDate (DateTime startDate,decimal monthCount)
{
    int y = startDate.Year;
    int m = startDate.Month;

    for (decimal  i = monthCount; i > 1; i--)
    {
        m++;
        if (m == 12)
        { y++;
            m = 1;
        }
    }
    return string.Format("{0}-{1}-{2}", y.ToString(), m.ToString(), startDate.Day.ToString());
}

英語で答えてください(発明された言語ではありません...)
kleopatra

startDate.AddMonths(monthCount).ToShortDateString()を実行しないのはなぜですか?これはとにかく尋ねられた元の質問には答えません!
TabbyCool 2014

ああ、申し訳ありません@TabbyCool、このコードは私のプログラムでうまく機能します!プログラマーは言う:最初のコードが機能し、次に最適化!urコメントのtanx :)
reza akhlaghi

1

2つの日付間の合計月数の違いについての私の理解には、整数部分と小数部分があります(日付が重要です)。

不可欠な部分は、完全な月の違いです。

私にとって、小数部は、開始月と終了月の間の(月の丸ごとの)日の%の差です。

public static class DateTimeExtensions
{
    public static double TotalMonthsDifference(this DateTime from, DateTime to)
    {
        //Compute full months difference between dates
        var fullMonthsDiff = (to.Year - from.Year)*12 + to.Month - from.Month;

        //Compute difference between the % of day to full days of each month
        var fractionMonthsDiff = ((double)(to.Day-1) / (DateTime.DaysInMonth(to.Year, to.Month)-1)) -
            ((double)(from.Day-1)/ (DateTime.DaysInMonth(from.Year, from.Month)-1));

        return fullMonthsDiff + fractionMonthsDiff;
    }
}

この拡張により、結果は次のようになります。

2/29/2000 TotalMonthsDifference 2/28/2001 => 12
2/28/2000 TotalMonthsDifference 2/28/2001 => 12.035714285714286
01/01/2000 TotalMonthsDifference 01/16/2000 => 0.5
01/31/2000 TotalMonthsDifference 01/01/2000 => -1.0
01/31/2000 TotalMonthsDifference 02/29/2000 => 1.0
01/31/2000 TotalMonthsDifference 02/28/2000 => 0.9642857142857143
01/31/2001 TotalMonthsDifference 02/28/2001 => 1.0

1

あなたはいつも物事を想定しているので、これについて明確な答えはあまりありません。

このソリューションは、2つの日付の間で、比較のために月の日を保存することを想定している間の月を計算します(つまり、計算では月の日が考慮されます)。

たとえば、2012年1月30日の日付がある場合、2012年2月29日は月ではなく、2013年3月1日は月になります。

それはかなり徹底的にテストされています、おそらくそれを使用するときに後でクリーンアップしますが、ここでは:

private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;
    bool sameMonth = false;

    if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
        intReturn--;

    int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
    int daysinMonth = 0; //used to caputre how many days are in the month

    while (dtOther.Date > dtThis.Date) //while Other date is still under the other
    {
        dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
        daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month

        if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
        {
            if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
                dtThis.AddDays(daysinMonth - dtThis.Day);
            else
                dtThis.AddDays(dayOfMonth - dtThis.Day);
        }
        if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
        {
            if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
                intReturn++;
            sameMonth = true; //sets this to cancel out of the normal counting of month
        }
        if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
            intReturn++;
    }
    return intReturn; //return month
}

1

上記の優れたDateTimeSpan作業に基づいて、コードを少し正規化しました。これはかなりうまくいくようです:

public class DateTimeSpan
{
  private DateTimeSpan() { }

  private DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
  {
    Years = years;
    Months = months;
    Days = days;
    Hours = hours;
    Minutes = minutes;
    Seconds = seconds;
    Milliseconds = milliseconds;
  }

  public int Years { get; private set; } = 0;
  public int Months { get; private set; } = 0;
  public int Days { get; private set; } = 0;
  public int Hours { get; private set; } = 0;
  public int Minutes { get; private set; } = 0;
  public int Seconds { get; private set; } = 0;
  public int Milliseconds { get; private set; } = 0;

  public static DateTimeSpan CompareDates(DateTime StartDate, DateTime EndDate)
  {
    if (StartDate.Equals(EndDate)) return new DateTimeSpan();
    DateTimeSpan R = new DateTimeSpan();
    bool Later;
    if (Later = StartDate > EndDate)
    {
      DateTime D = StartDate;
      StartDate = EndDate;
      EndDate = D;
    }

    // Calculate Date Stuff
    for (DateTime D = StartDate.AddYears(1); D < EndDate; D = D.AddYears(1), R.Years++) ;
    if (R.Years > 0) StartDate = StartDate.AddYears(R.Years);
    for (DateTime D = StartDate.AddMonths(1); D < EndDate; D = D.AddMonths(1), R.Months++) ;
    if (R.Months > 0) StartDate = StartDate.AddMonths(R.Months);
    for (DateTime D = StartDate.AddDays(1); D < EndDate; D = D.AddDays(1), R.Days++) ;
    if (R.Days > 0) StartDate = StartDate.AddDays(R.Days);

    // Calculate Time Stuff
    TimeSpan T1 = EndDate - StartDate;
    R.Hours = T1.Hours;
    R.Minutes = T1.Minutes;
    R.Seconds = T1.Seconds;
    R.Milliseconds = T1.Milliseconds;

    // Return answer. Negate values if the Start Date was later than the End Date
    if (Later)
      return new DateTimeSpan(-R.Years, -R.Months, -R.Days, -R.Hours, -R.Minutes, -R.Seconds, -R.Milliseconds);
    return R;
  }
}

比較するとCompareDates(x, y)どこx={01/02/2019 00:00:00}y={01/05/2020 00:00:00}その後、Months私を与える2
Bassie

1

この単純な静的関数は、2つの日時間の月の割合を計算します。

  • 1.1。31.1に。= 1.0
  • 1.4。15.4に。= 0.5
  • 16.4。30.4に。= 0.5
  • 1.3。1.4に。= 1 + 1/30

この関数は、最初の日付が2番目の日付より小さいと想定しています。負の時間間隔を処理するには、最初に符号と変数スワップを導入することにより、関数を簡単に変更できます。

public static double GetDeltaMonths(DateTime t0, DateTime t1)
{
     DateTime t = t0;
     double months = 0;
     while(t<=t1)
     {
         int daysInMonth = DateTime.DaysInMonth(t.Year, t.Month);
         DateTime endOfMonth = new DateTime(t.Year, t.Month, daysInMonth);
         int cutDay = endOfMonth <= t1 ? daysInMonth : t1.Day;
         months += (cutDay - t.Day + 1) / (double) daysInMonth;
         t = new DateTime(t.Year, t.Month, 1).AddMonths(1);
     }
     return Math.Round(months,2);
 }

0

2か月間の日付の差を計算できるようにすることは、完全に論理的なことであり、多くのビジネスアプリケーションで必要です。「2010年5月1日」と「2010年6月16日」の月の違いは何ですか?2010年12月31日と2011年1月1日の月の違いは何ですか?-理解できませんでした。ビジネスアプリケーションの基本。

上記の2つのコメントに対する回答です。2010年5月1日から2010年6月16日までの月数は1か月で、2010年12月31日から2011年1月1日までの月数は0です。上記のコーダーが示唆しているように、1.5か月と1秒と計算するのは非常に愚かです。

クレジットカード、住宅ローン処理、税処理、家賃処理、毎月の利息計算、およびその他の多種多様なビジネスソリューションに携わったことがある人は、それに同意するでしょう。

問題は、そのような関数がC#またはVB.NETに含まれていないことです。Datediffは、年または月のコンポーネントのみを考慮しているため、実際には役に立たない。

月を計算する必要があり、正しく計算できる実際の例を以下に示します。

2月18日から8月23日までの短期賃貸に住んでいました。そこに何ヶ月滞在しましたか?答えは簡単です-6か月

利息が計算され、毎月末に支払われる銀行口座があります。10月6日に預金し、それを29 oct(同じ年)から取り出します。何ヶ月間興味がありますか?非常に簡単な回答-4か月(ここでも、追加の日数は関係ありません)

ビジネスアプリケーションでは、ほとんどの場合、月を計算する必要があるのは、人間が時間を計算する方法に基づいて「完全な」月を知る必要があるためです。いくつかの抽象的な/無関係な考えに基づいていません。


5
これが、会計が数学ではない理由の1つです。結果を計算する際の結果は、計算方法に依存します。私はあなたのポイントを知っており、これについての「共通のビジネスビュー」を知っていますが、この説明は明らかに間違っています。2012.11.30と2012.12.01の間には、何を要求したか応じて、0、または1/30、または1/31、または1または2か月があります。日付は排他的または包括的でしたか?超えた、触れた、または経過した月数を尋ねましたか?切り上げ、切り捨て、または正確にしたいですか?
ケツァルコアトル2013

3
今それをビジネスマンや会計士に説明してください、そうすれば彼らはあなたに困惑した表情を与えるでしょう。それは常に「彼らにとって当然のことながら、XとYとZを意味しているのは明らかですが、どのように違う考えができましたか?」次に、何人かのビジネスパーソンを集めて、彼らにそのトピックについて同意してもらいます。会計士は、ある時点で、偶然に同じ期間を2度合計する可能性のあるオプションを確認するために数学を使用するため、同意する可能性が高くなります。余分な日を無視するような追加のビジネスルール。
ケツァルコアトル2013

2
-1すべてのソフトウェアが「ビジネスアプリケーション」であると想定しています。問題のコードの目的は言及されていません。また、すべての「ビジネスアプリケーション」が同じルールを持っていると仮定しますが、これは間違いなく真実ではありません。
Jesse Webb

0

ToString(format)とDuration(long ms)を含む拡張されたKirks構造体

 public struct DateTimeSpan
{
    private readonly int years;
    private readonly int months;
    private readonly int days;
    private readonly int hours;
    private readonly int minutes;
    private readonly int seconds;
    private readonly int milliseconds;

    public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
    {
        this.years = years;
        this.months = months;
        this.days = days;
        this.hours = hours;
        this.minutes = minutes;
        this.seconds = seconds;
        this.milliseconds = milliseconds;
    }

    public int Years { get { return years; } }
    public int Months { get { return months; } }
    public int Days { get { return days; } }
    public int Hours { get { return hours; } }
    public int Minutes { get { return minutes; } }
    public int Seconds { get { return seconds; } }
    public int Milliseconds { get { return milliseconds; } }

    enum Phase { Years, Months, Days, Done }


    public string ToString(string format)
    {
        format = format.Replace("YYYY", Years.ToString());
        format = format.Replace("MM", Months.ToString());
        format = format.Replace("DD", Days.ToString());
        format = format.Replace("hh", Hours.ToString());
        format = format.Replace("mm", Minutes.ToString());
        format = format.Replace("ss", Seconds.ToString());
        format = format.Replace("ms", Milliseconds.ToString());
        return format;
    }


    public static DateTimeSpan Duration(long ms)
    {
        DateTime dt = new DateTime();
        return CompareDates(dt, dt.AddMilliseconds(ms));
    }


    public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
    {
        if (date2 < date1)
        {
            var sub = date1;
            date1 = date2;
            date2 = sub;
        }

        DateTime current = date1;
        int years = 0;
        int months = 0;
        int days = 0;

        Phase phase = Phase.Years;
        DateTimeSpan span = new DateTimeSpan();

        while (phase != Phase.Done)
        {
            switch (phase)
            {
                case Phase.Years:
                    if (current.AddYears(years + 1) > date2)
                    {
                        phase = Phase.Months;
                        current = current.AddYears(years);
                    }
                    else
                    {
                        years++;
                    }
                    break;
                case Phase.Months:
                    if (current.AddMonths(months + 1) > date2)
                    {
                        phase = Phase.Days;
                        current = current.AddMonths(months);
                    }
                    else
                    {
                        months++;
                    }
                    break;
                case Phase.Days:
                    if (current.AddDays(days + 1) > date2)
                    {
                        current = current.AddDays(days);
                        var timespan = date2 - current;
                        span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
                        phase = Phase.Done;
                    }
                    else
                    {
                        days++;
                    }
                    break;
            }
        }

        return span;
    }
}

0
  var dt1 = (DateTime.Now.Year * 12) + DateTime.Now.Month;
  var dt2 = (DateTime.Now.AddMonths(-13).Year * 12) + DateTime.Now.AddMonths(-13).Month;
  Console.WriteLine(dt1);
  Console.WriteLine(dt2);
  Console.WriteLine((dt1 - dt2));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.