在C#中如何计算两个日期之间的月份差异?
是否有等效于VB的DateDiff()
方法的C#方法。我需要找到年份不同的两个日期之间的月份差异。文档中说我可以使用TimeSpan
,例如:
TimeSpan ts = date1 - date2;
但是这样会给我返回以天为单位的数据。我不想将这个数字除以30,因为并非每个月都有30天,而且由于这两个操作数之间相差很大,我担心除以30可能会给我错误的值。
有什么建议吗?
假设月份中的日期无关紧要(即2011.1.1与2010.12.31之间的差值为1),其中date1 > date2时返回正值,date2 > date1时返回负值。
((date1.Year - date2.Year) * 12) + date1.Month - date2.Month
或者假设你想要获取两个日期之间大致的“平均月数”,以下代码适用于除非日期差距非常大的情况。
date1.Subtract(date2).Days / (365.25 / 12)
请注意,如果您使用后一种解决方案,则您的单元测试应该声明应用程序可以处理的最宽日期范围,并相应地验证计算结果。
更新(感谢 Gary)
如果使用“平均月份”方法,则“每年平均天数”的更准确数字是365.2425。
(date1.Year - date2.Year) * 12 + date1.Month - date2.Month + (date1.Day >= date2.Day ? 0 : -1)
。 - DrunkCoder下面是一个全面的解决方案,用于返回一个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;
}
}
2020-02-29
到 2021-06-29
- 它返回 "1y 4m 1d",但实际上应该是 "1y 4m 0d",对吧? - EnigmativityPhase.Months
部分处理此场景来更新我的答案。对我来说似乎可以工作。 - Kirk WollDateTime.MinValue
或DateTime.MaxValue
,则会出现问题。例如:System.ArgumentOutOfRangeException': "The added or subtracted value results in an un-representable DateTime."
- iCollect.it Ltd你可以这样做
if ( date1.AddMonths(x) > date2 )
if ( date1.AddMonths(x).Month == date2.Month )
,然后您只需使用 x + 1 作为月份计数。 - Tommixpublic 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。 - Cristian Badila我通过 MSDN 检查了这种方法在 VB.NET 中的用法,发现它有很多用途。 在 C# 中没有这样的内置方法。(即使这不是一个好主意)您可以在 C# 中调用 VB。
Microsoft.VisualBasic.dll
添加到您的项目中作为参考。Microsoft.VisualBasic.DateAndTime.DateDiff
。Microsoft.VisualBasic.dll
模块,但执行该操作所需的时间微不足道。没有理由放弃经过全面测试和有用的功能,只是因为您选择使用C#编写程序。(这也适用于类似My.Application.SplashScreen
之类的内容。) - Cody GrayDateAndTime.Year()
,因为 DateTime
已经有了一个 Year
属性。它的存在只是为了让 VB.NET 看起来更像 VB6。作为一名前 VB6 程序员,我能够理解这一点 ;-) - Adam RalphDateTime 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);
start
和 end
是相同的。那么你会得到一个结果为 1。这怎么对呢?为什么要将结果加 1 呢?谁给这个答案点了赞 :-/ ? - paul我只需要一个简单的方法来处理只输入了月份/年份的就业日期,所以希望得到区分年份和月份的工作时长。这是我使用的代码,仅供参考。
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
};
}
// ----------------------------------------------------------------------
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
以下是我对获取月份差异的贡献,我认为这种方法非常精确:
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。
DateDiff
实现:http://referencesource.microsoft.com/#Microsoft.VisualBasic/DateAndTime.vb,229。 - dovid