C#/。NETではTimeSpan
にはTotalDays
、TotalMinutes
などがありますが、合計月差の計算式はわかりません。月ごとの変動日とうるう年は、私を失望させ続けます。 TotalMonthsを取得するにはどうすればよいですか?
編集もっと明確にしないで申し訳ありません:TimeSpan
から実際にこれを取得できないことは知っていますが、TotalDays
とTotalMinutes
を使用するのが良い例だと思いました私が探していたものを表現するために...合計月を取得しようとしていることを除いて。
例:2009年12月25日-2009年10月6日= 2 TotalMonths。 10月6日から11月5日は0か月に相当します。 11月6日、1か月。 12月6日、2か月
「月」は変数の測定単位であるため、TimeSpan
から取得することはできません。あなたはそれを自分で計算する必要があり、あなたはそれがどのように機能するかを正確に把握する必要があります。
たとえば、July 5, 2009
やAugust 4, 2009
のような日付は1か月または0か月の差を生じますか?あなたはそれが1つをもたらすべきだと言うなら、July 31, 2009
とAugust 1, 2009
はどうですか? thatは月ですか?それは単に日付の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));
}
(これは古い質問ですが、...)
これは相対的純粋な.NETで行うのは苦痛です。私自身の Noda Time ライブラリをお勧めします。これは特に次のようなもののために設計されています。
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);
を使用します)
たぶん、月の端数について知りたくないかもしれません。このコードはどうですか?
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"
最初にTotalMonthsの意味を定義する必要があります。
簡単な定義では、1か月は30.4日(365.25/12)になります。
それを超えると、端数を含む定義は役に立たないように思われ、より一般的な整数値(日付間の月全体)も非標準のビジネスルールに依存します。
日付時刻を自分で解決する必要があります。最後にスタブ日をどのように扱うかは、それを何に使用したいかによって異なります。
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;
これを行うために、DateTime
およびDateTimeOffset
に非常に簡単な拡張メソッドを作成しました。 TotalMonths
のTimeSpan
プロパティとまったく同じように動作することを望んでいました。つまり、2つの日付の間の完全な月のカウントを返します。 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)));
私はこのようにします:
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;
}
あなたは常に物事を仮定しているので、これに関する多くの明確な答えはありません。
このソリューションは、比較のために月の日付を保存することを想定して、2つの日付の間で月を計算します(つまり、計算で月の日が考慮されることを意味します)
たとえば、2012年1月30日の日付がある場合、2012年2月29日は月ではなく、2013年3月1日となります。
それはかなり徹底的にテストされており、おそらく使用時に後でクリーンアップされ、タイムスパンの代わりに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
}
古い質問ですが、誰かを助けるかもしれません。上記の@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);
}
case IntervalType.Month:
returnValue = start.AddMonths(-end.Month).Month.ToString();
break;
case IntervalType.Year:
returnValue = (start.Year - end.Year).ToString();
break;
受け入れられた答えは、完全な月が欲しいときに完全に機能します。
部分的な月が必要でした。これは私が数ヶ月間思いついた解決策です:
/// <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);
}
以下は、実際に最も正確な方法です。「1か月」の定義は、どの月であるかによって異なり、他の回答ではこれを考慮に入れていないためです。フレームワークに組み込まれていない問題に関する詳細情報が必要な場合は、この投稿を読むことができます。 。Years&.Monthsを使用したReal Timespanオブジェクト (ただし、その投稿を読む必要はありません以下の関数を理解して使用すると、他の人が使用するのが好きな近似の固有の不正確さなしに100%動作します。そして、フレームワークにあるかもしれない組み込みの.Reverse関数で.ReverseIt関数を自由に置き換えることができます(ちょうどです完全を期すためにここに)。
日付/時間の精度、秒と分、または秒、分と日、任意の数の年(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"
が理解できるようになります上位3つのセグメントには日付データがなく、6つのセグメントを指定しても無視されますので、心配しないでください:)。もちろん、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 funcはpre.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
これ ライブラリは、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日から2月1日まで-2か月が関与している、または1か月であると主張することができます。では、「1月1日20:00」から「2月1日00:00」まではどうでしょうか。1か月全体ではありません。それは0ですか? 1?他の方法(1月1日00:00から2月1日20:00)についてはどうですか... 1? 2?
最初にルールを定義してから、自分でコーディングする必要があります。
1
と28th Feb
の間の結果1st March
を取得する場合:
DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month
ゲームに遅刻しますが、これは誰かに役立つと思います。大部分の人は、月がさまざまなバリエーションがあるという事実を除いて、日付ごとに月ごとに測定する傾向があります。その考えの枠組みを使用して、日付を比較する1つのライナーを作成しました。次のプロセスを使用します。
終了年がそれより大きくない場合は、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)));
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);
正確な数字が必要な場合は、タイムスパンだけでは取得できません。これは、あなたが言っているように、どの月を処理していて、うるう年を処理しているかを知る必要があるからです。
おおよその数値を取得するか、元のDateTimesを少し調整します。
このメソッドは、最初の要素が年、2番目の要素が月、最後の要素が日である3つの要素を含むリストを返します。
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;
}
}
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;
http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html
グレゴリオ暦の日付から ユリウス暦の日数 に変換された時間を取得できる場合は、zulianの曜日数の比較を行う演算子を作成できます。など。グレゴリオ暦からジュリアンへの変換アルゴリズムについては、上記のリンクをご覧ください。
Idiomatic-c#でこれを正確に行うための組み込みの方法はありません。 このCodeProjectの例 など、いくつかの回避策がありますが、人々はコーディングしています。
正確であることがわかった月の差を得るための私の貢献は次のとおりです。
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とまったく同じロジックを適用できます。