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

时间:2022-12-28 21:29:14

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.

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

21 个解决方案

#1


31  

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. Daylight saving anyone?

更让人困惑的是:即使一天也不总是24小时。夏令时吗?

Even further confusion (almost certainly not relevant): even a minute isn't always 60 seconds. Leap seconds are highly confusing...

更让人困惑的是:一分钟也不总是60秒。闰秒非常令人困惑……

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:

编辑:不幸的是,我没有足够的时间来回答这个问题。我建议你首先定义一个表示周期的结构体:

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.

我建议您先实现+运算符,它应该通知差分方法—您应该确保第一个+(句号)。差分(first, second)) =所有first/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. So given a (positive) period, you'd have:

注意,我在这里省略了几周——这个值至少很简单,因为它总是7天。给定一个(正值)周期,你会:

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

(我建议你避免考虑消极时期——确保一切都是积极的。)

#2


17  

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.

部分是准备试图回答这个问题正确(甚至明确…),部分原因是为了检查多少一个可以信任的代码粘贴,和部分作为练习发现bug,我创建了一个群单元测试这个问题,从这个页面和应用他们许多提出的解决方案和一些重复。

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. 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. (Note that in Jani's case this was my error and his code was actually correct - see Update 4 below)

这些都是在他们的代码中提供了三年、几个月和几天的所有答案。请注意,其中的两个,Dave和Jani,给出了天数和月份的总数,而不是数了数年后剩下的几个月的总数,以及数个月后剩下的天数。我认为对于OP的需求来说,答案是错误的,但是单元测试显然在这些情况下不会告诉你太多。(注意,在贾尼的案例中,这是我的错误,他的代码实际上是正确的——参见下面的更新4)

The answers by Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir, just i saw, Chalkey and Andy, were incomplete. 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 DateTimes and returning 3 ints that I could properly test. Four of these do however talk about using TimeSpan. As many people have mentioned, TimeSpan doesn't return counts of anything larger than days.

Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir,我看到的,Chalkey和Andy的回答是不完整的。这并不意味着答案是不好的,事实上,其中有几个是对解决方案有用的贡献。它只是意味着没有代码花费两个日期时间并返回3个我可以正确测试的ints。其中有4个是关于使用TimeSpan的。正如许多人提到的,TimeSpan不返回任何大于天数的计数。

The other answers I tested were from

我测试的其他答案来自

  • question 3054715 - LukeH, ho1 and this. ___curious_geek
  • 问题3054715 -卢克,ho1和这个。___curious_geek
  • question 6260372 - Chuck Rostance and Jani (same answer as this question)
  • 问题6260372 - Chuck Rostance和Jani(与此问题相同)
  • question 9 (!) - Dylan Hayes, Jon and Rajeshwaran S P
  • 问题9(!)迪伦·海耶斯,琼恩和拉杰什瓦兰的P

this.___curious_geek's answer is code on a page he linked to, which I don't think he wrote. 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. If anyone finds further duplicates of this question please let me know.

所有这些问题的答案似乎都是不完整的。问题9是关于年岁的问题,这三个答案都超过了简单计算的年份、月份和天数。如果有人发现这个问题的更多副本,请告诉我。

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.

其余的都是聪明的。

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 Asserts, CheckResults() and 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.

执行所有断言的函数有两个:CheckResults()和checkalresults()。这些方法可以很好地保存输入并给出正确的结果,但不幸的是,它们使我们很难确切地看到哪里出错了(因为checkdispute results()中的Assert将在“Expected true”中失败,而不是告诉您哪个值不正确。如果有人有更好的方法(避免每次都写同样的检查,但是有更多有用的错误信息),请告诉我。

CheckControversialResults() is used for a couple of cases where there seem to be two different opinions on what is right. 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.

checkcontroalresults()用于两种情况,在这些情况下,似乎对什么是正确的有两种不同的观点。我有自己的看法,但我认为我在这里接受的应该是*的。这其中的要点是决定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 DateTimes are at midnight. 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. Bear in mind these tests were probably written specifically to address flaws in those answers.

三个答案都没有通过,只有一个测试,Jon,卢克和Manu MN的。请记住,这些测试可能是专门为解决这些问题的缺陷而编写的。

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

    日历是很难的。我写了9个测试,包括3个可能有两个答案的测试。我只有一个答案的一些测试可能不会得到一致的同意。当我们说“1个月后”或“2年之前”的时候,在很多情况下都很棘手。这些代码都不需要处理所有复杂的事情,比如计算闰年是什么时候。所有这些都使用库代码来处理日期。如果你想象一下在几天、几周、几个月甚至几年的时间里说出的“规范”,就会发现各种各样的问题。因为我们从小就很了解它,并且每天都在使用它,所以我们对很多特质视而不见。这个问题不是一个学术问题——在债券和其他金融产品的会计软件中,不同类型的时间间隔分解为年、季度和月份是必不可少的。

  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. 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!)

    编写正确的代码很困难。有很多bug。在稍微模糊的话题或不太流行的问题中,一个bug不被评论人指出的可能性要比这个问题高得多。你真的应该永远,永远不要把代码从SO拷贝到你的程序中而不知道它到底是做什么的。它的另一面是,您可能不应该在您的答案中编写可以复制和粘贴的代码,而应该编写智能的、有表现力的伪代码,这些伪代码允许某人理解解决方案并实现自己的版本(有自己的错误!)

  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. Feel free to add either more tests to break this and other implementations or better code for answering the question.

整个项目现在位于https://github.com/jwg4/date-difference包括我自己的尝试jwg。cs,通过了我目前所有的测试,包括一些新的测试,它们检查了正确的一天处理时间。可以添加更多的测试来打破这个和其他的实现,也可以添加更好的代码来回答这个问题。

Update 2

@MattJohnson has added an implementation which uses Jon Skeet's NodaTime. It passes all the current tests.

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

Update 3

@KirkWoll's answer to Difference in months between two dates has been added to the project on github. It passes all the current tests.

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

Update 4

@Jani pointed out in a comment that I had used his code wrongly. 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. Two use libraries (Intenso.TimePeriod and NodaTime) and two are written from scratch.

@Jani在评论中指出,我错误地使用了他的代码。他的确提出了正确计算年份、月份和天数的方法(有些方法只计算天数和月份,而不是剩余的天数),但我在测试代码中错误地使用了错误的方法。我已经在他的代码周围纠正了我的包装,现在它通过了所有的测试。现在有四个正确的解决方案,其中贾尼是第一个。两个用库(Intenso。时间周期和节点)和两个是从零开始写的。

#3


16  

For the correct difference calculation of Years/Months/Weeks, the Calendar of the CultureInfo must be considered:

对于正确计算年份/月/周的差异,必须考虑文化信息的日历:

  • leap vs. non-leap years
  • 飞跃与non-leap年
  • 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还计算了季度差异。

#4


15  

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

#5


10  

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静态方法。

#6


4  

Subtract two DateTime instances to give you a TimeSpan which has a Days property. (E.g. in PowerShell):

减去两个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

Converting days into years or weeks is relatively easy (days in a year could be 365, 365.25, ... depending on context). Months is much harder, because without a base date you don't know which month lengths apply.

将一天转换成年或周是相对容易的(一年中的天数可以是365天,365.25天,……)根据上下文)。月要难得多,因为没有基准日期,你不知道哪个月的长度适用。

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

假设你想从你的基本日期开始,你可以在计算最初年份(检查闰年)、月份长度(索引从起始日期开始。月)、周(剩余天数除以7)和天(剩余天数)的同时,递增地细分。

There are a lot of edge cases to consider, e.g. 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年取决于你所说的“年”是什么意思。

#7


3  

If you subtract two instances of DateTime, that will return an instance of TimeSpan, which will represent the difference between the two dates.

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

#8


3  

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#. 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...

嗯,@Jon Skeet,如果我们不担心变得比天数更细(仍然将天数滚动成更大的单位,而不是有一天的总数),就像在OP中那样,c#真的没有那么难。使日期计算如此困难的是,每个复合单元的单元数经常发生变化。想象一下,如果每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. 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. 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.

幸运的是,日期只是通过最大整数函数的一段很长的路程。这些疯狂的例外是令人抓狂的,除非你已经通过了组成古怪的单元,而这已经不是什么大事了。如果你出生在1900年12月25日,你在2000年12月25日仍然是100岁,不管你经历过闰年、秒还是白天储蓄。一旦你在组成最后一个复合单元的百分比上做了手脚,你就回到了统一。你已经添加了一个,然后重新开始。

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

(如果你现在的月比以前的月小,那就没有问题了。)每年有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.)

(编辑:把每月的>月份的月份改为>= OldMonth,因为如果月份是相同的,我们不需要借1个月)。也就是说,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. ;^D I think I'm close now.)

(编辑:当我们需要借几天的时间去减去一个dteThen时,必须检查月=月。从dteNow天。天& dteNow。天< dteThen。天,因为我们要减去一年才能得到11个月和额外的天数。好,有一些离群值。,^ D我认为我现在关闭。)

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.

}

#9


2  

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);

int diffMonth = Math.Abs((dt2.Year - dt1.Year)*12 + dt1.Month - dt2.Month)

#10


2  

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. When my form was passed a birth date from a month with less than 31 days, the value calculated was 1 day off. I based my solution off of Ic's answer above.

我在寻找解决类似问题的方法时遇到了这篇文章。我试着以年、月、周、日为单位寻找动物的年龄。然后这些值显示在spinedit中,用户可以手动更改这些值以查找/估计出生日期。当我的表格通过一个月以内的出生日期时,计算的值是1天的假。我根据上面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://*.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. (SpinEdits are constrained to only allow >=0)

然后,在spinEdit_EditValueChanged事件处理程序中,我根据spin edits中的值从startDateforCalc开始计算新的出生日期。(匪徒被限制只允许>=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.

我知道这不是最漂亮的解决方案,但它似乎对我来说是一个月,一整年。

#11


1  

Days: (endDate - startDate).Days
Weeks: (endDate - startDate).Days / 7
Years: Months / 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. 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.

天:(endDate - startDate可以)。日周:(开始)。天数/ 7年:月/ 12个月:TimeSpan只提供天数,因此使用下面的代码获取从指定的开始日期到结束日期的整个月数。例如,在01/10/2000和02/10/2000之间的整个月数是1。从01/10/2000到02/09/2000的整个月数为0。

    public int getMonths(DateTime startDate, DateTime endDate)
    {
        int months = 0;

        if (endDate.Month <= startDate.Month)
        {
            if (endDate.Day < startDate.Day)
            {
                months = (12 * (endDate.Year - startDate.Year - 1))
                       + (12 - startDate.Month + endDate.Month - 1);
            }
            else if (endDate.Month < startDate.Month)
            {
                months = (12 * (endDate.Year - startDate.Year - 1))
                       + (12 - startDate.Month + endDate.Month);
            }
            else  // (endDate.Month == startDate.Month) && (endDate.Day >= startDate.Day)
            {
                months = (12 * (endDate.Year - startDate.Year));
            }
        }
        else if (endDate.Day < startDate.Day)
        {
            months = (12 * (endDate.Year - startDate.Year))
                   + (endDate.Month - startDate.Month) - 1;
        }
        else  // (endDate.Month > startDate.Month) && (endDate.Day >= startDate.Day)
        {
            months = (12 * (endDate.Year - startDate.Year))
                   + (endDate.Month - startDate.Month);
        }

        return months;
    }

#12


1  

If you have to find the difference between originalDate and today’s date, Here is a reliable algorithm without so many condition checks.

如果你要找出原始日期和今天日期之间的区别,这里有一个可靠的算法,没有那么多的条件检查。

  1. Declare a intermediateDate variable and initialize to the originalDate
  2. 声明一个中间变量并初始化到原始日期
  3. Find difference between years.(yearDiff)
  4. 找到区别年。(yearDiff)
  5. Add yearDiff to intermediateDate and check whether the value is greater than today’s date.
  6. 将yearDiff添加到中间产品,并检查其值是否大于今天的日期。
  7. If newly obtained intermediateDate > today’s date adjust the yearDiff and intermediateDate by one.
  8. 如果新获得的中间体>今天的日期调整一岁和中间体一个。
  9. Continue above steps for month and Days.
  10. 继续以上步骤,持续一个月和几天。

I have used System.Data.Linq functions to do find the year, month and day differences. Please find c# code below

我用System.Data。Linq函数查找年份、月份和日期的差异。请在下面找到c#代码。

        DateTime todaysDate = DateTime.Now;
        DateTime interimDate = originalDate;

        ///Find Year diff
        int yearDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffYear(interimDate, todaysDate);
        interimDate = interimDate.AddYears(yearDiff);
        if (interimDate > todaysDate)
        {
            yearDiff -= 1;
            interimDate = interimDate.AddYears(-1);
        }

        ///Find Month diff
        int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(interimDate, todaysDate);
        interimDate = interimDate.AddMonths(monthDiff);
        if (interimDate > todaysDate)
        {
            monthDiff -= 1;
            interimDate = interimDate.AddMonths(-1);
        }

        ///Find Day diff
        int daysDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffDay(interimDate, todaysDate);

#13


1  

private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{

        int gyear = dateTimePicker1.Value.Year; 
        int gmonth = dateTimePicker1.Value.Month; 
        int gday = dateTimePicker1.Value.Day; 
        int syear = DateTime.Now.Year; 
        int smonth = DateTime.Now.Month; 
        int sday = DateTime.Now.Day;

        int difday = DateTime.DaysInMonth(syear, gmonth);

        agedisplay = (syear - gyear); 

        lmonth = (smonth - gmonth);
        lday = (sday - gday);


        if (smonth < gmonth)
        {
            agedisplay = agedisplay - 1;
        }
        if (smonth == gmonth)
        {
            if (sday < (gday))
            {
                agedisplay = agedisplay - 1;
            }
        }

        if (smonth < gmonth)
        {
            lmonth = (-(-smonth)+(-gmonth)+12);
        }
        if (lday < 0)
        {
            lday = difday - (-lday);
            lmonth = lmonth - 1;
        }

        if (smonth == gmonth && sday < gday&&gyear!=syear)
        {
            lmonth = 11;
        }

            ageDisplay.Text = Convert.ToString(agedisplay) + " Years,  " + lmonth + " Months,  " + lday + " Days.";

    }

#14


1  

Use Noda Time:

野田佳彦使用时间:

var ld1 = new LocalDate(2012, 1, 1);
var ld2 = new LocalDate(2013, 12, 25);
var period = Period.Between(ld1, ld2);

Debug.WriteLine(period);        // "P1Y11M24D"  (ISO8601 format)
Debug.WriteLine(period.Years);  // 1
Debug.WriteLine(period.Months); // 11
Debug.WriteLine(period.Days);   // 24

#15


1  

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.Month - 1, the month 0 doesn't exist

日期。1年,因为0年不存在。日期。月- 1,月0不存在。

#16


1  

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

#17


0  

Use the Subtract method of the DateTime object which returns a TimeSpan...

使用DateTime对象的相减方法,它返回一个TimeSpan…

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);

TimeSpan ts = dt1.Subtract(dt2);

Double days = ts.TotalDays;
Double hours = ts.TotalHours;
Double years = ts.TotalDays / 365;

#18


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:

编辑No,它仍然不起作用。这个测试失败:

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

Expected:<24>. Actual:<12>

预期:< 24 >。实际:< 12 >

#19


0  

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

#20


-1  

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

#21


-1  

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

#1


31  

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. Daylight saving anyone?

更让人困惑的是:即使一天也不总是24小时。夏令时吗?

Even further confusion (almost certainly not relevant): even a minute isn't always 60 seconds. Leap seconds are highly confusing...

更让人困惑的是:一分钟也不总是60秒。闰秒非常令人困惑……

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:

编辑:不幸的是,我没有足够的时间来回答这个问题。我建议你首先定义一个表示周期的结构体:

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.

我建议您先实现+运算符,它应该通知差分方法—您应该确保第一个+(句号)。差分(first, second)) =所有first/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. So given a (positive) period, you'd have:

注意,我在这里省略了几周——这个值至少很简单,因为它总是7天。给定一个(正值)周期,你会:

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

(我建议你避免考虑消极时期——确保一切都是积极的。)

#2


17  

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.

部分是准备试图回答这个问题正确(甚至明确…),部分原因是为了检查多少一个可以信任的代码粘贴,和部分作为练习发现bug,我创建了一个群单元测试这个问题,从这个页面和应用他们许多提出的解决方案和一些重复。

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. 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. (Note that in Jani's case this was my error and his code was actually correct - see Update 4 below)

这些都是在他们的代码中提供了三年、几个月和几天的所有答案。请注意,其中的两个,Dave和Jani,给出了天数和月份的总数,而不是数了数年后剩下的几个月的总数,以及数个月后剩下的天数。我认为对于OP的需求来说,答案是错误的,但是单元测试显然在这些情况下不会告诉你太多。(注意,在贾尼的案例中,这是我的错误,他的代码实际上是正确的——参见下面的更新4)

The answers by Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir, just i saw, Chalkey and Andy, were incomplete. 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 DateTimes and returning 3 ints that I could properly test. Four of these do however talk about using TimeSpan. As many people have mentioned, TimeSpan doesn't return counts of anything larger than days.

Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir,我看到的,Chalkey和Andy的回答是不完整的。这并不意味着答案是不好的,事实上,其中有几个是对解决方案有用的贡献。它只是意味着没有代码花费两个日期时间并返回3个我可以正确测试的ints。其中有4个是关于使用TimeSpan的。正如许多人提到的,TimeSpan不返回任何大于天数的计数。

The other answers I tested were from

我测试的其他答案来自

  • question 3054715 - LukeH, ho1 and this. ___curious_geek
  • 问题3054715 -卢克,ho1和这个。___curious_geek
  • question 6260372 - Chuck Rostance and Jani (same answer as this question)
  • 问题6260372 - Chuck Rostance和Jani(与此问题相同)
  • question 9 (!) - Dylan Hayes, Jon and Rajeshwaran S P
  • 问题9(!)迪伦·海耶斯,琼恩和拉杰什瓦兰的P

this.___curious_geek's answer is code on a page he linked to, which I don't think he wrote. 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. If anyone finds further duplicates of this question please let me know.

所有这些问题的答案似乎都是不完整的。问题9是关于年岁的问题,这三个答案都超过了简单计算的年份、月份和天数。如果有人发现这个问题的更多副本,请告诉我。

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.

其余的都是聪明的。

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 Asserts, CheckResults() and 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.

执行所有断言的函数有两个:CheckResults()和checkalresults()。这些方法可以很好地保存输入并给出正确的结果,但不幸的是,它们使我们很难确切地看到哪里出错了(因为checkdispute results()中的Assert将在“Expected true”中失败,而不是告诉您哪个值不正确。如果有人有更好的方法(避免每次都写同样的检查,但是有更多有用的错误信息),请告诉我。

CheckControversialResults() is used for a couple of cases where there seem to be two different opinions on what is right. 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.

checkcontroalresults()用于两种情况,在这些情况下,似乎对什么是正确的有两种不同的观点。我有自己的看法,但我认为我在这里接受的应该是*的。这其中的要点是决定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 DateTimes are at midnight. 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. Bear in mind these tests were probably written specifically to address flaws in those answers.

三个答案都没有通过,只有一个测试,Jon,卢克和Manu MN的。请记住,这些测试可能是专门为解决这些问题的缺陷而编写的。

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

    日历是很难的。我写了9个测试,包括3个可能有两个答案的测试。我只有一个答案的一些测试可能不会得到一致的同意。当我们说“1个月后”或“2年之前”的时候,在很多情况下都很棘手。这些代码都不需要处理所有复杂的事情,比如计算闰年是什么时候。所有这些都使用库代码来处理日期。如果你想象一下在几天、几周、几个月甚至几年的时间里说出的“规范”,就会发现各种各样的问题。因为我们从小就很了解它,并且每天都在使用它,所以我们对很多特质视而不见。这个问题不是一个学术问题——在债券和其他金融产品的会计软件中,不同类型的时间间隔分解为年、季度和月份是必不可少的。

  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. 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!)

    编写正确的代码很困难。有很多bug。在稍微模糊的话题或不太流行的问题中,一个bug不被评论人指出的可能性要比这个问题高得多。你真的应该永远,永远不要把代码从SO拷贝到你的程序中而不知道它到底是做什么的。它的另一面是,您可能不应该在您的答案中编写可以复制和粘贴的代码,而应该编写智能的、有表现力的伪代码,这些伪代码允许某人理解解决方案并实现自己的版本(有自己的错误!)

  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. Feel free to add either more tests to break this and other implementations or better code for answering the question.

整个项目现在位于https://github.com/jwg4/date-difference包括我自己的尝试jwg。cs,通过了我目前所有的测试,包括一些新的测试,它们检查了正确的一天处理时间。可以添加更多的测试来打破这个和其他的实现,也可以添加更好的代码来回答这个问题。

Update 2

@MattJohnson has added an implementation which uses Jon Skeet's NodaTime. It passes all the current tests.

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

Update 3

@KirkWoll's answer to Difference in months between two dates has been added to the project on github. It passes all the current tests.

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

Update 4

@Jani pointed out in a comment that I had used his code wrongly. 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. Two use libraries (Intenso.TimePeriod and NodaTime) and two are written from scratch.

@Jani在评论中指出,我错误地使用了他的代码。他的确提出了正确计算年份、月份和天数的方法(有些方法只计算天数和月份,而不是剩余的天数),但我在测试代码中错误地使用了错误的方法。我已经在他的代码周围纠正了我的包装,现在它通过了所有的测试。现在有四个正确的解决方案,其中贾尼是第一个。两个用库(Intenso。时间周期和节点)和两个是从零开始写的。

#3


16  

For the correct difference calculation of Years/Months/Weeks, the Calendar of the CultureInfo must be considered:

对于正确计算年份/月/周的差异,必须考虑文化信息的日历:

  • leap vs. non-leap years
  • 飞跃与non-leap年
  • 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还计算了季度差异。

#4


15  

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

#5


10  

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静态方法。

#6


4  

Subtract two DateTime instances to give you a TimeSpan which has a Days property. (E.g. in PowerShell):

减去两个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

Converting days into years or weeks is relatively easy (days in a year could be 365, 365.25, ... depending on context). Months is much harder, because without a base date you don't know which month lengths apply.

将一天转换成年或周是相对容易的(一年中的天数可以是365天,365.25天,……)根据上下文)。月要难得多,因为没有基准日期,你不知道哪个月的长度适用。

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

假设你想从你的基本日期开始,你可以在计算最初年份(检查闰年)、月份长度(索引从起始日期开始。月)、周(剩余天数除以7)和天(剩余天数)的同时,递增地细分。

There are a lot of edge cases to consider, e.g. 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年取决于你所说的“年”是什么意思。

#7


3  

If you subtract two instances of DateTime, that will return an instance of TimeSpan, which will represent the difference between the two dates.

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

#8


3  

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#. 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...

嗯,@Jon Skeet,如果我们不担心变得比天数更细(仍然将天数滚动成更大的单位,而不是有一天的总数),就像在OP中那样,c#真的没有那么难。使日期计算如此困难的是,每个复合单元的单元数经常发生变化。想象一下,如果每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. 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. 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.

幸运的是,日期只是通过最大整数函数的一段很长的路程。这些疯狂的例外是令人抓狂的,除非你已经通过了组成古怪的单元,而这已经不是什么大事了。如果你出生在1900年12月25日,你在2000年12月25日仍然是100岁,不管你经历过闰年、秒还是白天储蓄。一旦你在组成最后一个复合单元的百分比上做了手脚,你就回到了统一。你已经添加了一个,然后重新开始。

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

(如果你现在的月比以前的月小,那就没有问题了。)每年有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.)

(编辑:把每月的>月份的月份改为>= OldMonth,因为如果月份是相同的,我们不需要借1个月)。也就是说,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. ;^D I think I'm close now.)

(编辑:当我们需要借几天的时间去减去一个dteThen时,必须检查月=月。从dteNow天。天& dteNow。天< dteThen。天,因为我们要减去一年才能得到11个月和额外的天数。好,有一些离群值。,^ D我认为我现在关闭。)

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.

}

#9


2  

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);

int diffMonth = Math.Abs((dt2.Year - dt1.Year)*12 + dt1.Month - dt2.Month)

#10


2  

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. When my form was passed a birth date from a month with less than 31 days, the value calculated was 1 day off. I based my solution off of Ic's answer above.

我在寻找解决类似问题的方法时遇到了这篇文章。我试着以年、月、周、日为单位寻找动物的年龄。然后这些值显示在spinedit中,用户可以手动更改这些值以查找/估计出生日期。当我的表格通过一个月以内的出生日期时,计算的值是1天的假。我根据上面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://*.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. (SpinEdits are constrained to only allow >=0)

然后,在spinEdit_EditValueChanged事件处理程序中,我根据spin edits中的值从startDateforCalc开始计算新的出生日期。(匪徒被限制只允许>=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.

我知道这不是最漂亮的解决方案,但它似乎对我来说是一个月,一整年。

#11


1  

Days: (endDate - startDate).Days
Weeks: (endDate - startDate).Days / 7
Years: Months / 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. 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.

天:(endDate - startDate可以)。日周:(开始)。天数/ 7年:月/ 12个月:TimeSpan只提供天数,因此使用下面的代码获取从指定的开始日期到结束日期的整个月数。例如,在01/10/2000和02/10/2000之间的整个月数是1。从01/10/2000到02/09/2000的整个月数为0。

    public int getMonths(DateTime startDate, DateTime endDate)
    {
        int months = 0;

        if (endDate.Month <= startDate.Month)
        {
            if (endDate.Day < startDate.Day)
            {
                months = (12 * (endDate.Year - startDate.Year - 1))
                       + (12 - startDate.Month + endDate.Month - 1);
            }
            else if (endDate.Month < startDate.Month)
            {
                months = (12 * (endDate.Year - startDate.Year - 1))
                       + (12 - startDate.Month + endDate.Month);
            }
            else  // (endDate.Month == startDate.Month) && (endDate.Day >= startDate.Day)
            {
                months = (12 * (endDate.Year - startDate.Year));
            }
        }
        else if (endDate.Day < startDate.Day)
        {
            months = (12 * (endDate.Year - startDate.Year))
                   + (endDate.Month - startDate.Month) - 1;
        }
        else  // (endDate.Month > startDate.Month) && (endDate.Day >= startDate.Day)
        {
            months = (12 * (endDate.Year - startDate.Year))
                   + (endDate.Month - startDate.Month);
        }

        return months;
    }

#12


1  

If you have to find the difference between originalDate and today’s date, Here is a reliable algorithm without so many condition checks.

如果你要找出原始日期和今天日期之间的区别,这里有一个可靠的算法,没有那么多的条件检查。

  1. Declare a intermediateDate variable and initialize to the originalDate
  2. 声明一个中间变量并初始化到原始日期
  3. Find difference between years.(yearDiff)
  4. 找到区别年。(yearDiff)
  5. Add yearDiff to intermediateDate and check whether the value is greater than today’s date.
  6. 将yearDiff添加到中间产品,并检查其值是否大于今天的日期。
  7. If newly obtained intermediateDate > today’s date adjust the yearDiff and intermediateDate by one.
  8. 如果新获得的中间体>今天的日期调整一岁和中间体一个。
  9. Continue above steps for month and Days.
  10. 继续以上步骤,持续一个月和几天。

I have used System.Data.Linq functions to do find the year, month and day differences. Please find c# code below

我用System.Data。Linq函数查找年份、月份和日期的差异。请在下面找到c#代码。

        DateTime todaysDate = DateTime.Now;
        DateTime interimDate = originalDate;

        ///Find Year diff
        int yearDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffYear(interimDate, todaysDate);
        interimDate = interimDate.AddYears(yearDiff);
        if (interimDate > todaysDate)
        {
            yearDiff -= 1;
            interimDate = interimDate.AddYears(-1);
        }

        ///Find Month diff
        int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(interimDate, todaysDate);
        interimDate = interimDate.AddMonths(monthDiff);
        if (interimDate > todaysDate)
        {
            monthDiff -= 1;
            interimDate = interimDate.AddMonths(-1);
        }

        ///Find Day diff
        int daysDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffDay(interimDate, todaysDate);

#13


1  

private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{

        int gyear = dateTimePicker1.Value.Year; 
        int gmonth = dateTimePicker1.Value.Month; 
        int gday = dateTimePicker1.Value.Day; 
        int syear = DateTime.Now.Year; 
        int smonth = DateTime.Now.Month; 
        int sday = DateTime.Now.Day;

        int difday = DateTime.DaysInMonth(syear, gmonth);

        agedisplay = (syear - gyear); 

        lmonth = (smonth - gmonth);
        lday = (sday - gday);


        if (smonth < gmonth)
        {
            agedisplay = agedisplay - 1;
        }
        if (smonth == gmonth)
        {
            if (sday < (gday))
            {
                agedisplay = agedisplay - 1;
            }
        }

        if (smonth < gmonth)
        {
            lmonth = (-(-smonth)+(-gmonth)+12);
        }
        if (lday < 0)
        {
            lday = difday - (-lday);
            lmonth = lmonth - 1;
        }

        if (smonth == gmonth && sday < gday&&gyear!=syear)
        {
            lmonth = 11;
        }

            ageDisplay.Text = Convert.ToString(agedisplay) + " Years,  " + lmonth + " Months,  " + lday + " Days.";

    }

#14


1  

Use Noda Time:

野田佳彦使用时间:

var ld1 = new LocalDate(2012, 1, 1);
var ld2 = new LocalDate(2013, 12, 25);
var period = Period.Between(ld1, ld2);

Debug.WriteLine(period);        // "P1Y11M24D"  (ISO8601 format)
Debug.WriteLine(period.Years);  // 1
Debug.WriteLine(period.Months); // 11
Debug.WriteLine(period.Days);   // 24

#15


1  

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.Month - 1, the month 0 doesn't exist

日期。1年,因为0年不存在。日期。月- 1,月0不存在。

#16


1  

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

#17


0  

Use the Subtract method of the DateTime object which returns a TimeSpan...

使用DateTime对象的相减方法,它返回一个TimeSpan…

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);

TimeSpan ts = dt1.Subtract(dt2);

Double days = ts.TotalDays;
Double hours = ts.TotalHours;
Double years = ts.TotalDays / 365;

#18


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:

编辑No,它仍然不起作用。这个测试失败:

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

Expected:<24>. Actual:<12>

预期:< 24 >。实际:< 12 >

#19


0  

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

#20


-1  

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

#21


-1  

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