2つの日付間の月の差を計算する


128

C#で/。NETはTimeSpan持っているTotalDaysTotalMinutesなどが、私は総ヶ月差の計算式を把握することはできません。月あたりの変動する日数とうるう年は、私を失望させ続けています。TotalMonthsを取得するにはどうすればよいですか?

編集より明確になっていないので申し訳ありません:実際にはこれを取得できないことはわかっていますTimeSpanが、を使用してTotalDaysTotalMinutes私が探しているものを表現する良い例になると思いました...合計月数を取得しようとしているのを除いて。

例:2009年12月25日-2009年10月6日= 2 TotalMonths。10月6日から11月5日は0か月です。11月6日、1ヶ月。12月6日、2ヶ月


2
2009年12月25日から2009年10月6日まで何を期待していますか?
ジェフ・モーザー、

2
TimeSpanを月単位でどのように定義しますか?
Aliostad

1
@Aliostad-日付がなければ、1か月を30日と定義してかなり正確にすることができます。
ChaosPandion 2011年

何らかの理由でmodによってこの質問とマージされました。
ジャミエツ

実際には、uが、この質問に答える&コード化されたソリューションを提供し、ここで私の記事を読む必要がstackoverflow.com/questions/1916358/... supercatとのコメントを経由して、私の会話にトロール(brianary)&有料の注意を無視します。「孤立した月」と呼ばれる、タイムスパンの開始と終了の月。問題は、これらの孤立した月を日で定義する方法です。これを決定したら(&どのように定義したいか) )、残りは単なるコードです(含まれています)。私のデフ。ユーザーが期待するものに基づいています
Erx_VB.NExT.Coder '10

回答:


222

TimeSpan「月」は可変の測定単位であるため、から取得することはできません。あなたはそれを自分で計算する必要があり、あなたはそれがどのように機能するのか正確に理解する必要があります。

たとえば、日付が1か月または0か月の違いJuly 5, 2009August 4, 2009もたらすかどうか。あなたはそれが何について、その後、1を得なければならないと言うならJuly 31, 2009August 1, 2009?であることは、月?それは単にMonth日付の値の違いですか、それとも実際の期間に関連していますか?これらのルールをすべて決定するロジックは重要なので、独自のルールを決定して適切なアルゴリズムを実装する必要があります。

必要なのは、月の違い(日付の値を完全に無視)だけの場合は、次のように使用できます。

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}

これは相対的な差を返すことに注意してください。つまり、rValueがより大きい場合lValue、戻り値は負になります。絶対的な差異が必要な場合は、これを使用できます。

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}

@Dinahこれは単なる近似値です。真の.Monthと.Yearsを知りたい場合-私はあなたが読める答えを投稿しました。概算がそうである限り、これは適切な概算です(Adam Robinsonの小道具です)が、これらの概算のいずれかを使用する場合は、意図せずにユーザーに嘘をついていることに注意してください。
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder:小道具に感謝しますが、あなたの答えはいずれも月が可変の測定単位であることを考慮に入れていないと述べていますが、それらのほとんどはそうです。特定の近似値を使用しないだけです。適例として、私の回答の最初の文は、それが可変であることを示しています。正確な答えではないという理由だけで、あなたの回答を含め、どのような答えも近似値です。「2か月」の結果は、入力によって異なることを意味する可能性があるため、概算です。
アダムロビンソン

私の概算ではありませんが、今日が3月14日である場合、過去2か月は、janが31日、2月が29日であるという事実に基づいて計算されます。さて、あなたは私の方法が「一般的な」月の定義ではなく、あなたのそれが正しいという点で正しいです!ただし、「このコメントはxか月前とy日前に投稿されました」のようなものを報告する場合にのみ当てはまります。「AGO」の部分は違います。これは、過去xか月を参照しているため、以前のxか月を計算する必要があるためですこれらのxか月に存在した日数に基づいて!link ....
Erx_VB.NExT.Coder 2012年

それは理にかなっていますか?したがって、特定の既知の月を参照している場合、私の方法は100%正確であり、概算になりますが、一般的に月を参照している場合は、概算の方が適切です。私の考えは悪い考えです(そのために作られているわけではなく、それを使用しても意味がありません)。ここに私の記事へのリンクは、問題を説明し、解決策を提供している:stackoverflow.com/questions/1916358/...
Erx_VB.NExT.Coder

2
これは、SQL ServerのDateDiff(month、...)関数で使用されるのと同じロジックのようです。また、非常に簡潔で、説明と理解が容易であるという利点もあります。私はそれを次のように説明します...ある日付から別の日付に移動するには、カレンダーの何ページをめくる必要がありますか?
JoelFan 2016年

51

(これは古い質問だと思いますが...)

これは、純粋な.NETで行うのは比較的困難です。私は自分の野田タイムライブラリをお勧めします。これは特に次のようなもののために設計されています。

LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;

(他のオプションもあります。たとえば、年を超えても数か月しか必要ない場合は、を使用しますPeriod period = Period.Between(start, end, PeriodUnits.Months);


ライブラリをダウンロードし、上記のコードをコピーしましたが、コンパイル時エラーが発生します。エラー1演算子 '-'は、タイプ 'NodaTime.LocalDate'および 'NodaTime.LocalDate'のオペランドには適用できません。私はこの投稿を5年前から知っています。そのときから何か変更があり、このコードが機能しなくなったのですか?
HakanFıstık15年

1
@HakamFostok:申し訳ありません-2.0がリリースされたときに機能しますが、それまではを使用する必要がありますPeriod.Between。NodaTime 1.3.1で動作するようにコードを編集しました。
Jon Skeet、2015年

NodaTimeライブラリは私がやりたいことを正確に実行してくれてありがとう。2つの日付の間の月だけでなく、残りの日も計算したかったのですが、このおかげで、NodaTimeは正確に何をしたのか、もう一度感謝します。
HakanFıstık2015年

1
@JonSkeetあなたのライブラリは本当に黒魔術です。日付はいつも私を噛みます。このコードスニペットを使用すると、時間を大幅に節約できました。
onefootswill

28

たぶん、あなたは月の端数について知りたくないでしょう。このコードはどうですか?


public static class DateTimeExtensions
{
    public static int TotalMonths(this DateTime start, DateTime end)
    {
        return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
    }
}

//  Console.WriteLine(
//     DateTime.Now.TotalMonths(
//         DateTime.Now.AddMonths(-1))); // prints "1"



1
* 100がわかりません。* 12にする必要がありますか?
ラッフルズ

9

まず、TotalMonthsで何を意味するかを定義する必要があります。
単純な定義では、1か月は30.4日(365.25 / 12)になります。

それを超えると、端数を含む定義は役に立たないように見え、より一般的な整数値(日付間の月数)も非標準のビジネスルールに依存します。


9

私は上の非常に単純な拡張メソッドを書いているDateTimeし、DateTimeOffsetこれを実行します。私はそれが機能するTotalMonthsプロパティとまったく同じように機能することを望みましたTimeSpan。それはそれに基づいてDateTime.AddMonths()いるため、さまざまな月の長さを尊重し、人間が月の期間として理解するものを返します。

(残念ながら、TimeSpanで拡張メソッドとして実装することはできません。これは、実際に使用された日付の知識が保持されず、月間は重要であるためです。)

コードとテストはどちらもGitHubで入手できます。コードは非常に簡単です:

public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
    DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
    DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;

    // Start with 1 month's difference and keep incrementing
    // until we overshoot the late date
    int monthsDiff = 1;
    while (earlyDate.AddMonths(monthsDiff) <= lateDate)
    {
        monthsDiff++;
    }

    return monthsDiff - 1;
}

そして、これらすべての単体テストケースに合格しています。

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));

3
素朴ですが、最高のソリューションです。コピーして貼り付けます。ありがとう
Daniel Dolz

8

あなたは日時を自分で解決する必要があります。最後にスタブ日をどのように処理するかは、それを何に使用するかによって異なります。

1つの方法は、月を数え、最後に日数を修正することです。何かのようなもの:

   DateTime start = new DateTime(2003, 12, 25);
   DateTime end = new DateTime(2009, 10, 6);
   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
   double daysInEndMonth = (end - end.AddMonths(1)).Days;
   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

素敵なコードですが、1つのバグ:代わりに:(2月28日+ 1か月== 3月28日として):-) // decimal daysInEndMonth =(end-end.AddMonths(1))。Days; 推奨:10進数のdaysInEndMonth = DateTime.DaysInMonth(end.Year、end.Month)* -1;
bezieur 2014

3

私はそれをこのようにします:

static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;

    dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
    dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));

    while (dtOther.Date > dtThis.Date)
    {
        intReturn++;     
        dtThis = dtThis.AddMonths(1);
    }

    return intReturn;
}

4
それは確かに1そのアルゴリズムだが、それを大幅に簡素化することができたreturn (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
アダム・ロビンソン

1
2つの問題:TimeSpanではなく、2つの日付から開始しています。次に、両方の月の1日の間で計算します。これは非常に疑わしい定義です。それは時々正しいかもしれませんが。
Henk Holterman、

@ヘンク:ええ、もちろんそれは常に正しいとは限りません。だからこそ、これは私がやる方法であり、誰もがやるべきではない、と私が言ったのです。OPは、結果の計算方法を指定しませんでした。@アダム:うわー、私はあまりにも複雑すぎる方法をもう一度考えました...それは私にはあまりにも頻繁に起こります。コメントをありがとう、あなたは明らかに正しい、あなたのバージョンははるかに優れています。これからはこれを使います。
Maximilian Mayerl、

@アダム:これを実際の回答として提出してみませんか?!これは、これまでで最もコンパクトです。非常に滑らかです。
ダイナ

@ダイナ:私はそれがあなたが実際に望んでいたことだと思いたくなかった。もしそうなら、私は以前の答えを編集してこのアプローチを含めました。
Adam Robinson、

3

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

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

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

これはかなり徹底的にテストされており、おそらく後で使用するときにクリーンアップされ、Timespanの代わりに2つの日付が使用されます。これが他の人を助けることを願っています。

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
}

3

承認された回答は、完全な月が必要な場合に完全に機能します。

私は部分的な月を必要としていました。これは私が部分的な月のために思いついた解決策です:

    /// <summary>
    /// Calculate the difference in months.
    /// This will round up to count partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    public static int MonthDifference(DateTime lValue, DateTime rValue)
    {
        var yearDifferenceInMonths = (lValue.Year - rValue.Year) * 12;
        var monthDifference = lValue.Month - rValue.Month;

        return yearDifferenceInMonths + monthDifference + 
            (lValue.Day > rValue.Day
                ? 1 : 0); // If end day is greater than start day, add 1 to round up the partial month
    }

また、年の差も必要でしたが、部分的な年も同様に必要でした。これが私が思いついた解決策です:

    /// <summary>
    /// Calculate the differences in years.
    /// This will round up to catch partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    public static int YearDifference(DateTime lValue, DateTime rValue)
    {
        return lValue.Year - rValue.Year +
               (lValue.Month > rValue.Month // Partial month, same year
                   ? 1
                   : ((lValue.Month = rValue.Month) 
                     && (lValue.Day > rValue.Day)) // Partial month, same year and month
                   ? 1 : 0);
    }

YearDifference関数にロジックバグがあったときlValue.Month < rValue.Month-私はそれを今修正したので、レビューしたいかもしれません...
Stobor

2

古い質問ですが、誰かを助けるかもしれません。上記の@Adam承認済みの回答を使用しましたが、差が1または-1かどうかを確認してから、完全な暦月の差かどうかを確認します。したがって、21/07/55と20/08/55は1か月ではなく、21/07/55と21/07/55は1か月になります。

/// <summary>
/// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
/// </summary>
/// <param name="dateOfBirth">Date of birth user could have amended.</param>
/// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
/// <returns></returns>
public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
{
    DateTime dob, originalDob;
    bool isValid = false;

    if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
    {
        int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));

        switch (diff)
        {
            case 0:
                // We're on the same month, so ok.
                isValid = true;
                break;
            case -1:
                // The month is the previous month, so check if the date makes it a calendar month out.
                isValid = (dob.Day > originalDob.Day);
                break;
            case 1:
                // The month is the next month, so check if the date makes it a calendar month out.
                isValid = (dob.Day < originalDob.Day);
                break;
            default:
                // Either zero or greater than 1 month difference, so not ok.
                isValid = false;
                break;
        }
        if (!isValid)
            return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
    }
    else
    {
        return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
    }
    return Json(true, JsonRequestBehavior.AllowGet);
}

2
case IntervalType.Month:
    returnValue = start.AddMonths(-end.Month).Month.ToString();
    break;
case IntervalType.Year:
    returnValue = (start.Year - end.Year).ToString();
    break;

2
コードに付随する説明は、他の読者にとっても有益です。
Boeckm

ええと、コメントを追加してください。
アマー

1

月の問題は、それが実際に単純な測定ではないことです-それらは一定のサイズではありません。何を含めたいかについてルールを定義し、そこから作業する必要があります。たとえば、1月1日から2月1日-2か月が関与していると主張することも、1か月と言うこともできます。次に、「1月1日20:00」から「1月2日00:00」まではどうでしょうか。これは、1か月全体ではありません。それは0ですか?1?逆はどうですか(1月1日00:00から2月1日20:00)... 1?2?

最初にルールを定義してから、自分でコーディングする必要があります。恐らく...


1

との1間に結果を入れたい場合: 28th Feb1st March

DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month

これは、SQL ServerのDateDiff(month、...)関数で使用されるのと同じロジックのようです。また、非常に簡潔で、説明と理解が容易であるという利点もあります。私はそれを次のように説明します...ある日付から別の日付に移動するには、カレンダーの何ページをめくる必要がありますか?
JoelFan 2016年

1

このライブラリは、DateTimeのすべての部分を考慮して、月の差を計算します。

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  Console.WriteLine( "Date1: {0}", date1 );
  // > Date1: 08.11.2009 07:13:59
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  Console.WriteLine( "Date2: {0}", date2 );
  // > Date2: 20.03.2011 19:55:28

  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
  // > DateDiff.Years: 1
  Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
  // > DateDiff.Quarters: 5
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16
  Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
  // > DateDiff.Weeks: 70
  Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
  // > DateDiff.Days: 497
  Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
  // > DateDiff.Weekdays: 71
  Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
  // > DateDiff.Hours: 11940
  Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
  // > DateDiff.Minutes: 716441
  Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
  // > DateDiff.Seconds: 42986489

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
  // > DateDiff.ElapsedYears: 1
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4
  Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
  // > DateDiff.ElapsedDays: 12
  Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
  // > DateDiff.ElapsedHours: 12
  Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
  // > DateDiff.ElapsedMinutes: 41
  Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
  // > DateDiff.ElapsedSeconds: 29
} // DateDiffSample

1

「1か月」の定義は月によって異なり、他の回答ではこれを考慮に入れていないため、実際には次の方法で実際に実行できます。フレームワークに組み込まれていない問題に関する詳細情報が必要な場合は、この投稿をご覧ください.Years&.Monthsを使用したReal Timespanオブジェクト(ただし、以下の関数を理解して使用するためにその投稿を読む必要はありません、それは100%機能しますが、他の人が使用するのが好きな近似の固有の不正確さはありません。また、.ReverseIt関数をフレームワークにある組み込みの.Reverse関数に置き換えてもかまいません(完全を期すためにここにあります)。

日付/時刻の精度、秒と分、または秒、分と日、何年にも及ぶ任意の数(6つの部分/セグメントを含む)を取得できることに注意してください。上位2つを指定し、それが1年以上前の場合、「1年3か月前」を返し、2つのセグメントを要求したため、残りは返しません。数時間しか経過していない場合は、「2時間1分前」のみが返されます。もちろん、1、2、3、4、5、または6セグメントを指定した場合も同じ規則が適用されます(秒、分、時間、日、月、年は6つのタイプしか作成しないため、6で最大になります)。また、1分以上かどうかに応じて、「分」と「分」のような文法の問題を修正します。すべてのタイプで同じであり、生成される「文字列」は常に文法的に正しくなります。

使用例をいくつか示します。bAllowSegmentsは、表示するセグメントの数を識別します...つまり、3の場合、返される文字列は(例として)となります... "3 years, 2 months and 13 days"(時間、分、秒を上位3時間に含めませんただし、日付が数日前などの新しい日付の場合、同じセグメント(3)を指定すると"4 days, 1 hour and 13 minutes ago"代わりに返されるため、すべてが考慮されます。

bAllowSegmentsが2であれば、それは戻ってくる"3 years and 2 months"6(最大値)とあれば返します"3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"、しかし、それはすることが思い出されNEVER RETURN、このような何かを"0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"あなたは6つのセグメントを指定した場合でも、それはトップ3のセグメントには日付データがありません理解し、それらを無視して、なので心配しないでください:) もちろん、その中に0があるセグメントがある場合、文字列を形成するときにそれが考慮され"3 days and 4 seconds ago"、「0時間」の部分として無視されて表示されます。楽しんでコメントしてください。

 Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
  ' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
  ' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
  ' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
  Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
  Dim dtNow = DateTime.Now
  Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)

  rYears = dtNow.Year - dt.Year
  rMonths = dtNow.Month - dt.Month
  If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
  rDays = dtNow.Day - dt.Day
  If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
  rHours = dtNow.Hour - dt.Hour
  If rHours < 0 Then rHours += 24 : rDays -= 1
  rMinutes = dtNow.Minute - dt.Minute
  If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
  rSeconds = dtNow.Second - dt.Second
  If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1

  ' this is the display functionality
  Dim sb As StringBuilder = New StringBuilder()
  Dim iSegmentsAdded As Int16 = 0

  If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1

parseAndReturn:

  ' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
  ' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...

  If sb.ToString = "" Then sb.Append("less than 1 second")

  Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")

 End Function

もちろん、「ReplaceLast」関数が必要です。これは、ソース文字列と、置換する必要があるものを指定する引数と、置換するものを指定する別の引数を必要とし、その文字列の最後の出現のみを置換します...実装していない場合、または実装したくない場合に備えています。ここでは、変更を加えることなく「そのまま」機能します。reverseit関数が不要になったことはわかっていますが(.netに存在します)、ReplaceLastとReverseIt関数は.netより前の日付から引き継がれています。 emは10年以上、バグがないことを保証できます)... :)。乾杯。

<Extension()> _ 
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String 
    ' let empty string arguments run, incase we dont know if we are sending and empty string or not. 
    sReplacable = sReplacable.ReverseIt 
    sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version! 
    Return sReplacable.ReverseIt.ToString 
End Function 

<Extension()> _ 
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String 
    Dim strTempX As String = "", intI As Integer 

    If n > strS.Length Or n = -1 Then n = strS.Length 

    For intI = n To 1 Step -1 
        strTempX = strTempX + Mid(strS, intI, 1) 
    Next intI 

    ReverseIt = strTempX + Right(strS, Len(strS) - n) 

End Function 

0

正確な数値が必要な場合は、タイムスパンだけから取得することはできません。これは、扱っている月とうるう年を扱っているかどうかを知る必要があるためです。

おおよその数を取得するか、元のDateTimesを試してみてください




0

月と年を扱っている場合は、各月の日数とうるう年を知る必要があります。

グレゴリオ暦(および他の文化固有のカレンダー)を入力してくださいの実装を)。

Calendarには、2つの時点の差を直接計算するメソッドはありませんが、次のようなメソッドがあります。

DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)

0
DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

0

このメソッドは、3つの要素を含むリストを返します。最初は年、2番目は月、最後の要素は日です。

public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
    {
        try
        {
            if (from > to)
                return null;

            var fY = from.Year;
            var fM = from.Month;
            var fD = DateTime.DaysInMonth(fY, fM);

            var tY = to.Year;
            var tM = to.Month;
            var tD = DateTime.DaysInMonth(tY, tM);

            int dY = 0;
            int dM = 0;
            int dD = 0;

            if (fD > tD)
            {
                tM--;

                if (tM <= 0)
                {
                    tY--;
                    tM = 12;
                    tD += DateTime.DaysInMonth(tY, tM);
                }
                else
                {
                    tD += DateTime.DaysInMonth(tY, tM);
                }
            }
            dD = tD - fD;

            if (fM > tM)
            {
                tY--;

                tM += 12;
            }
            dM = tM - fM;

            dY = tY - fY;

            return new List<int>() { dY, dM, dD };
        }
        catch (Exception exception)
        {
            //todo: log exception with parameters in db

            return null;
        }
    }

0

私が正確であることがわかった月の違いを取得するための私の貢献は次のとおりです。

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を適用できます。


0

ゲームにはかなり遅れましたが、これは誰かに役立つかもしれないと思います。大多数の人は、月ごとに異なるバリエーションがあるという事実を除いて、日付ごとに月ごとに測定する傾向があります。その考え方の枠組みを使用して、日付を比較する1つのライナーを作成しました。次のプロセスを使用します。

  1. 年を比較する場合、1を超える年数は12倍されます。これが1年間に満たない場合はありません。
  2. 終了年が大きい場合は、当日が前日2A以上かどうかを評価する必要があります。終了日がそれ以上の場合は、現在の月を使用して、12か月を加算し、開始月2Bの月を減算します。終了日が開始日よりも短い場合は、減算する前に開始月に1を加えることを除いて、上記と同じように実行します。
  3. 終了年がそれより大きくない場合、2A / 2Bと同じように実行しますが、年の前後を評価する必要がないため、12か月を追加しません。

        DateTime date = new DateTime(2003, 11, 25);
        DateTime today = new DateTime(2004, 12, 26);
        var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) +  (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));

三元による死?
SpaceBison 2017

0

この回答に対する私の見方も拡張メソッドを使用していますが、肯定的または否定的な結果を返す可能性があります。

public static int MonthsBefore(this DateTime dt1, DateTime dt2)
{
    (DateTime early, DateTime late, bool dt2After) = dt2 > dt1 ? (dt1,dt2,true) : (dt2,dt1,false);
    DateTime tmp; // Save the result so we don't repeat work
    int months = 1;
    while ((tmp = early.AddMonths(1)) <= late)
    {
        early = tmp;
        months++;
    }
    return (months-1)*(dt2After ? 1 : -1);
}

いくつかのテスト:

// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 2, 2)));    
// Past date returns NEGATIVE
Assert.AreEqual(-6, new DateTime(2012, 1, 1).MonthsBefore(new DateTime(2011, 6, 10)));

0

上記の2つの答えを組み合わせると、別の拡張方法は次のとおりです。

public static int ElapsedMonths(this DateTime date1, DateTime date2)
{
    DateTime earlierDate = (date1 > date2) ? date2 : date1;
    DateTime laterDate = (date1 > date2) ? date1 : date2;
    var eMonths = (laterDate.Month - earlierDate.Month) + 12 * (laterDate.Year - earlierDate.Year) - 
                                            ((earlierDate.Day > laterDate.Day) ? 1 : 0);
    return eMonths;
}

@AdamRobinsonと@MarkWhittakerに感謝


-1

2つの日付の間の月数を計算します。

$date1 = '2017-01-20';
$date2 = '2019-01-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);

1
これはC#ではなくPHPです。
AFract
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.