如何以有效的方式在年/月/周/日之间获得两个日期之间的差异?
例如。两个日期之间的差异是1年,2个月,3个星期,4天。
差异代表两个日期之间的年(s),月(s),周(s)和日(s)的数量。
答案 0 :(得分:32)
这实际上非常棘手。不同的总天数可能会产生相同的结果。例如:
2008年6月19日至2010年6月19日= 2年,但也是365 * 2天
2006年6月19日至2008年6月19日= 2年,但由于闰年也是365 + 366天
你可能想要减去年数,直到你得到两个相隔不到一年的日期。然后减去几个月,直到你得到两个相隔不到一个月的日期。
进一步的混淆:如果您可以从“3月30日”开始减去(或增加)月份是比较棘手的 - 比这个月还要早一个月?
更进一步的混乱(可能不相关):即使一天也不总是24小时。夏令时任何人?
更进一步的混乱(几乎可以肯定不相关):即使一分钟也不总是60秒。闰秒非常混乱......
我现在没有时间找出正确的方法 - 这个答案主要是为了提出这个问题并不像听起来那么简单。
编辑:不幸的是,我没有足够的时间来完全回答这个问题。我建议你先定义一个代表Period
的结构:
public struct Period
{
private readonly int days;
public int Days { get { return days; } }
private readonly int months;
public int Months { get { return months; } }
private readonly int years;
public int Years { get { return years; } }
public Period(int years, int months, int days)
{
this.years = years;
this.months = months;
this.days = days;
}
public Period WithDays(int newDays)
{
return new Period(years, months, newDays);
}
public Period WithMonths(int newMonths)
{
return new Period(years, newMonths, days);
}
public Period WithYears(int newYears)
{
return new Period(newYears, months, days);
}
public static DateTime operator +(DateTime date, Period period)
{
// TODO: Implement this!
}
public static Period Difference(DateTime first, DateTime second)
{
// TODO: Implement this!
}
}
我建议您首先实现+运算符,该运算符应该通知Difference
方法 - 您应该确保所有first + (Period.Difference(first, second)) == second
/ first
值的second
。
首先编写一系列单元测试 - 最初是“简单”的案例,然后转向涉及闰年的棘手问题。我知道正常的方法是一次编写一个测试,但在开始任何实现工作之前,我会亲自集思广益。
让自己有一天正确实施。这很棘手。
请注意,我在这里省略了几周 - 这个价值至少很容易,因为它总是7天。因此,给定(正)期间,您将拥有:
int years = period.Years;
int months = period.Months;
int weeks = period.Days / 7;
int daysWithinWeek = period.Days % 7;
(我建议你甚至不要考虑消极时期 - 确保所有时间都是积极的。)
答案 1 :(得分:17)
部分是为了尝试正确地回答这个问题(甚至可能是明确的......),部分是为了检查一个人可以信任粘贴在SO上的代码,并且部分地作为查找错误的练习,我创建了这个问题的一系列单元测试,并将它们应用于此页面中的许多建议解决方案以及几个重复项。
结果是确凿的:没有一个代码贡献准确地回答了这个问题。 更新:我现在有四个正确的解决方案,包括我自己的,请参阅下面的更新。
通过这个问题,我测试了以下用户的代码: Mohammed Ijas Nasirudeen,ruffin,Malu MN,Dave,pk。,Jani,lc。
这些都是他们的代码中提供了三年,几个月和几天的所有答案。请注意,其中两个,Dave和Jani,给出了总天数和月数,而不是计算年数后剩余的总月数,以及计算月数后剩余的总天数。我认为答案在OP似乎想要的方面是错误的,但是在这些情况下,单元测试显然不会告诉你太多。 (请注意,在Jani的情况下,这是我的错误,他的代码实际上是正确的 - 请参阅下面的更新4)
Jon Skeet,Aghasoleimani,Mukesh Kumar,Richard,Colin,Sheir的回答,我看到的,Chalkey和Andy,都是不完整的。这并不意味着答案没有任何好处,实际上其中有几个对解决方案是有用的贡献。这只意味着没有代码占用两个DateTime
并返回3 int
个我可以正确测试的代码。然而,其中四个人谈论使用TimeSpan
。正如许多人所提到的,TimeSpan
并不会返回任何大于天数的数字。
我测试的其他答案来自
这个.___ curious_geek的回答是他链接到的页面上的代码,我不认为他写的。 Jani的答案是唯一一个使用外部库,即.Net的时间段库。
所有这些问题的所有其他答案似乎都不完整。问题9是关于年龄的年龄,三个答案是超过简要和计算的年,月和日的答案。如果有人发现此问题的进一步重复,请告诉我。
很简单:我创建了一个界面
public interface IDateDifference
{
void SetDates(DateTime start, DateTime end);
int GetYears();
int GetMonths();
int GetDays();
}
对于每个答案,我编写了一个实现此接口的类,使用复制和粘贴的代码作为基础。当然我不得不调整具有不同签名等功能,但我尝试进行最小编辑,保留所有逻辑代码。
我在抽象泛型类中编写了一堆NUnit测试
[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
并添加了一个空的派生类
public class Rajeshwaran_S_P_Test : DateDifferenceTests<Rajeshwaran_S_P>
{
}
到每个IDateDifference
类的源文件。
NUnit非常聪明,可以完成剩下的任务。
其中一些是事先写好的,剩下的就是试图打破看似有效的实施。
[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
{
protected IDateDifference ddClass;
[SetUp]
public void Init()
{
ddClass = new DDC();
}
[Test]
public void BasicTest()
{
ddClass.SetDates(new DateTime(2012, 12, 1), new DateTime(2012, 12, 25));
CheckResults(0, 0, 24);
}
[Test]
public void AlmostTwoYearsTest()
{
ddClass.SetDates(new DateTime(2010, 8, 29), new DateTime(2012, 8, 14));
CheckResults(1, 11, 16);
}
[Test]
public void AlmostThreeYearsTest()
{
ddClass.SetDates(new DateTime(2009, 7, 29), new DateTime(2012, 7, 14));
CheckResults(2, 11, 15);
}
[Test]
public void BornOnALeapYearTest()
{
ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 2, 28));
CheckControversialResults(0, 11, 30, 1, 0, 0);
}
[Test]
public void BornOnALeapYearTest2()
{
ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 3, 1));
CheckControversialResults(1, 0, 0, 1, 0, 1);
}
[Test]
public void LongMonthToLongMonth()
{
ddClass.SetDates(new DateTime(2010, 1, 31), new DateTime(2010, 3, 31));
CheckResults(0, 2, 0);
}
[Test]
public void LongMonthToLongMonthPenultimateDay()
{
ddClass.SetDates(new DateTime(2009, 1, 31), new DateTime(2009, 3, 30));
CheckResults(0, 1, 30);
}
[Test]
public void LongMonthToShortMonth()
{
ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 30));
CheckControversialResults(0, 1, 0, 0, 0, 30);
}
[Test]
public void LongMonthToPartWayThruShortMonth()
{
ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 10));
CheckResults(0, 0, 10);
}
private void CheckResults(int years, int months, int days)
{
Assert.AreEqual(years, ddClass.GetYears());
Assert.AreEqual(months, ddClass.GetMonths());
Assert.AreEqual(days, ddClass.GetDays());
}
private void CheckControversialResults(int years, int months, int days,
int yearsAlt, int monthsAlt, int daysAlt)
{
// gives the right output but unhelpful messages
bool success = ((ddClass.GetYears() == years
&& ddClass.GetMonths() == months
&& ddClass.GetDays() == days)
||
(ddClass.GetYears() == yearsAlt
&& ddClass.GetMonths() == monthsAlt
&& ddClass.GetDays() == daysAlt));
Assert.IsTrue(success);
}
}
大多数名字都略显愚蠢,并没有真正解释为什么代码可能无法通过测试,但是看两个日期和答案应该足以理解测试。
所有Assert
,CheckResults()
和CheckControversialResults()
都有两个功能。这些可以很好地保存输入并提供正确的结果,但不幸的是,它们使得更难以确切地看到出现了什么问题(因为Assert
中的CheckControversialResults()
将失败并且#34;预期为真&#34;而不是告诉你哪个值不正确。如果有人有更好的方法(每次都避免写相同的检查,但有更多有用的错误信息),请告诉我。
CheckControversialResults()
用于几个案例,对于什么是正确的,似乎有两种不同的意见。我有自己的看法,但我认为我应该接受我在这里接受的自由。其中的要点是决定2月29日之后一年是2月28日还是3月1日。
这些测试是问题的症结所在,并且很可能会出现错误,所以如果您发现错误,请发表评论。听取其他测试的一些建议来检查任何未来的答案迭代也是很好的。
没有测试涉及时间 - 所有DateTime
都在午夜。包括时间在内,只要它清楚地表明如何逐日上升(我认为是这样),可能会出现更多的缺陷。
结果的完整记分板如下:
ChuckRostance_Test 3 failures S S S F S S F S F
Dave_Test 6 failures F F S F F F F S S
Dylan_Hayes_Test 9 failures F F F F F F F F F
ho1_Test 3 failures F F S S S S F S S
Jani_Test 6 failures F F S F F F F S S
Jon_Test 1 failure S S S S S S F S S
lc_Test 2 failures S S S S S F F S S
LukeH_Test 1 failure S S S S S S F S S
Malu_MN_Test 1 failure S S S S S S S F S
Mohammed_Ijas_Nasirudeen_Test 2 failures F S S F S S S S S
pk_Test 6 failures F F F S S F F F S
Rajeshwaran_S_P_Test 7 failures F F S F F S F F F
ruffin_Test 3 failures F S S F S S F S S
this_curious_geek_Test 2 failures F S S F S S S S S
但请注意,Jani的解决方案实际上是正确的,并通过了所有测试 - 请参阅下面的更新4.
列按测试名称的字母顺序排列:
三个答案每个只有1个测试失败,Jon,LukeH&Manu MN&#39。请记住,这些测试可能是专门为解决这些答案中的缺陷而编写的。
每个测试都至少通过一段代码,这有点令人放心,没有一个测试是错误的。
一些答案未通过大量测试。我希望没有人觉得这是对海报的努力的谴责。首先,成功的数量是相当随意的,因为测试不能均匀地覆盖问题空间的问题区域。其次,这不是生产代码 - 发布了答案,以便人们可以向他们学习,而不是将其完全复制到他们的程序中。未通过大量测试的代码仍然可以有很好的想法。至少有一件未通过大量测试的产品中有一个小错误,我没有修复。我非常感谢任何花时间与其他人分享工作的人,因为这个项目非常有趣。
有三个:
日历很难。 我写了九个测试,包括三个可能有两个答案的测试。我只有一个答案的一些测试可能不会得到一致同意。只考虑一下我们说“1个月后”的确切含义&#39;或者&#39; 2年前&#39;在很多情况下很棘手。而这些代码中没有一个必须处理所有复杂的事情,比如闰年时的工作。所有这些都使用库代码来处理日期。如果你想象&#39; spec&#39;因为在几天,几周,几个月和几年里写出来的时间,有各种各样的残余。因为我们从小学就知道这一点,并且每天都使用它,所以我们对许多特质都视而不见。问题不是学术问题 - 在债券和其他金融产品的会计软件中,各种类型的时间段分解为年,季度和月份都是必不可少的。
编写正确的代码很难。 有很多错误。在一个稍微更加模糊的主题或不那么受欢迎的问题,而不是一个没有被评论者指出的错误存在的机会远远高于这个问题。你应该永远不要,永远不要将SO中的代码复制到你的程序中,而不要完全理解它的作用。另一方面,您可能不应该在准备复制和粘贴的答案中编写代码,而是智能且富有表现力的伪代码,允许某人理解解决方案并实现自己的版本(使用他们的自己的错误!)
单元测试很有帮助。 当我接触到它时,我仍然意味着发布我自己的解决方案(为了其他人找到隐藏的,不正确的假设!)这样做是一个很好的例子来保存错误&#39;通过将它们转换为单元测试来修复下一版本的代码。
整个项目现在在https://github.com/jwg4/date-difference
这包括我自己的尝试jwg.cs
,它通过了我目前拥有的所有测试,包括一些新的测试,检查当天的正确处理时间。随意添加更多测试来打破这个和其他实现或更好的代码来回答问题。
@MattJohnson添加了一个使用Jon Skeet的NodaTime的实现。它通过了所有当前的测试。
@ KirkWoll对Difference in months between two dates的回答已添加到github上的项目中。它通过了所有当前的测试。
答案 2 :(得分:16)
对于年/月/周的正确差异计算,必须考虑 CultureInfo 的日历:
Time Period Library for .NET的 DateDiff 类尊重所有这些因素:
// ----------------------------------------------------------------------
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
// description
Console.WriteLine( "DateDiff.GetDescription(1): {0}", dateDiff.GetDescription( 1 ) );
// > DateDiff.GetDescription(1): 1 Year
Console.WriteLine( "DateDiff.GetDescription(2): {0}", dateDiff.GetDescription( 2 ) );
// > DateDiff.GetDescription(2): 1 Year 4 Months
Console.WriteLine( "DateDiff.GetDescription(3): {0}", dateDiff.GetDescription( 3 ) );
// > DateDiff.GetDescription(3): 1 Year 4 Months 12 Days
Console.WriteLine( "DateDiff.GetDescription(4): {0}", dateDiff.GetDescription( 4 ) );
// > DateDiff.GetDescription(4): 1 Year 4 Months 12 Days 12 Hours
Console.WriteLine( "DateDiff.GetDescription(5): {0}", dateDiff.GetDescription( 5 ) );
// > DateDiff.GetDescription(5): 1 Year 4 Months 12 Days 12 Hours 41 Mins
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
DateDiff 也会计算Quarters的差异。
答案 3 :(得分:15)
闰年和不均衡的月份实际上使这成为一个非常重要的问题。我确信有人可以提出一种更有效的方法,但这里有一个选项 - 首先是小方面的近似值并调整(未经测试):
public static void GetDifference(DateTime date1, DateTime date2, out int Years,
out int Months, out int Weeks, out int Days)
{
//assumes date2 is the bigger date for simplicity
//years
TimeSpan diff = date2 - date1;
Years = diff.Days / 366;
DateTime workingDate = date1.AddYears(Years);
while(workingDate.AddYears(1) <= date2)
{
workingDate = workingDate.AddYears(1);
Years++;
}
//months
diff = date2 - workingDate;
Months = diff.Days / 31;
workingDate = workingDate.AddMonths(Months);
while(workingDate.AddMonths(1) <= date2)
{
workingDate = workingDate.AddMonths(1);
Months++;
}
//weeks and days
diff = date2 - workingDate;
Weeks = diff.Days / 7; //weeks always have 7 days
Days = diff.Days % 7;
}
答案 4 :(得分:10)
使用System.Data.Linq
命名空间及其SqlMethods.DateDiffMonth
方法怎么样?
例如,说:
DateTime starDT = {01-Jul-2009 12:00:00 AM}
DateTime endDT = {01-Nov-2009 12:00:00 AM}
然后:
int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(startDT, endDT);
==&GT; 4
DateDiff
类中还有其他SqlMethods
个静态方法。
答案 5 :(得分:4)
减去两个DateTime
个实例,为您提供TimeSpan
属性Days
。 (例如在PowerShell中):
PS > ([datetime]::today - [datetime]"2009-04-07") Days : 89 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 0 Ticks : 76896000000000 TotalDays : 89 TotalHours : 2136 TotalMinutes : 128160 TotalSeconds : 7689600 TotalMilliseconds : 7689600000
将天数转换为数年或数周相对容易(一年中的天数可能是365,365.25,......取决于具体情况)。几个月要困难得多,因为没有基准日期,你不知道适用哪个月份。
假设你想从你的基准日期开始,你可以在计算第一年(检查闰年),然后是月份长度(从startDate.Month索引),然后是几周(剩余天数除以7)然后逐步减去天(剩余)。
需要考虑许多边缘情况,例如2005-03-01是2004-03-01,和2004-02-29 的一年,取决于你对“年”的意思。
答案 6 :(得分:3)
好吧,@ Jon Skeet,如果我们不担心得到比天更精细(并且仍然在更大的单位中滚动天而不是总天数),根据OP,它真的不是那么困难C#。使日期数学如此困难的原因是每个复合单元中的单元数经常变化。想象一下,如果每3加仑汽油只有3夸脱,但每12加仑只有7,除了星期五,... ...
幸运的是,日期只是greatest integer function的长途旅行。这些疯狂的异常是令人抓狂的,除非你已经完成了整个wackily组成的单位,当它不再是一个大问题。如果你出生在19/22/19,你在12/25/2000仍然是100,不管你经历过的闰年或秒或夏令时。一旦你完成了构成最后一个复合单元的百分比,你就会恢复统一。你已经添加了一个,然后重新开始。
这就是说,如果你做几年到几个月到几天,唯一奇怪的组成单位是月(天)。如果您需要借用月份值来处理您减去的天数,那么您只需要知道上个月的天数。没有其他异常值很重要。
C#在System.DateTime.DaysInMonth(intYear,intMonth)中给你。
(如果您的Now月份小于您的当月,则没有问题。每年有12个月。)
如果我们更细化那么同样的交易......你只需要知道最后一个(复合单位)中有多少(小单位)。一旦你过去,你会得到另一个整数值(复合单位)。然后减去你错过了多少个小单位,从那里开始,然后再添加你在现在通过复合单位中断的人数。
所以这就是我从减少两个日期的第一次减产中获得的。它可能会奏效。希望有用。
(编辑:将NewMonth&gt; OldMonth检查更改为NewMonth&gt; = OldMonth,因为如果月份相同(几天相同)我们不需要借一个。即2011年11月11日减去2010年11月9日给予了1年,12个月,2天(即2天,但是当皇室成员不需要时,我们借用了皇室。)
(编辑:当我们需要借用天数从dteNow.Day和dteNow.Day&lt; dteThen.Day减去dteThen.Day时,必须要检查月份=月份,因为我们不得不减去一年才能获得11几个月和额外的日子。好的,所以有一些异常值。^ DI认为我现在很接近。)
private void Form1_Load(object sender, EventArgs e) {
DateTime dteThen = DateTime.Parse("3/31/2010");
DateTime dteNow = DateTime.Now;
int intDiffInYears = 0;
int intDiffInMonths = 0;
int intDiffInDays = 0;
if (dteNow.Month >= dteThen.Month)
{
if (dteNow.Day >= dteThen.Day)
{ // this is a best case, easy subtraction situation
intDiffInYears = dteNow.Year - dteThen.Year;
intDiffInMonths = dteNow.Month - dteThen.Month;
intDiffInDays = dteNow.Day - dteThen.Day;
}
else
{ // else we need to substract one from the month diff (borrow the one)
// and days get wacky.
// Watch for the outlier of Month = Month with DayNow < DayThen, as then we've
// got to subtract one from the year diff to borrow a month and have enough
// days to subtract Then from Now.
if (dteNow.Month == dteThen.Month)
{
intDiffInYears = dteNow.Year - dteThen.Year - 1;
intDiffInMonths = 11; // we borrowed a year and broke ONLY
// the LAST month into subtractable days
// Stay with me -- because we borrowed days from the year, not the month,
// this is much different than what appears to be a similar calculation below.
// We know we're a full intDiffInYears years apart PLUS eleven months.
// Now we need to know how many days occurred before dteThen was done with
// dteThen.Month. Then we add the number of days we've "earned" in the current
// month.
//
// So 12/25/2009 to 12/1/2011 gives us
// 11-9 = 2 years, minus one to borrow days = 1 year difference.
// 1 year 11 months - 12 months = 11 months difference
// (days from 12/25 to the End Of Month) + (Begin of Month to 12/1) =
// (31-25) + (0+1) =
// 6 + 1 =
// 7 days diff
//
// 12/25/2009 to 12/1/2011 is 1 year, 11 months, 7 days apart. QED.
int intDaysInSharedMonth = System.DateTime.DaysInMonth(dteThen.Year, dteThen.Month);
intDiffInDays = intDaysInSharedMonth - dteThen.Day + dteNow.Day;
}
else
{
intDiffInYears = dteNow.Year - dteThen.Year;
intDiffInMonths = dteNow.Month - dteThen.Month - 1;
// So now figure out how many more days we'd need to get from dteThen's
// intDiffInMonth-th month to get to the current month/day in dteNow.
// That is, if we're comparing 2/8/2011 to 11/7/2011, we've got (10/8-2/8) = 8
// full months between the two dates. But then we've got to go from 10/8 to
// 11/07. So that's the previous month's (October) number of days (31) minus
// the number of days into the month dteThen went (8), giving the number of days
// needed to get us to the end of the month previous to dteNow (23). Now we
// add back the number of days that we've gone into dteNow's current month (7)
// to get the total number of days we've gone since we ran the greatest integer
// function on the month difference (23 to the end of the month + 7 into the
// next month == 30 total days. You gotta make it through October before you
// get another month, G, and it's got 31 days).
int intDaysInPrevMonth = System.DateTime.DaysInMonth(dteNow.Year, (dteNow.Month - 1));
intDiffInDays = intDaysInPrevMonth - dteThen.Day + dteNow.Day;
}
}
}
else
{
// else dteThen.Month > dteNow.Month, and we've got to amend our year subtraction
// because we haven't earned our entire year yet, and don't want an obo error.
intDiffInYears = dteNow.Year - dteThen.Year - 1;
// So if the dates were THEN: 6/15/1999 and NOW: 2/20/2010...
// Diff in years is 2010-1999 = 11, but since we're not to 6/15 yet, it's only 10.
// Diff in months is (Months in year == 12) - (Months lost between 1/1/1999 and 6/15/1999
// when dteThen's clock wasn't yet rolling == 6) = 6 months, then you add the months we
// have made it into this year already. The clock's been rolling through 2/20, so two months.
// Note that if the 20 in 2/20 hadn't been bigger than the 15 in 6/15, we're back to the
// intDaysInPrevMonth trick from earlier. We'll do that below, too.
intDiffInMonths = 12 - dteThen.Month + dteNow.Month;
if (dteNow.Day >= dteThen.Day)
{
intDiffInDays = dteNow.Day - dteThen.Day;
}
else
{
intDiffInMonths--; // subtract the month from which we're borrowing days.
// Maybe we shoulda factored this out previous to the if (dteNow.Month > dteThen.Month)
// call, but I think this is more readable code.
int intDaysInPrevMonth = System.DateTime.DaysInMonth(dteNow.Year, (dteNow.Month - 1));
intDiffInDays = intDaysInPrevMonth - dteThen.Day + dteNow.Day;
}
}
this.addToBox("Years: " + intDiffInYears + " Months: " + intDiffInMonths + " Days: " + intDiffInDays); // adds results to a rich text box.
}
答案 7 :(得分:2)
我在寻找解决类似问题时遇到过这篇文章。我试图以年,月,周和日为单位找到动物的年龄。然后,这些值显示在SpinEdits中,用户可以手动更改值以查找/估计出生日期。当我的表格从一个月内通过出生日期少于31天时,计算的价值为1天。我的解决方案是基于Ic上面的答案。
我的表单加载后调用的主要计算方法。
birthDateDisplay.Text = birthDate.ToString("MM/dd/yyyy");
DateTime currentDate = DateTime.Now;
Int32 numOfDays = 0;
Int32 numOfWeeks = 0;
Int32 numOfMonths = 0;
Int32 numOfYears = 0;
// changed code to follow this model http://stackoverflow.com/posts/1083990/revisions
//years
TimeSpan diff = currentDate - birthDate;
numOfYears = diff.Days / 366;
DateTime workingDate = birthDate.AddYears(numOfYears);
while (workingDate.AddYears(1) <= currentDate)
{
workingDate = workingDate.AddYears(1);
numOfYears++;
}
//months
diff = currentDate - workingDate;
numOfMonths = diff.Days / 31;
workingDate = workingDate.AddMonths(numOfMonths);
while (workingDate.AddMonths(1) <= currentDate)
{
workingDate = workingDate.AddMonths(1);
numOfMonths++;
}
//weeks and days
diff = currentDate - workingDate;
numOfWeeks = diff.Days / 7; //weeks always have 7 days
// if bday month is same as current month and bday day is after current day, the date is off by 1 day
if(DateTime.Now.Month == birthDate.Month && DateTime.Now.Day < birthDate.Day)
numOfDays = diff.Days % 7 + 1;
else
numOfDays = diff.Days % 7;
// If the there are fewer than 31 days in the birth month, the date calculated is 1 off
// Dont need to add a day for the first day of the month
int daysInMonth = 0;
if ((daysInMonth = DateTime.DaysInMonth(birthDate.Year, birthDate.Month)) != 31 && birthDate.Day != 1)
{
startDateforCalc = DateTime.Now.Date.AddDays(31 - daysInMonth);
// Need to add 1 more day if it is a leap year and Feb 29th is the date
if (DateTime.IsLeapYear(birthDate.Year) && birthDate.Day == 29)
startDateforCalc = startDateforCalc.AddDays(1);
}
yearsSpinEdit.Value = numOfYears;
monthsSpinEdit.Value = numOfMonths;
weeksSpinEdit.Value = numOfWeeks;
daysSpinEdit.Value = numOfDays;
然后,在我的spinEdit_EditValueChanged事件处理程序中,我根据旋转编辑中的值计算从startDateforCalc开始的新生日期。 (SpinEdits被限制为仅允许&gt; = 0)
birthDate = startDateforCalc.Date.AddYears(-((Int32)yearsSpinEdit.Value)).AddMonths(-((Int32)monthsSpinEdit.Value)).AddDays(-(7 * ((Int32)weeksSpinEdit.Value) + ((Int32)daysSpinEdit.Value)));
birthDateDisplay.Text = birthDate.ToString("MM/dd/yyyy");
我知道它不是最漂亮的解决方案,但它似乎对我来说都适用于所有月份和年份。
答案 8 :(得分:2)
TimeSpan period = endDate.AddDays(1) - startDate;
DateTime date = new DateTime(period.Ticks);
int totalYears = date.Year - 1;
int totalMonths = ((date.Year - 1) * 12) + date.Month - 1;
int totalWeeks = (int)period.TotalDays / 7;
date.Year - 1因为0年不存在。 date.Month - 1,第0个月不存在
答案 9 :(得分:2)
DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);
int diffMonth = Math.Abs((dt2.Year - dt1.Year)*12 + dt1.Month - dt2.Month)
答案 10 :(得分:2)
如果你减去两个DateTime,
的实例,它将返回TimeSpan的实例,这将代表两个日期之间的差异。
答案 11 :(得分:1)
天:(endDate - startDate).Days
周:(endDate - startDate).Days / 7
年:月/ 12
月份:TimeSpan仅提供天数,因此请使用以下代码获取指定开始日期和结束日期之间的整月数。例如,2000年10月1日到02/10/2000之间的整月数是1. 2000年10月1日到02/09/2000之间的整月数是0.
public int getMonths(DateTime startDate, DateTime endDate)
{
int months = 0;
if (endDate.Month <= startDate.Month)
{
if (endDate.Day < startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year - 1))
+ (12 - startDate.Month + endDate.Month - 1);
}
else if (endDate.Month < startDate.Month)
{
months = (12 * (endDate.Year - startDate.Year - 1))
+ (12 - startDate.Month + endDate.Month);
}
else // (endDate.Month == startDate.Month) && (endDate.Day >= startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year));
}
}
else if (endDate.Day < startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year))
+ (endDate.Month - startDate.Month) - 1;
}
else // (endDate.Month > startDate.Month) && (endDate.Day >= startDate.Day)
{
months = (12 * (endDate.Year - startDate.Year))
+ (endDate.Month - startDate.Month);
}
return months;
}
答案 12 :(得分:1)
如果你必须找到originalDate和今天的日期之间的差异,这是一个可靠的算法,没有那么多的条件检查。
我使用System.Data.Linq函数来查找年,月,日差异。请在下面找到c#代码
DateTime todaysDate = DateTime.Now;
DateTime interimDate = originalDate;
///Find Year diff
int yearDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffYear(interimDate, todaysDate);
interimDate = interimDate.AddYears(yearDiff);
if (interimDate > todaysDate)
{
yearDiff -= 1;
interimDate = interimDate.AddYears(-1);
}
///Find Month diff
int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(interimDate, todaysDate);
interimDate = interimDate.AddMonths(monthDiff);
if (interimDate > todaysDate)
{
monthDiff -= 1;
interimDate = interimDate.AddMonths(-1);
}
///Find Day diff
int daysDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffDay(interimDate, todaysDate);
答案 13 :(得分:1)
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
int gyear = dateTimePicker1.Value.Year;
int gmonth = dateTimePicker1.Value.Month;
int gday = dateTimePicker1.Value.Day;
int syear = DateTime.Now.Year;
int smonth = DateTime.Now.Month;
int sday = DateTime.Now.Day;
int difday = DateTime.DaysInMonth(syear, gmonth);
agedisplay = (syear - gyear);
lmonth = (smonth - gmonth);
lday = (sday - gday);
if (smonth < gmonth)
{
agedisplay = agedisplay - 1;
}
if (smonth == gmonth)
{
if (sday < (gday))
{
agedisplay = agedisplay - 1;
}
}
if (smonth < gmonth)
{
lmonth = (-(-smonth)+(-gmonth)+12);
}
if (lday < 0)
{
lday = difday - (-lday);
lmonth = lmonth - 1;
}
if (smonth == gmonth && sday < gday&&gyear!=syear)
{
lmonth = 11;
}
ageDisplay.Text = Convert.ToString(agedisplay) + " Years, " + lmonth + " Months, " + lday + " Days.";
}
答案 14 :(得分:1)
使用Noda Time:
var ld1 = new LocalDate(2012, 1, 1);
var ld2 = new LocalDate(2013, 12, 25);
var period = Period.Between(ld1, ld2);
Debug.WriteLine(period); // "P1Y11M24D" (ISO8601 format)
Debug.WriteLine(period.Years); // 1
Debug.WriteLine(period.Months); // 11
Debug.WriteLine(period.Days); // 24
答案 15 :(得分:0)
我会证明实际上没有完全一致的答案。为简化起见,让我们忽略年和周,只看非闰年中的月和日。
什么是预期的输出:
28/1 to 28/2?
-> Alice: "1 month" (1 month takes you to the same day of next month)
-> Brad: "1 month and 3 days" (3 days until end of Jan and the entire month of Feb)
两个合法的答案,这已经是第一个问题。但情况会变得更糟。让我们将日期差减少 1 天并再次询问:
28/1 to 27/2?
-> Alice: "30 days"
-> Brad: "Umm... 1 month and 2 days does not seem right!"
布拉德已经放弃了他的概念。但爱丽丝仍然为她感到高兴。接下来,让我们从最初的问题将日期差增加 1 天:
28/1 to 1/3?
-> Alice: "1 month and 1 day" (1 month to 28/2, plus 1 day)
Alice 现在真的很自信。
29/1 to 1/3?
-> Alice: "Umm... still 1 month 1 day?"
30/1 to 1/3?
-> Alice: "Ok, I see... "
Alice 和 Brad 的概念都不一致。对于时间跨度计数器,您必须接受以下两个警告中的一个:
1 month
到 1 month and 3 days
从一天到下一天1 month and 1 day
最多连续 4 天我个人更愿意接受最后一个,如果这意味着您的计数器一直在计数,但我想这取决于用例。
一旦您接受警告 #3,将计算出的时间跨度添加到 DateTime.MinValue
并从您着陆的地方获取年/月/日对我来说似乎是合理的近似值。下面的代码改编自 this answer。您也可以考虑闰年,以便在输出满足预期的情况下多花几天时间。
public static string GetYearMonthDayDiff(DateTime start, DateTime end)
{
var span = (end - start).Duration(); // absolute value (allows switched start/end)
var diff = DateTime.MinValue + span;
int years = diff.Year - 1;
int months = diff.Month - 1;
int days = diff.Day - 1; // matter of definition if you start counting at 0 or 1
return $"{years}Y {months}M {days}D";
}
上面的代码然后给出了以下答案
28/1 to 27/2? -> 30 days
28/1 to 28/2? -> 1 month
28/1 to 1/3? -> 1 month and 1 day
29/1 to 1/3 -> 1 month
30/1 to 1/3 -> 30 days
你不喜欢它,当这一切都加起来不完全时:)?
答案 16 :(得分:0)
int day=0,month=0,year=0;
DateTime smallDate = Convert.ToDateTime(string.Format("{0}", "01.01.1900"));
DateTime bigDate = Convert.ToDateTime(string.Format("{0}", "05.06.2019"));
TimeSpan timeSpan = new TimeSpan();
//timeSpan is diff between bigDate and smallDate as days
timeSpan = bigDate - smallDate;
//year is totalDays / 365 as int
year = timeSpan.Days / 365;
//smallDate.AddYears(year) is closing the difference for year because we found the year variable
smallDate = smallDate.AddYears(year);
//again subtraction because we don't need the year now
timeSpan = bigDate - smallDate;
//month is totalDays / 30 as int
month = timeSpan.Days / 30;
//smallDate.AddMonths(month) is closing the difference for month because we found the month variable
smallDate = smallDate.AddMonths(month);
if (bigDate > smallDate)
{
timeSpan = bigDate - smallDate;
day = timeSpan.Days;
}
//else it is mean already day is 0
答案 17 :(得分:0)
我有以下解决方案,对我来说正常工作(做了一些测试用例)。 额外的测试用例是可以接受的。
public class DateDiffernce
{
private int m_nDays = -1;
private int m_nWeek;
private int m_nMonth = -1;
private int m_nYear;
public int Days
{
get
{
return m_nDays;
}
}
public int Weeks
{
get
{
return m_nWeek;
}
}
public int Months
{
get
{
return m_nMonth;
}
}
public int Years
{
get
{
return m_nYear;
}
}
public void GetDifferenceBetwwenTwoDate(DateTime objDateTimeFromDate, DateTime objDateTimeToDate)
{
if (objDateTimeFromDate.Date > objDateTimeToDate.Date)
{
DateTime objDateTimeTemp = objDateTimeFromDate;
objDateTimeFromDate = objDateTimeToDate;
objDateTimeToDate = objDateTimeTemp;
}
if (objDateTimeFromDate == objDateTimeToDate)
{
//textBoxDifferenceDays.Text = " Same dates";
//textBoxAllDifference.Text = " Same dates";
return;
}
// If From Date's Day is bigger than borrow days from previous month
// & then subtract.
if (objDateTimeFromDate.Day > objDateTimeToDate.Day)
{
objDateTimeToDate = objDateTimeToDate.AddMonths(-1);
int nMonthDays = DateTime.DaysInMonth(objDateTimeToDate.Year, objDateTimeToDate.Month);
m_nDays = objDateTimeToDate.Day + nMonthDays - objDateTimeFromDate.Day;
}
// If From Date's Month is bigger than borrow 12 Month
// & then subtract.
if (objDateTimeFromDate.Month > objDateTimeToDate.Month)
{
objDateTimeToDate = objDateTimeToDate.AddYears(-1);
m_nMonth = objDateTimeToDate.Month + 12 - objDateTimeFromDate.Month;
}
//Below are best cases - simple subtraction
if (m_nDays == -1)
{
m_nDays = objDateTimeToDate.Day - objDateTimeFromDate.Day;
}
if (m_nMonth == -1)
{
m_nMonth = objDateTimeToDate.Month - objDateTimeFromDate.Month;
}
m_nYear = objDateTimeToDate.Year - objDateTimeFromDate.Year;
m_nWeek = m_nDays / 7;
m_nDays = m_nDays % 7;
}
}
答案 18 :(得分:0)
我试图找到一个明确的答案,包括年,月和日,我没有找到任何明确的答案,如果你还在寻找这种方法:
public static string GetDifference(DateTime d1, DateTime d2)
{
int[] monthDay = new int[12] { 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
DateTime fromDate;
DateTime toDate;
int year;
int month;
int day;
int increment = 0;
if (d1 > d2)
{
fromDate = d2;
toDate = d1;
}
else
{
fromDate = d1;
toDate = d2;
}
// Calculating Days
if (fromDate.Day > toDate.Day)
{
increment = monthDay[fromDate.Month - 1];
}
if (increment == -1)
{
if (DateTime.IsLeapYear(fromDate.Year))
{
increment = 29;
}
else
{
increment = 28;
}
}
if (increment != 0)
{
day = (toDate.Day + increment) - fromDate.Day;
increment = 1;
}
else
{
day = toDate.Day - fromDate.Day;
}
// Month Calculation
if ((fromDate.Month + increment) > toDate.Month)
{
month = (toDate.Month + 12) - (fromDate.Month + increment);
increment = 1;
}
else
{
month = (toDate.Month) - (fromDate.Month + increment);
increment = 0;
}
// Year Calculation
year = toDate.Year - (fromDate.Year + increment);
return year + " years " + month + " months " + day + " days";
}
答案 19 :(得分:0)
使用Subtract
对象的DateTime
方法返回TimeSpan
...
DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);
TimeSpan ts = dt1.Subtract(dt2);
Double days = ts.TotalDays;
Double hours = ts.TotalHours;
Double years = ts.TotalDays / 365;
答案 20 :(得分:-1)
根据Joaquim的回答,但在结束日期月份小于开始日期月份时修复计算,并添加代码以处理开始日期之前的结束日期:
public static class GeneralHelper
{
public static int GetYears(DateTime startDate, DateTime endDate)
{
if (endDate < startDate)
return -GetYears(endDate, startDate);
int years = (endDate.Year - startDate.Year);
if (endDate.Year == startDate.Year)
return years;
if (endDate.Month < startDate.Month)
return years - 1;
if (endDate.Month == startDate.Month && endDate.Day < startDate.Day)
return years - 1;
return years;
}
public static int GetMonths(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
return -GetMonths(endDate, startDate);
int months = 12 * GetYears(startDate, endDate);
if (endDate.Month > startDate.Month)
months = months + endDate.Month - startDate.Month;
else
months = 12 - startDate.Month + endDate.Month;
if (endDate.Day < startDate.Day)
months = months - 1;
return months;
}
}
[TestClass()]
public class GeneralHelperTest
{
[TestMethod]
public void GetYearsTest()
{
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2000, 12, 31)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 4, 4)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 5, 4)));
Assert.AreEqual(1, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 5, 5)));
Assert.AreEqual(1, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 12, 31)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 12, 31), new DateTime(2000, 5, 5)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2001, 4, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2001, 5, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetYears(new DateTime(2001, 5, 5), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetYears(new DateTime(2001, 12, 31), new DateTime(2000, 5, 5)));
}
[TestMethod]
public void GetMonthsTest()
{
Assert.AreEqual(0, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 4)));
Assert.AreEqual(1, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 5)));
Assert.AreEqual(1, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 6)));
Assert.AreEqual(11, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 5, 4)));
Assert.AreEqual(12, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 5, 5)));
Assert.AreEqual(13, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 6, 6)));
Assert.AreEqual(0, GeneralHelper.GetMonths(new DateTime(2000, 6, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetMonths(new DateTime(2000, 6, 5), new DateTime(2000, 5, 5)));
Assert.AreEqual(-1, GeneralHelper.GetMonths(new DateTime(2000, 6, 6), new DateTime(2000, 5, 5)));
Assert.AreEqual(-11, GeneralHelper.GetMonths(new DateTime(2001, 5, 4), new DateTime(2000, 5, 5)));
Assert.AreEqual(-12, GeneralHelper.GetMonths(new DateTime(2001, 5, 5), new DateTime(2000, 5, 5)));
Assert.AreEqual(-13, GeneralHelper.GetMonths(new DateTime(2001, 6, 6), new DateTime(2000, 5, 5)));
}
}
编辑不,这仍然无效。它没有通过这个测试:
Assert.AreEqual(24, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2003, 5, 5)));
预期:其中24。实际:其中12所取代;
答案 21 :(得分:-2)
DateTime startTime = DateTime.Now;
DateTime endTime = DateTime.Now.AddSeconds( 75 );
TimeSpan span = endTime.Subtract ( startTime );
Console.WriteLine( "Time Difference (seconds): " + span.Seconds );
Console.WriteLine( "Time Difference (minutes): " + span.Minutes );
Console.WriteLine( "Time Difference (hours): " + span.Hours );
Console.WriteLine( "Time Difference (days): " + span.Days );
输出:
Time Difference (seconds): 15
Time Difference (minutes): 1
Time Difference (hours): 0
Time Difference (days): 0
答案 22 :(得分:-2)
Console.WriteLine("Enter your Date of Birth to Know your Current age in DD/MM/YY Format");
string str = Console.ReadLine();
DateTime dt1 = DateTime.Parse(str);
DateTime dt2 = DateTime.Parse("10/06/2012");
int result = (dt2 - dt1).Days;
result = result / 365;
Console.WriteLine("Your Current age is {0} years.",result);