计算两个日期之间以月为单位的差额

C #/.NET TimeSpanTotalDaysTotalMinutes等,但我不能算出一个公式的总月份差异。每个月可变的日子和闰年一直让我迷茫。我怎样才能得到 总月数

编辑 抱歉没有说得更清楚: 我知道我实际上不能从 TimeSpan得到这个,但我认为使用 TotalDaysTotalMinutes将是一个很好的例子来表达我所寻找的... 除了我试图得到总月份。

例如: 2009年12月25日-2009年10月6日 = 2个月。10月6日到11月5日等于0个月。11月6日,一个月。12月6日,两个月

175533 次浏览

你不能从 TimeSpan得到这个结果,因为“月”是一个可变的度量单位。你必须自己计算,你必须弄清楚你到底想要它怎么工作。

例如,像 July 5, 2009August 4, 2009这样的日期应该产生一个月的差异还是零个月的差异?如果你说它应该产生一个,那么 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));
}

首先,您必须定义 TotalMonths 的含义。
一个简单的定义是一个月有30.4天(365.25/12)。

除此之外,包括分数在内的任何定义似乎都是无用的,更常见的整数值(日期之间的整个月)也依赖于非标准的业务规则。

如果你想要确切的数字,你不能只从时间跨度,因为你需要知道你正在处理的月份,以及你是否正在处理闰年,就像你说的。

要么选择一个大致的数字,要么对原始的日期时间进行一些调整

我会这样做:

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

你得自己解决约会时间问题。您如何处理最后的存根日将取决于您想用它来做什么。

一种方法是计算月份,然后在最后修正天数,比如:

   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

如果可以将时间从 Gregorian Date 转换为 朱利安日号,那么只需创建一个操作符来比较 Zulian 日数,该操作符可以输入 double 来获取月、日、秒等。查看上面的链接,了解从格里高利文到朱利安文的转换算法。

也许您不想知道月分数; 那么这个代码怎么样呢?


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月1日至2月1日-你可以说2个月,或者你可以说那是一个月。那么“1月1日20:00”到“2月1日00:00”又是怎么回事呢? 这还不是一个完整的月份。是0吗?1?反过来(1月1日00:00至2月1日20:00) ... 1?两个?

首先定义规则,然后你得自己编写代码,恐怕..。

如果你想在 28th Feb1st March之间得到一个结果 1:

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

在惯用语 c # 中没有内置的方法可以准确地做到这一点。还有一些变通方法,比如人们编写的 < strong > this CodeProject example

这个 库计算月份的差异,考虑到日期时间的所有部分:

// ----------------------------------------------------------------------
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

如果你处理的是月份和年份,你需要一些知道每个月有多少天,哪些年份是闰年的东西。

输入 公历(和其他特定于区域性的 日历实现)。

尽管 Calendar 没有提供直接计算两个时间点之间的差异的方法,但是它确实有如下方法

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

我知道这是个老问题,但也许能帮到别人。我使用了@Adam 接受上面的答案,但是然后检查差值是1还是 -1,然后检查是否是整个日历月的差值。所以21/07/55和20/08/55不是一个完整的月份,但21/07/55和21/07/55是。

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

下面实际上是你能做到的最准确的方法,因为“1个月”的定义会根据月份的不同而变化,而其他的答案都没有考虑到这一点!如果你想了解更多关于这个问题的信息,而这些信息并没有内置在框架中,你可以阅读这篇文章: 一个具有. Years 和. Month 的实时时间跨度对象(然而,阅读这篇文章并不需要理解和使用下面的函数,它100% 有效,没有其他人喜欢使用的近似值的固有不准确性——并且可以随意替换。它的功能与内置。框架上可能有的反向函数(这里只是为了完整性)。

请注意,您可以获得任意数量的日期/时间的准确性,秒和分钟,或秒,分钟和天,任何地方,直到年(这将包含6个部分/片段)。如果您指定的是 top two,它已经存在一年多了,那么它将返回“1年3个月前”,而不会返回其余部分,因为您已经请求了两个段。如果只有几个小时,那么它只会返回“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",但是,请注意,它将返回类似于 "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"NEVER RETURN,因为它知道在前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”函数,它接受一个源字符串,一个参数指定需要替换的内容,另一个参数指定需要替换的内容,它只替换该字符串的最后一次出现... 如果你没有或者不想实现它,我已经包含了我的参数,所以在这里,它将“按原样”工作,不需要修改。我知道反转函数不再需要(存在于。但是 Replace Last 和 OverseIt 功能是从 pre.net 时代延续下来的,所以请原谅它看起来有多过时(仍然可以100% 正常工作,已经使用了10多年,可以保证它们没有 bug) ... :)。干杯。

<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
case IntervalType.Month:
returnValue = start.AddMonths(-end.Month).Month.ToString();
break;
case IntervalType.Year:
returnValue = (start.Year - end.Year).ToString();
break;
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;

关于这个问题没有很多明确的答案,因为你总是假设一些事情。

这个解决方案在两个日期之间进行计算,假设您想要保存月份中的某一天以便进行比较(这意味着在计算中考虑了月份中的某一天)

例如,如果你的日期是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
}

为此,我在 DateTimeDateTimeOffset上编写了一个非常简单的扩展方法。我希望它能像 TimeSpan上的 TotalMonths属性一样工作: 即返回两个日期之间的完整月份数,忽略任何部分月份。因为它是基于 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)));

(我知道这是个老问题,但是...)

这是 相对而言痛苦做在纯粹。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);)

该方法返回一个包含3个元素的列表,第一个元素是 year,第二个元素是 month,最后一个元素是 day:

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

以下是我的贡献,以得到差异的月份,我发现是准确的:

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 循环中应用与上面完全相同的逻辑和 AddYears,而不是 AddMonths。

虽然已经很晚了,但我想这对某些人可能会有帮助。大多数人倾向于按月计算日期,但不包括月份有不同变化的事实。利用这种思维框架,我创建了一个对比我们的日期一行。使用以下过程。

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

当你想要整整几个月的时候,接受的答案是完美的。

我需要几个月的时间,这就是我想出来的解决方案:

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

我对 这个答案的看法也使用 推广方法推广方法,但它可以返回一个正面或负面的结果。

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

综合上述两个答案,另一种扩展方法是:

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

我们是这样做的:

int DifferenceInMonth(DateTime startDate, DateTime endDate) {
long ToTicks(DateTime date) => new DateTimeOffset(date).Ticks;
var daysPerMonth = 30.4;
return (int)Math.Round((ToTicks(endDate) - ToTicks(startDate)) / TimeSpan.TicksPerDay / daysPerMonth);
}

人们普遍接受的答案大错特错:

对于这些日期: ldate = 2020-08-30和 rdate = 2020-08-01,我们有一个月的时间,但是接受的答案返回0。

对于这些日期: ldate = 2020-08-30和 rdate = 2020-10-01,我们有三个月的时间,但是接受的答案返回 -2。

下面是计算两个日期之间月份数量的正确方法(可能不是唯一的方法,但是正确的) :

  • 你不必检查哪个日期比其他日期低。

  • 从月的第一天到最后一天的时间间隔算作一个月。

      public static int GetMontsBetween(DateTime date1, DateTime date2)
    {
    int monthCount = 0;
    int direction = date1 < date2 ? 1 : -1;
    date2 = date2.AddDays(direction);
    
    
    while (date1.Year != date2.Year || date1.Month != date2.Month)
    {
    date1 = date1.AddMonths(direction);
    monthCount++;
    }
    
    
    return monthCount;
    }