如何获取年/月/周/日的两个日期之间的差异?

如何有效地获取年/月/周/日中两个日期之间的差异?

例如。 两个日期之间的差值为1年,2个月,3周,4天。

差异表示两个日期之间的年,月,周和天的计数。

Ahmed asked 2020-06-28T00:24:31Z
22个解决方案
34 votes

这实际上很棘手。 总天数不同可以得到相同的结果。 例如:

  • 2008年6月19日至2010年6月19日= 2年,但也为365 * 2天

  • 2006年6月19日至2008年6月19日= 2年,但由于leap年也需要365 + 366天

您可能要减去几年,直到到达两个相距不到一年的日期为止。 然后减去数月,直到达到两个相距不到一个月的日期为止。

进一步的混乱:如果您可能以“ 3月30日”作为开始日期,那么减去(或增加)月份会很棘手-比该日期早一个月?

更进一步的混乱(可能无关紧要):甚至一天也不总是24小时。 夏令时有人吗?

更进一步的混乱(几乎可以肯定是不相关的):一分钟并不总是60秒。 ap秒令人非常困惑...

我现在没有时间来确定执行此操作的正确方法-这个答案主要是为了提出一个事实,即它听起来并不简单。

编辑:不幸的是,我将没有足够的时间来完全回答这个问题。 我建议您先定义代表Difference的结构:

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/second179值都使用first + (Period.Difference(first, second)) == second

首先编写大量的单元测试-最初是“简单”的案例,然后继续进行涉及leap年的棘手案例。 我知道通常的方法是一次编写一个测试,但是在您开始执行任何实现工作之前,我个人都会进行头脑风暴。

给自己一天时间来正确实施此操作。 这是棘手的东西。

请注意,我在这里省略了几周-该值至少很容易,因为它始终为7天。 因此,给定一个(正)期间,您将拥有:

int years = period.Years;
int months = period.Months;
int weeks = period.Days / 7;
int daysWithinWeek = period.Days % 7;

(我建议您甚至不要考虑消极的时期-确保所有时间都是积极的。)

Jon Skeet answered 2020-06-28T00:25:39Z
17 votes

部分是为尝试正确回答这个问题(甚至可能是确定地……)做准备,部分是为了研究可以信任粘贴在SO上的代码的程度,部分是作为查找错误的练习,我创建了一堆 对该问题进行单元测试,并将其应用于此页面上的许多建议解决方案以及几个重复项。

结果是结论性的:没有一个代码贡献能够准确地回答问题。 更新:我现在有四个正确的解决方案,包括我自己的问题,请参见下面的更新。

经过测试的代码

基于这个问题,我测试了以下用户的代码:Mohammed Ijas Nasirudeen,ruffin,Malu MN,Dave,PK。,Jani,lc。

这些都是在其代码中提供了三年,几个月和几天的所有答案。 请注意,其中的两个Dave和Jani给出了天数和月数的总数,而不是计算年数后剩余的月数总数,以及给出了月数后的剩余天数总数。 我认为,就OP的需求而言,答案是错误的,但是在这些情况下,单元测试显然不能告诉您太多信息。 (请注意,在Jani的情况下,这是我的错误,并且他的代码实际上是正确的-请参阅下面的Update 4)

Jon Skeet,Aghasoleimani,Mukesh Kumar,Richard,Colin,Sheir(我看到的Chalkey和Andy)的答案并不完整。 这并不意味着答案没有任何意义,实际上,其中有几个是对解决方案的有益贡献。 这只是意味着没有代码接受两个我可以正确测试的jwg.css并返回3个2942157534314103103809s。 然而,其中有四个确实在谈论使用CheckControversialResults()。正如许多人所提到的,Assert不会返回大于天的计数。

我测试的其他答案来自

  • 问题3054715-卢克,ho1和这个。 ___curious_geek
  • 第6260372号问题-Chuck Rostance和Jani(与这个问题的答案相同)
  • 问题9(!)-Dylan Hayes,Jon和Rajeshwaran S P

this .___ 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>
{
}

到每个jwg.cs类的源文件。

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

大多数名称有些愚蠢,并不能真正解释代码为何无法通过测试,但是查看两个日期和答案应该足以理解测试。

所有jwg.css,CheckResults()CheckControversialResults()都有两个函数。它们可以很好地保存键入并给出正确的结果,但是不幸的是,它们使您很难准确地查明出了什么问题(因为CheckControversialResults()中的Assert将失败,并显示“预期为真”。 ”,而不是告诉您哪个值不正确。如果有人有更好的方法(避免每次都写相同的检查,但是有更多有用的错误消息),请告诉我。

jwg.cs用于几种情况,对于正确的情况似乎有两种不同的看法。 我有自己的看法,但我认为我应该在这里接受的态度宽松。 要点是确定2月29日之后的一年是2月28日还是3月1日。

这些测试是问题的症结所在,并且很可能会出现错误,因此如果发现错误,请发表评论。 听到一些关于其他测试的建议,以检查答案的将来任何迭代也是很好的。

没有测试涉及一天中的时间-所有jwg.css都是在午夜。 包括时间在内,只要很清楚地舍入为整数(我认为是这样),就可能会出现更多缺陷。

结果

结果的完整记分板如下:

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。

这些列按测试名称的字母顺序排列:

  • 近三年测试
  • 差不多两年测试
  • 基本测试
  • 说明MayerTest
  • 说明MayerTest 2
  • 长月到长月
  • 长月至长月倒数第二天
  • LongMonthToPartWayThruShortMonth
  • 长月到短月

乔恩(Jon's),卢克(LukeH)和曼努(Manu MN)的三个答案均只有1个测试不及格。 请记住,这些测试可能是专门为解决这些答案中的缺陷而编写的。

每个测试至少要通过一条代码,这可以稍微保证所有测试都不是错误的。

一些答案在很多测试中都失败了。 我希望没有人会觉得这是对那位海报制作者的谴责。 首先,成功的数目是相当随意的,因为测试不能均匀地覆盖问题空间的问题领域。 其次,这不是生产代码-发布答案,以便人们可以从中学习,而不是将其完全复制到他们的程序中。 在很多测试中失败的代码中仍然可以包含很多想法。 至少有一部分未通过大量测试,但其中有一个小错误,但我没有修复。 感谢所有花时间与其他人分享他们的工作的人,感谢他使这个项目变得如此有趣。

我的结论

有三种:

  1. 日历很难。我写了9个测试,其中3个可以给出两个答案。 我只有一个答案的某些测试可能未获得一致同意。 在很多情况下,仅想一想“晚一个月”或“早两年”是什么意思。 而且,这些代码都不需要处理所有复杂的事情,例如在leap年时进行锻炼。 所有这些都使用库代码来处理日期。 如果您想像一下要写出以天,周,月和年为单位的时间说明的“规范”,那么您将遇到各种各样的麻烦。 因为我们从小学起就非常了解它,并且每天都在使用它,所以我们对许多特质视而不见。 问题不是学术上的问题-在债券和其他金融产品的会计软件中,将时间段分解成年,季度和月的各种类型至关重要。

  2. 编写正确的代码很困难。有很多错误。 在一些晦涩的话题或不那么受欢迎的问题中,比没有被评论者指出的存在错误的机会要高得多,比这个问题要高得多。 您应该绝对不要,永远不要在不完全了解SO的功能的情况下将代码从SO复制到您的程序中。 不利的一面是,您可能不应该在准备好要复制和粘贴的答案中编写代码,而应该使用智能且富有表现力的伪代码,使某人可以理解解决方案并实现自己的版本(带有自己的错误) !)

  3. 单元测试很有帮助。我仍然想在解决这个问题时发布自己的解决方案(供其他人在其中找到隐藏的,错误的假设!)这样做是通过将其转换为单元测试以“保存错误”的一个很好的例子。 修复代码的下一个版本。

更新资料

整个项目现在位于[https://github.com/jwg4/date-difference]这包括我自己的尝试jwg.cs,该尝试通过了我目前进行的所有测试,包括一些新的测试,这些测试可以检查一天中的正确时间。 随意添加更多测试以破坏此实现和其他实现,或者添加更好的代码来回答问题。

更新2

@MattJohnson添加了使用Jon Skeet的NodaTime的实现。 它通过了所有当前测试。

更新3

@KirkWoll对两个日期之间月份差异的回答已添加到github上的项目中。 它通过了所有当前测试。

更新4

@Jani在评论中指出我错误地使用了他的代码。 他的确提出了正确计算年,月和日的方法(除了一些计算了天和月的总数,而不是剩余数的方法),但是我在测试代码中错误地使用了错误的方法。 我已经更正了他的代码的包装程序,现在它可以通过所有测试。 现在有四个正确的解决方案,其中Jani是第一个。 两个使用库(Intenso.TimePeriod和NodaTime),两个从头开始编写。

jwg answered 2020-06-28T00:30:07Z
16 votes

为了正确计算年/月/周的差异,必须考虑CultureInfo的日历:

  • vs年与非-年
  • 不同天数的月份
  • 不同星期数的年份(根据星期的第一天和日历周的规则而变化)

.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还计算季度的差额。

answered 2020-06-28T00:30:50Z
15 votes

years年和不平坦的月份实际上使这成为一个不平凡的问题。 我敢肯定有人会想出一种更有效的方法,但这是一个选择-首先从小角度进行估算,然后进行调整(未经测试):

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;
}
lc. answered 2020-06-28T00:31:10Z
10 votes

使用DateDiff命名空间及其SqlMethods方法怎么样?

例如,说:

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

==> 4

SqlMethods类中还有其他DateDiff静态方法。

sheir answered 2020-06-28T00:31:47Z
4 votes

减去两个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等,具体取决于上下文)。 月份要难得多,因为没有基准日期,您将不知道哪个月份适用。

假设您想从基准日期开始,您可以在计算前几年(检查leap年),然后按月长度(从startDate.Month索引),然后按周(剩余天数除以7),然后按天(剩余数)时递减。 )。

有很多边缘情况需要考虑,例如 2005-03-01是2004-03-01和2004-02-29的一年,具体取决于您所说的“年份”。

Richard answered 2020-06-28T00:32:21Z
3 votes

好吧,@ Jon Skeet,如果我们不担心要获得比天数更细的数据(并且仍将天数滚动成更大的单位,而不是总天数),那么根据OP,在C#中并不是那么困难。 使日期数学如此困难的原因是每个复合单元中的单元数经常变化。 想象一下,如果每3加仑汽油只有3夸脱,但是每12加仑汽油只有7夸脱,那么星期五除外,那时……

幸运的是,通过最大的整数函数,日期只是很长的路要走。 这些疯狂的例外令人发疯,除非您一路走过这个由怪异组成的单位,而现在这已不再是什么大问题了。 如果您出生于1900年12月25日,那么无论您经历的是years年还是秒或夏令时,您在2000年12月25日的年龄仍然是100。 一旦您花完了构成最后一个复合单位的百分比,您就会恢复统一。 您已经添加了一个,然后重新开始。

这就是说,如果您要进行数年至数月至数日的操作,则唯一奇怪的组成单位是月(以天为单位)。 如果您需要从月份值中借钱来处理要减去的天数超过所剩天数的地方,则只需知道上个月的天数即可。 没有其他异常值很重要。

C#在System.DateTime.DaysInMonth(intYear,intMonth)中将其提供给您。

(如果您的Now月份小于then月份,则没有问题。每年有12个月。)

如果我们更细化,这也是相同的……您只需要知道最后一个(复合单位)中有多少(小单位)。 过去之后,您将获得另一个(复合单位)的整数值。 然后从开始的位置减去您错过了多少个小单元,然后加回您现在使用复合单元突破的小单元。

因此,这就是我第一次减去两个日期后得到的结果。 它可能会起作用。 希望有用。

(编辑:将NewMonth> OldMonth检查更改为NewMonth> = OldMonth,因为如果Months相同(天数同上),我们不需要借入一笔。也就是说,2011年11月11日减去2010年11月9日给出的是-1年 ,12个月,2天(即2天,但我们在不需要提成时借用了王室)。

(编辑:必须检查Month = Month,我们需要借用天数来从dteNow.Day和dteNow.Day <dteThen.Day中减去dteThen.Day,因为我们必须减去一年才能得到11个月和额外的天数 好的,所以有一些异常值。

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.

}
ruffin answered 2020-06-28T00:33:20Z
2 votes

如果减去DateTime,的两个实例,则将返回TimeSpan的实例,该实例将表示两个日期之间的差值。

Andy answered 2020-06-28T00:33:40Z
2 votes
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)
Blorgbeard answered 2020-06-28T00:33:55Z
2 votes

我在寻找解决类似问题时遇到了这篇文章。 我试图以年,月,周和天为单位找到动物的年龄。 这些值然后显示在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开始计算新的出生日期。 (SpinEdit被限制为仅允许> = 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");

我知道这不是最漂亮的解决方案,但是它似乎对我来说都适用了所有月份和年份。

pk. answered 2020-06-28T00:34:29Z
2 votes
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不存在

agua from mars answered 2020-06-28T00:34:49Z
1 votes

天数:(结束日期-开始日期)。
周:(endDate-startDate)。天/ 7
年:月/ 12
月:“时间跨度”仅提供“天”,因此请使用以下代码获取指定开始日期和结束日期之间的整月数。 例如,2000年1月10日到2000年2月10日之间的整月数是1。2000年1月10日到2000年2月9日之间的整月数是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;
    }
Dave answered 2020-06-28T00:35:23Z
1 votes

如果您必须找出originalDate和今天的日期之间的差异,这是一种可靠的算法,无需进行太多条件检查。

  1. 声明一个middleDate变量并初始化为originalDate
  2. 找出年份之间的差异。(yearDiff)
  3. 将yearDiff添加到middleDate,然后检查该值是否大于今天的日期。
  4. 如果新获得的middleDate>今天的日期,则将yearDiff和middleDate调整一个。
  5. 继续上述步骤进行月份和天数。

我使用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);
Malu MN answered 2020-06-28T00:36:10Z
1 votes
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.";

    }
Mohammed Ijas Nasirudeen answered 2020-06-28T00:36:25Z
1 votes

使用Noda时间:

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
Matt Johnson-Pint answered 2020-06-28T00:36:45Z
0 votes

使用DateTime对象的Subtract方法可返回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;
answered 2020-06-28T00:37:05Z
0 votes

我试图为年,月和日找到一个明确的答案,但没有找到任何明确的答案。如果您仍在寻找此方法,请执行以下操作:

        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";
        }
Chris Rosete answered 2020-06-28T00:37:25Z
0 votes

我有下面的解决方案,对我来说正确工作(做一些测试用例后)。可以接受额外的测试用例。

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;    
    }
}
Neel Maheta answered 2020-06-28T00:37:45Z
0 votes
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
ubsygt answered 2020-06-28T00:38:01Z
-1 votes

基于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>

Colin answered 2020-06-28T00:38:29Z
-2 votes
    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);
Mukesh Kumar answered 2020-06-28T00:38:45Z
-2 votes
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
Aghasoleimani answered 2020-06-28T00:39:05Z
translate from https://stackoverflow.com:/questions/1083955/how-to-get-difference-between-two-dates-in-year-month-week-day