简体   繁体   English

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

[英]How to get difference between two dates in Year/Month/Week/Day?

How to get difference between two dates in Year/Month/Week/Day in an efficient way? 如何以有效的方式在年/月/周/日中获得两个日期之间的差异?

eg. 例如。 difference between two dates is 1 Year, 2 Months, 3 Weeks, 4 Days. 两个日期之间的差异是1年,2个月,3个星期,4天。

Difference represents count of year(s), month(s), week(s) and day(s) between two dates. 差异表示两个日期之间的年(s),月(s),周(s)和日(s)的计数。

This is actually quite tricky. 这实际上非常棘手。 A different total number of days can result in the same result. 不同的总天数可能会产生相同的结果。 For example: 例如:

  • 19th June 2008 to 19th June 2010 = 2 years, but also 365 * 2 days 2008年6月19日至2010年6月19日= 2年,也是365 * 2天

  • 19th June 2006 to 19th June 2008 = 2 years, but also 365 + 366 days due to leap years 2006年6月19日至2008年6月19日= 2年,但由于闰年也是365 + 366天

You may well want to subtract years until you get to the point where you've got two dates which are less than a year apart. 你可能想要减去年数,直到你得到两个相隔不到一年的日期。 Then subtract months until you get to the point where you've got two dates which are less than a month apart. 然后减去几个月,直到你得到两个相隔不到一个月的日期。

Further confusion: subtracting (or adding) months is tricky when you might start with a date of "30th March" - what's a month earlier than that? 进一步混淆:当你可能以“3月30日”开始时减去(或增加)月份是棘手的 - 比这更早一个月?

Even further confusion ( may not be relevant): even a day isn't always 24 hours. 更进一步的混乱( 可能不相关):即使一天也不总是24小时。 Daylight saving anyone? 夏令时任何人?

Even further confusion (almost certainly not relevant): even a minute isn't always 60 seconds. 更进一步的混乱(几乎肯定相关):即使一分钟也不总是60秒。 Leap seconds are highly confusing... 闰秒非常令人困惑......

I don't have the time to work out the exact right way of doing this right now - this answer is mostly to raise the fact that it's not nearly as simple as it might sound. 我现在没有时间找出正确的方法 - 这个答案主要是为了提出这个事实并不像听起来那么简单。

EDIT: Unfortunately I'm not going to have enough time to answer this fully. 编辑:不幸的是,我没有足够的时间来完全回答这个问题。 I would suggest you start off by defining a struct representing a Period : 我建议你从定义代表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!
    }
}

I suggest you implement the + operator first, which should inform the Difference method - you should make sure that first + (Period.Difference(first, second)) == second for all first / second values. 我建议你首先实现+运算符,它应该通知Difference方法 - 你应该确保所有first / second值的first + (Period.Difference(first, second)) == second

Start with writing a whole slew of unit tests - initially "easy" cases, then move on to tricky ones involving leap years. 首先编写一系列单元测试 - 最初是“简单”的案例,然后转向涉及闰年的棘手问题。 I know the normal approach is to write one test at a time, but I'd personally brainstorm a bunch of them before you start any implementation work. 我知道正常的方法是一次编写一个测试,但在开始任何实现工作之前,我会亲自集体讨论它们。

Allow yourself a day to implement this properly. 让自己有一天正确实施。 It's tricky stuff. 这是棘手的事情。

Note that I've omitted weeks here - that value at least is easy, because it's always 7 days. 请注意,我在这里省略了几周 - 这个值至少很容易,因为它总是7天。 So given a (positive) period, you'd have: 因此,给定(正)期间,您将拥有:

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

(I suggest you avoid even thinking about negative periods - make sure everything is positive, all the time.) (我建议你甚至不要考虑消极时期 - 确保一切都是正面的。)

Partly as a preparation for trying to answer this question correctly (and maybe even definitively...), partly to examine how much one can trust code that is pasted on SO, and partly as an exercise in finding bugs, I created a bunch of unit tests for this question, and applied them to many proposed solutions from this page and a couple of duplicates. 部分是为了尝试正确地回答这个问题(甚至可能是最终......),部分是为了检查一个人可以信任粘贴在SO上的代码,并且部分地作为查找错误的练习,我创建了一堆对此问题进行单元测试,并将其应用于此页面中的许多建议解决方案以及几个重复项。

The results are conclusive: not a single one of the code contributions accurately answers the question. 结果是决定性的:没有一个代码贡献准确地回答了这个问题。 Update: I now have four correct solutions to this question, including my own, see updates below. 更新:我现在有四个正确的解决方案,包括我自己的,请参阅下面的更新。

Code tested 代码测试

From this question, I tested code by the following users: Mohammed Ijas Nasirudeen, ruffin, Malu MN, Dave, pk., Jani, lc. 从这个问题,我测试了以下用户的代码:Mohammed Ijas Nasirudeen,ruffin,Malu MN,Dave,pk。,Jani,lc。

These were all the answers which provided all three of years, months, and days in their code. 这些都是他们的代码中提供了三年,几个月和几天的所有答案。 Note that two of these, Dave and Jani, gave the total number of days and months, rather than the total number of months left after counting the years, and the total number of days left after counting the months. 请注意,其中两个,Dave和Jani,给出了总天数和月数,而不是计算年数后剩余的总月数,以及计算月数后剩余的总天数。 I think the answers are wrong in terms of what the OP seemed to want, but the unit tests obviously don't tell you much in these cases. 我认为答案在OP似乎想要的方面是错误的,但在这些情况下,单元测试显然不会告诉你太多。 (Note that in Jani's case this was my error and his code was actually correct - see Update 4 below) (请注意,在Jani的情况下,这是我的错误,他的代码实际上是正确的 - 请参阅下面的更新4)

The answers by Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir, just i saw, Chalkey and Andy, were incomplete. Jon Skeet,Aghasoleimani,Mukesh Kumar,Richard,Colin,Sheir的答案,我看到的,Chalkey和Andy,都是不完整的。 This doesn't mean that the answers weren't any good, in fact several of them are useful contributions towards a solution. 这并不意味着答案没有任何好处,实际上其中一些答案对解决方案是有用的贡献。 It just means that there wasn't code taking two DateTime s and returning 3 int s that I could properly test. 它只是意味着没有代码采用两个DateTime并返回3个int我可以正确测试。 Four of these do however talk about using TimeSpan . 然而,其中四个谈论使用TimeSpan As many people have mentioned, TimeSpan doesn't return counts of anything larger than days. 正如许多人所提到的, TimeSpan不会返回任何大于天数的计数。

The other answers I tested were from 我测试的其他答案来自

  • question 3054715 - LukeH, ho1 and this. 问题3054715 - LukeH,ho1和this。 ___curious_geek ___curious_geek
  • question 6260372 - Chuck Rostance and Jani (same answer as this question) 问题6260372 - Chuck Rostance和Jani(与这个问题的答案相同)
  • question 9 (!) - Dylan Hayes, Jon and Rajeshwaran SP 问题9(!) - Dylan Hayes,Jon和Rajeshwaran SP

this.___curious_geek's answer is code on a page he linked to, which I don't think he wrote. 这个.___ curious_geek的回答是他链接到的页面上的代码,我不认为他写的。 Jani's answer is the only one which uses an external library, Time Period Library for .Net. Jani的答案是唯一一个使用外部库,即.Net的时间段库。

All other answers on all these questions seemed to be incomplete. 所有这些问题的所有其他答案似乎都不完整。 Question 9 is about age in years, and the three answers are ones which exceeded the brief and calculated years, months and days. 问题9是关于年龄的年龄,三个答案是超过简要和计算的年,月和日的答案。 If anyone finds further duplicates of this question please let me know. 如果有人发现此问题的进一步重复,请告诉我。

How I tested 我是如何测试的

Quite simply: I made an interface 很简单:我做了一个界面

public interface IDateDifference
{
  void SetDates(DateTime start, DateTime end);
  int GetYears();
  int GetMonths();
  int GetDays();

}

For each answer I wrote a class implementing this interface, using the copied and pasted code as a basis. 对于每个答案,我编写了一个实现此接口的类,使用复制和粘贴的代码作为基础。 Of course I had to adapt functions with different signatures etc, but I tried to make the minimal edits to do so, preserving all the logic code. 当然,我必须调整具有不同签名等功能,但我尝试进行最小编辑,保留所有逻辑代码。

I wrote a bunch of NUnit tests in an abstract generic class 我在抽象泛型类中写了一堆NUnit测试

[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()

and added an empty derived class 并添加了一个空的派生类

public class Rajeshwaran_S_P_Test : DateDifferenceTests<Rajeshwaran_S_P>
{
}

to the source file for each IDateDifference class. 到每个IDateDifference类的源文件。

NUnit is clever enough to do the rest. NUnit足够聪明,可以做其余的事情。

The tests 测试

A couple of these were written in advance and the rest were written to try and break seemingly working implementations. 其中一些是事先编写的,其余的都是为了尝试打破看似有效的实现而编写的。

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

Most of the names are slightly silly and don't really explain why code might fail the test, however looking at the two dates and the answer(s) should be enough to understand the test. 大多数名称都略显愚蠢,并没有真正解释为什么代码可能会通过测试,但是看两个日期和答案应该足以理解测试。

There are two functions that do all the Assert s, CheckResults() and CheckControversialResults() . 有两个函数可以执行所有AssertCheckResults()CheckControversialResults() These work well to save typing and give the right results, but unfortunately they make it harder to see exactly what went wrong (because the Assert in CheckControversialResults() will fail with "Expected true", rather than telling you which value was incorrect. If anyone has a better way to do this (avoid writing the same checks each time, but have more useful error messages) please let me know. 这些方法可以很好地保存输入并提供正确的结果,但不幸的是,它们使得更难以确切地看到出现了什么问题(因为CheckControversialResults()Assert将以“Expected true”失败,而不是告诉您哪个值不正确。任何人都有更好的方法来做到这一点(避免每次都写相同的检查,但有更多有用的错误信息)请告诉我。

CheckControversialResults() is used for a couple of cases where there seem to be two different opinions on what is right. CheckControversialResults()用于几个案例,其中似乎有两种不同的意见。 I have an opinion of my own, but I thought I should be liberal in what I accepted here. 我有自己的看法,但我认为我应该接受我在这里接受的自由。 The gist of this is deciding whether one year after Feb 29 is Feb 28 or Mar 1. 这个要点是决定2月29日之后一年是2月28日还是3月1日。

These tests are the crux of the matter, and there could very well be errors in them, so please do comment if you find one which is wrong. 这些测试是问题的症结所在,它们中很可能存在错误,所以如果你发现错误,请发表评论。 It would be also good to hear some suggestions for other tests to check any future iterations of answers. 听取其他测试的一些建议来检查任何未来的答案迭代也是很好的。

No test involves time of day - all DateTime s are at midnight. 没有测试涉及一天中的时间 - 所有DateTime都在午夜。 Including times, as long as it's clear how rounding up and down to days works (I think it is), might show up even more flaws. 包括时间在内,只要能够清楚地了解天数如何运作(我认为是这样),可能会出现更多的缺陷。

The results 结果

The complete scoreboard of results is as follows: 完整的结果记分板如下:

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

But note that Jani's solution was actually correct and passed all tests - see update 4 below. 但请注意,Jani的解决方案实际上是正确的,并通过了所有测试 - 请参阅下面的更新4。

The columns are in alphabetical order of test name: 列按测试名称的字母顺序排列:

  • AlmostThreeYearsTest AlmostThreeYearsTest
  • AlmostTwoYearsTest AlmostTwoYearsTest
  • BasicTest BasicTest
  • BornOnALeapYearTest BornOnALeapYearTest
  • BornOnALeapYearTest2 BornOnALeapYearTest2
  • LongMonthToLongMonth LongMonthToLongMonth
  • LongMonthToLongMonthPenultimateDay LongMonthToLongMonthPenultimateDay
  • LongMonthToPartWayThruShortMonth LongMonthToPartWayThruShortMonth
  • LongMonthToShortMonth LongMonthToShortMonth

Three answers failed only 1 test each, Jon's, LukeH's and Manu MN's. 三个答案每个只有1个测试失败,Jon's,LukeH's和Manu MN's。 Bear in mind these tests were probably written specifically to address flaws in those answers. 请记住,这些测试可能是专门为解决这些答案中的缺陷而编写的。

Every test was passed by at least one piece of code, which is slightly reassuring that none of the tests are erroneous. 每个测试都至少通过一段代码,这有点令人放心,没有一个测试是错误的。

Some answers failed a lot of tests. 一些答案未通过大量测试。 I hope no-one feels this is a condemnation of that poster's efforts. 我希望没有人觉得这是对海报努力的谴责。 Firstly the number of successes is fairly arbitrary as the tests don't evenly cover the problem areas of the question space. 首先,成功的数量是相当随意的,因为测试不能均匀地覆盖问题空间的问题区域。 Secondly this is not production code - answers are posted so people can learn from them, not copy them exactly into their programs. 其次,这不是生产代码 - 发布了答案,以便人们可以向他们学习,而不是将其完全复制到他们的程序中。 Code which fails a lot of tests can still have great ideas in it. 未通过大量测试的代码仍然可以有很好的想法。 At least one piece which failed a lot of tests had a small bug in it which I didn't fix. 至少有一件未通过大量测试的部件中有一个小错误,我没有解决。 I'm grateful to anyone who took the time to share their work with everyone else, for making this project so interesting. 我很感激任何花时间与其他人分享工作的人,因为这个项目非常有趣。

My conclusions 我的结论

There are three: 有三种:

  1. Calendars are hard. 日历很难。 I wrote nine tests, including three where two answers are possible. 我写了九个测试,包括三个可能有两个答案的测试。 Some of the tests where I only had one answer might not be unanimously agreed with. 我只有一个答案的一些测试可能不会得到一致同意。 Just thinking about exactly what we mean when we say '1 month later' or '2 years earlier' is tricky in a lot of situations. 在我们说'1个月之后'或'2年之前'时,正好想一想我们的意思在很多情况下都很棘手。 And none of this code had to deal with all the complexities of things like working out when leap years are. 而这些代码中没有一个必须处理所有复杂的事情,比如闰年时的工作。 All of it uses library code to handle dates. 所有这些都使用库代码来处理日期。 If you imagine the 'spec' for telling time in days, weeks, months and years written out, there's all sorts of cruft. 如果你想象出用几天,几周,几个月和几年的时间来说明时间的'规范',那就有各种各样的瑕疵。 Because we know it pretty well since primary school, and use it everyday, we are blind to many of the idiosyncracies. 因为我们从小学就知道这一点,并且每天都使用它,所以我们对许多特质都视而不见。 The question is not an academic one - various types of decomposition of time periods into years, quarters and months are essential in accounting software for bonds and other financial products. 问题不是学术问题 - 在债券和其他金融产品的会计软件中,各种类型的时间段分解为年,季度和月份是必不可少的。

  2. Writing correct code is hard. 编写正确的代码很难。 There were a lot of bugs. 有很多错误。 In slightly more obscure topics or less popular questions than the chances of a bug existing without having been pointed out by a commenter are much, much higher than for this question. 在一个稍微更加模糊的主题或不那么受欢迎的问题,而不是一个没有被评论者指出的错误存在的机会远远高于这个问题。 You should really never, never copy code from SO into your program without understanding exactly what it does. 你应该永远不要,永远不要将SO中的代码复制到你的程序中,而不要完全理解它的作用。 The flipside of this is that you probably shouldn't write code in your answer that is ready to be copied and pasted, but rather intelligent and expressive pseudo-code that allows someone to understand the solution and implement their own version (with their own bugs!) 另一方面,您可能不应该在准备复制和粘贴的答案中编写代码,而是智能且富有表现力的伪代码,允许某人理解解决方案并实现自己的版本(带有自己的错误) !)

  3. Unit tests are helpful. 单元测试很有帮助。 I am still meaning to post my own solution to this when I get round to it (for someone else to find the hidden, incorrect assumptions in!) Doing this was a great example of 'saving the bugs' by turning them into unit tests to fix the next version of the code with. 当我接触它时,我仍然意味着发布我自己的解决方案(为了其他人找到隐藏的,不正确的假设!)这样做是一个很好的例子,通过将它们变成单元测试来“保存错误”修复下一版本的代码。

Update 更新

The whole project is now at https://github.com/jwg4/date-difference This includes my own attempt jwg.cs , which passes all the tests I currently have, including a few new ones which check for proper time of day handling. 整个项目现在位于https://github.com/jwg4/date-difference这包括我自己的尝试jwg.cs ,它通过了我目前的所有测试,包括一些新的测试,检查当天的正确处理时间。 Feel free to add either more tests to break this and other implementations or better code for answering the question. 随意添加更多测试来打破这个和其他实现或更好的代码来回答问题。

Update 2 更新2

@MattJohnson has added an implementation which uses Jon Skeet's NodaTime. @MattJohnson添加了一个使用Jon Skeet的NodaTime的实现。 It passes all the current tests. 它通过了所有当前的测试。

Update 3 更新3

@KirkWoll's answer to Difference in months between two dates has been added to the project on github. @girkWoll 在两个日期之间的差异答案已添加到github上的项目中。 It passes all the current tests. 它通过了所有当前的测试。

Update 4 更新4

@Jani pointed out in a comment that I had used his code wrongly. @Jani在评论中指出我错误地使用了他的代码。 He did suggest methods that counted the years, months and days correctly, (alongside some which count the total number of days and months, not the remainders) however I mistakenly used the wrong ones in my test code. 他确实提出了正确计算年数,月数和日数的方法(除了一些计算总天数和月数,而不是剩余数),但我错误地在我的测试代码中使用了错误的数据。 I have corrected my wrapper around his code and it now passes all tests. 我已经纠正了我的代码周围的包装,它现在通过了所有测试。 There are now four correct solutions, of which Jani's was the first. 现在有四种正确的解决方案,其中Jani是第一种。 Two use libraries (Intenso.TimePeriod and NodaTime) and two are written from scratch. 两个使用库(Intenso.TimePeriod和NodaTime)和两个从头开始编写。

For the correct difference calculation of Years/Months/Weeks, the Calendar of the CultureInfo must be considered: 对于年/月/周的正确差异计算,必须考虑CultureInfo日历

  • leap vs. non-leap years 飞跃与非闰年
  • months with different count of days 不同天数的月份
  • years with different count of weeks (varying by the first day of week and the calendar week rule) 具有不同周数的年份(根据一周的第一天和日历周规则而变化)

The DateDiff class of the Time Period Library for .NET respects all these factors: .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 also calculates the difference of Quarters. DateDiff还计算Quarters的差异。

Leap years and uneven months actually make this a non-trivial problem. 闰年和不均衡的月份实际上使这成为一个非常重要的问题。 I'm sure someone can come up with a more efficient way, but here's one option - approximate on the small side first and adjust up (untested): 我确信有人可以提出一种更有效的方法,但这里有一个选项 - 首先在小方面进行近似调整(未经测试):

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

What about using the System.Data.Linq namespace and its SqlMethods.DateDiffMonth method? 使用System.Data.Linq命名空间及其SqlMethods.DateDiffMonth方法怎么样?

For example, say: 例如,说:

DateTime starDT = {01-Jul-2009 12:00:00 AM}
DateTime endDT = {01-Nov-2009 12:00:00 AM}

Then: 然后:

int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(startDT, endDT);

==> 4 ==> 4

There are other DateDiff static methods in the SqlMethods class. SqlMethods类中还有其他DateDiff静态方法。

Subtract two DateTime instances to give you a TimeSpan which has a Days property. 减去两个DateTime实例以为您提供具有Days属性的TimeSpan (Eg in PowerShell): (例如在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

Converting days into years or weeks is relatively easy (days in a year could be 365, 365.25, ... depending on context). 将天数转换为数年或数周相对容易(一年中的天数可能是365,365.25,......取决于具体情况)。 Months is much harder, because without a base date you don't know which month lengths apply. 几个月要困难得多,因为没有基准日期,您不知道适用哪个月份。

Assuming you want to start with your base date, you can incrementally substract while counting first years (checking for leap years), then month lengths (indexing from startDate.Month), then weeks (remaining days divided by 7) and then days (remainder). 假设你想从你的基准日期开始,你可以在计算第一年(检查闰年),然后是月份长度(从startDate.Month索引),然后是几周(剩余天数除以7)然后是天数(余数)时逐步减去)。

There are a lot of edge cases to consider, eg 2005-03-01 is one year from 2004-03-01, and from 2004-02-29 depending on what you mean by "Year". 有很多边缘案例要考虑,例如2005-03-01是2004-03-01的一年, 2004-02-29取决于你的“年”的意思。

Well, @Jon Skeet, if we're not worried about getting any more granular than days (and still rolling days into larger units rather than having a total day count), as per the OP, it's really not that difficult in C#. 嗯,@ Jon Skeet,如果我们不担心得到更多细节而不是几天(并且仍然将轮换天数变成更大的单位而不是总天数),按照OP,在C#中真的不是那么难。 What makes date math so difficult is that the number of units in each composite unit often changes. 使日期数学如此困难的原因是每个复合单元中的单元数经常变化。 Imagine if every 3rd gallon of gas was only 3 quarts, but each 12th was 7, except on Fridays, when... 想象一下,如果每3加仑汽油只有3夸脱,但每12加仑只有7,除了周五,当......

Luckily, dates are just a long ride through the greatest integer function . 幸运的是,日期只是通过最大整数函数的长途旅行。 These crazy exceptions are maddening, unless you've gone all the way through the wackily-comprised unit, when it's not a big deal any more. 这些疯狂的异常是令人抓狂的,除非你已经完成了整个wackily组成的单位,当它不再是一个大问题。 If you're born on 12/25/1900, you're still EXACTLY 100 on 12/25/2000, regardless of the leap years or seconds or daylight savings periods you've been through. 如果你出生在19/22/19,你在12/25/2000仍然是100,不管你经历过的闰年或秒或夏令时。 As soon as you've slogged through the percentages that make up the last composite unit, you're back to unity. 一旦你完成了构成最后一个复合单元的百分比,你就会恢复统一。 You've added one, and get to start over. 你已经添加了一个,然后重新开始。

Which is just to say that if you're doing years to months to days, the only strangely comprised unit is the month (of days). 这只是说,如果你做几年到几个月到几天,唯一奇怪的组成单位是月(天)。 If you need to borrow from the month value to handle a place where you're subtracting more days than you've got, you just need to know the number of days in the previous month. 如果您需要借用月份值来处理您减去的天数,那么您只需要知道上个月的天数。 No other outliers matter. 没有其他异常值很重要。

And C# gives that to you in System.DateTime.DaysInMonth(intYear, intMonth). C#在System.DateTime.DaysInMonth(intYear,intMonth)中给你这个。

(If your Now month is smaller than your Then month, there's no issue. Every year has 12 months.) (如果您的Now月份小于您的月份,则没有问题。每年有12个月。)

And the same deal if we go more granular... you just need to know how many (small units) are in the last (composite unit). 如果我们更细化那么同样的交易......你只需要知道最后一个(复合单位)中有多少(小单位)。 Once you're past, you get another integer value more of (composite unit). 一旦你过去,你会得到另一个整数值(复合单位)。 Then subtract how many small units you missed starting where you did Then and add back how many of those you went past the composite unit break-off with your Now. 然后减去你错过了多少个小单位,从那里开始,然后再添加你通过复合单位中断的人数。

So here's what I've got from my first cut at subtracting two dates. 所以这就是我在减去两个日期时的第一次减产。 It might work. 它可能会奏效。 Hopefully useful. 希望有用。

(EDIT: Changed NewMonth > OldMonth check to NewMonth >= OldMonth, as we don't need to borrow one if the Months are the same (ditto for days). That is, Nov 11 2011 minus Nov 9 2010 was giving -1 year, 12 months, 2 days (ie, 2 days, but the royal we borrowed when royalty didn't need to.) (编辑:将NewMonth> OldMonth检查更改为NewMonth> = OldMonth,因为如果月份相同(几天相同)我们不需要借一个。也就是说,2011年11月11日减去2010年11月9日给予-1年,12个月,2天(即2天,但是当皇室不需要时我们借用的皇室。)

(EDIT: Had to check for Month = Month when we needed to borrow days to subtract a dteThen.Day from dteNow.Day & dteNow.Day < dteThen.Day, as we had to subtract a year to get 11 months and the extra days. Okay, so there are a few outliers. ;^DI think I'm close now.) (编辑:当我们需要借用天数来减去dteNow.Day和dteNow.Day <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.

}

如果你减去两个DateTime,实例DateTime,那将返回一个TimeSpan实例,它将代表两个日期之间的差异。

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)
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 because the year 0 doesn't exist. date.Year - 1因为0年不存在。 date.Month - 1, the month 0 doesn't exist date.Month - 1,月份0不存在

I came across this post while looking to solve a similar problem. 我在寻找解决类似问题时遇到过这篇文章。 I was trying to find the age of an animal in units of Years, Months, Weeks, and Days. 我试图以年,月,周和日为单位找到动物的年龄。 Those values are then displayed in SpinEdits where the user can manually change the values to find/estimate a birth date. 然后,这些值显示在SpinEdits中,用户可以手动更改值以查找/估计出生日期。 When my form was passed a birth date from a month with less than 31 days, the value calculated was 1 day off. 当我的表格从一个月内通过出生日期少于31天时,计算的价值为1天。 I based my solution off of Ic's answer above. 我的解决方案是基于Ic上面的答案。

Main calculation method that is called after my form loads. 在我的表单加载后调用的主要计算方法。

        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;

And then, in my spinEdit_EditValueChanged event handler, I calculate the new birth date starting from my startDateforCalc based on the values in the spin edits. 然后,在我的spinEdit_EditValueChanged事件处理程序中,我根据旋转编辑中的值计算从startDateforCalc开始的新生日期。 (SpinEdits are constrained to only allow >=0) (SpinEdits被限制为仅允许> = 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");

I know its not the prettiest solution, but it seems to be working for me for all month lengths and years. 我知道它不是最漂亮的解决方案,但它似乎对我来说适合所有月份和年份。

If you have to find the difference between originalDate and today's date, Here is a reliable algorithm without so many condition checks. 如果你必须找到originalDate和今天的日期之间的差异,这是一个没有这么多条件检查的可靠算法。

  1. Declare a intermediateDate variable and initialize to the originalDate 声明一个intermediateDate变量并初始化为originalDate
  2. Find difference between years.(yearDiff) 找出年份之间的差异。(yearDiff)
  3. Add yearDiff to intermediateDate and check whether the value is greater than today's date. 将yearDiff添加到intermediateDate并检查该值是否大于今天的日期。
  4. If newly obtained intermediateDate > today's date adjust the yearDiff and intermediateDate by one. 如果新获得的intermediateDate>今天的日期将yearDiff和intermediateDate调整为1。
  5. Continue above steps for month and Days. 继续上面的月份和日期步骤。

I have used System.Data.Linq functions to do find the year, month and day differences. 我已经使用System.Data.Linq函数来查找年,月和日的差异。 Please find c# code below 请在下面找到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);
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.";

    }

Use Noda Time : 使用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

Days: (endDate - startDate).Days 天:( endDate - startDate).Days
Weeks: (endDate - startDate).Days / 7 周:(endDate - startDate).Days / 7
Years: Months / 12 年:月/ 12
Months: A TimeSpan only provides Days, so use the following code to get the number of whole months between a specified start and end date. 月份:TimeSpan仅提供天数,因此请使用以下代码获取指定开始日期和结束日期之间的整月数。 For example, the number of whole months between 01/10/2000 and 02/10/2000 is 1. The the number of whole months between 01/10/2000 and 02/09/2000 is 0. 例如,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;
    }

Use the Subtract method of the DateTime object which returns a TimeSpan ... 使用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;

I was trying to find a clear answer for Years, Months and Days, and I didn't find anything clear, If you are still looking check this method: 我试图为年,月和日找到一个明确的答案,我没有找到任何清楚的答案,如果你还在寻找这个方法:

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

I have below solution which works correctly for me(After doing some Test cases). 我有以下解决方案,对我来说正常工作(做了一些测试用例)。 Extra Test cases are acceptable. 额外的测试用例是可以接受

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

Based on Joaquim's answer, but fixing the calculation when end date month is less than start date month, and adding code to handle end date before start date: 基于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)));
            }
   }

EDIT No that still doesn't work. 编辑没有仍然无法正常工作。 It fails this test: 它未通过此测试:

Assert.AreEqual(24, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2003, 5, 5)));

Expected:<24>. 预期:<24>。 Actual:<12> 实际:<12>

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

Output: 输出:

Time Difference (seconds): 15
Time Difference (minutes): 1
Time Difference (hours): 0
Time Difference (days): 0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM