计算两个日期之间的月差。

时间:2022-08-03 21:31:47

In C#/.NET TimeSpan has TotalDays, TotalMinutes, etc. but I can't figure out a formula for total months difference. Variable days per month and leap years keep throwing me off. How can I get TotalMonths?

在c# /。NET TimeSpan有总天数,总分钟等等,但是我不能算出总月差的公式。每个月的可变天数和闰年一直让我感到困惑。我怎么才能得到完整的月份呢?

Edit Sorry for not being more clear: I know I can't actually get this from TimeSpan but I thought using TotalDays and TotalMinutes would be a good example to express what I was looking for ... except I'm trying to get Total Months.

不好意思,我没说清楚:我知道我无法从TimeSpan得到这个,但是我认为使用TotalDays和TotalMinutes是一个很好的例子来表达我想要的……但是我想要得到所有的月数。

Example: Dec 25, 2009 - Oct 6, 2009 = 2 TotalMonths. Oct 6th to Nov 5th equals 0 months. On Nov 6th, 1 month. On Dec 6th, 2 months

例:2009年12月25日- 2009年10月6日= 2个月。10月6日至11月5日为0个月。11月6日,1个月。12月6日,2个月

23 个解决方案

#1


188  

You won't be able to get that from a TimeSpan, because a "month" is a variable unit of measure. You'll have to calculate it yourself, and you'll have to figure out how exactly you want it to work.

你无法从TimeSpan中得到,因为“月”是一个可变的度量单位。你必须自己计算它,你必须计算出你想要它如何工作。

For example, should dates like July 5, 2009 and August 4, 2009 yield one month or zero months difference? If you say it should yield one, then what about July 31, 2009 and August 1, 2009? Is that a month? Is it simply the difference of the Month values for the dates, or is it more related to an actual span of time? The logic for determining all of these rules is non-trivial, so you'll have to determine your own and implement the appropriate algorithm.

例如,像2009年7月5日和2009年8月4日这样的日期应该是一个月还是零个月?如果你说它会产生一个,那么2009年7月31日和2009年8月1日呢?那是一个月吗?它仅仅是日期的月值差异,还是与实际的时间跨度有关?确定所有这些规则的逻辑是非常重要的,因此您必须确定自己的规则并实现适当的算法。

If all you want is simply a difference in the months--completely disregarding the date values--then you can use this:

如果你想要的只是几个月的不同——完全不考虑日期的价值——那么你可以用这个:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}

Note that this returns a relative difference, meaning that if rValue is greater than lValue, then the return value will be negative. If you want an absolute difference, you can use this:

注意,这将返回一个相对差异,这意味着如果rValue大于lValue,那么返回值将为负。如果你想要绝对的区别,你可以用这个:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}

#2


38  

(I realize this is an old question, but...)

(我知道这是个老问题,但是……)

This is relatively painful to do in pure .NET. I'd recommend my own Noda Time library, which is particularly designed for things like this:

这在纯。net中是比较痛苦的。我推荐我自己的野田时间图书馆,它是专门为这样的事情设计的:

LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;

(There are other options, e.g. if you only want a count of months even across years, you'd use Period period = Period.Between(start, end, PeriodUnits.Months);)

(还有其他选择,例如,如果你只需要数个月甚至数年,你就会使用Period Period = Period。(开始、结束PeriodUnits.Months);之间)

#3


23  

Maybe you don't want to know about month fractions; What about this code?

也许你不想知道月分数;这段代码呢?


public static class DateTimeExtensions
{
    public static int TotalMonths(this DateTime start, DateTime end)
    {
        return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
    }
}

//  Console.WriteLine(
//     DateTime.Now.TotalMonths(
//         DateTime.Now.AddMonths(-1))); // prints "1"


#4


8  

You will have to define what you mean by TotalMonths to start with.
A simple definition puts a month at 30.4 days (365.25 / 12).

你必须从整月开始定义你的意思。一个简单的定义是一个月为30.4天(365.25 / 12)。

Beyond that, any definition including fractions seems useless, and the more common integer value (whole months between dates) also depends on non-standard business rules.

除此之外,任何包含分数的定义似乎都是无用的,更常见的整数值(日期之间的完整月份)也依赖于非标准的业务规则。

#5


8  

You need to work it out yourself off the datetimes. How you deal with the stub days at the end will depend on what you want to use it for.

你需要在约会的时候自己解决这个问题。最后如何处理存根天数将取决于您想要使用它做什么。

One method would be to count month and then correct for days at the end. Something like:

一种方法是计算月份,然后在最后几天进行修正。喜欢的东西:

   DateTime start = new DateTime(2003, 12, 25);
   DateTime end = new DateTime(2009, 10, 6);
   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
   double daysInEndMonth = (end - end.AddMonths(1)).Days;
   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

#6


6  

I've written a very simple extension method on DateTime and DateTimeOffset to do this. I wanted it to work exactly like a TotalMonths property on TimeSpan would work: i.e. return the count of complete months between two dates, ignoring any partial months. Because it's based on DateTime.AddMonths() it respects different month lengths and returns what a human would understand as a period of months.

我已经在DateTime和DateTimeOffset上编写了一个非常简单的扩展方法。我希望它能像TimeSpan上的TotalMonths属性一样工作:即在两个日期之间返回完整的月份计数,忽略任何部分的月份。因为它是基于日期时间。

(Unfortunately you can't implement it as an extension method on TimeSpan because that doesn't retain knowledge of the actual dates used, and for months they're important.)

(不幸的是,您不能将它作为TimeSpan的扩展方法来实现,因为它不能记住实际使用的日期,而且几个月来它们很重要。)

The code and tests are both available on GitHub. The code is very simple:

在GitHub上,代码和测试都是可用的。代码非常简单:

public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
    DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
    DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;

    // Start with 1 month's difference and keep incrementing
    // until we overshoot the late date
    int monthsDiff = 1;
    while (earlyDate.AddMonths(monthsDiff) <= lateDate)
    {
        monthsDiff++;
    }

    return monthsDiff - 1;
}

And it passes all these unit test cases:

它通过了所有这些单元测试用例:

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));

#7


3  

I would do it like this:

我会这样做:

static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;

    dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
    dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));

    while (dtOther.Date > dtThis.Date)
    {
        intReturn++;     
        dtThis = dtThis.AddMonths(1);
    }

    return intReturn;
}

#8


2  

case TipoIntervalo.Mes:
    retorno = inicio.AddMonths(-fim.Month).Month.ToString();
    break;
case TipoIntervalo.Ano:
    retorno = (inicio.Year - fim.Year).ToString();
    break;

#9


2  

There are not a lot of clear answers on this because you are always assuming things.

关于这个问题没有很多明确的答案,因为你总是在假设。

This solution calculates between two dates the months between assuming you want to save the day of month for comparison, (meaning that the day of the month is considered in the calculation)

该解决方案计算两个日期之间的月份,假设您要保存月份的日期以便进行比较(这意味着在计算中考虑月份的日期)

Example, if you have a date of 30 Jan 2012, 29 Feb 2012 will not be a month but 01 March 2013 will.

例如,如果你的日期是2012年1月30日,2012年2月29日不是一个月,而是2013年3月1日。

It's been tested pretty thoroughly, probably will clean it up later as we use it, and takes in two dates instead of a Timespan, which is probably better. Hope this helps out anyone else.

它已经经过了非常彻底的测试,以后我们使用它的时候可能会清理它,并且使用两个日期而不是一个Timespan,这可能更好。希望这能帮助其他人。

private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;
    bool sameMonth = false;

    if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
        intReturn--;

    int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
    int daysinMonth = 0; //used to caputre how many days are in the month

    while (dtOther.Date > dtThis.Date) //while Other date is still under the other
    {
        dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
        daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month

        if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
        {
            if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
                dtThis.AddDays(daysinMonth - dtThis.Day);
            else
                dtThis.AddDays(dayOfMonth - dtThis.Day);
        }
        if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
        {
            if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
                intReturn++;
            sameMonth = true; //sets this to cancel out of the normal counting of month
        }
        if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
            intReturn++;
    }
    return intReturn; //return month
}

#10


2  

Old question I know, but might help someone. I've used @Adam accepted answer above, but then checked if the difference is 1 or -1 then check to see if it is a full calendar month's difference. So 21/07/55 and 20/08/55 would not be a full month, but 21/07/55 and 21/07/55 would be.

我知道这个老问题,但可能会对某人有所帮助。我已经使用@Adam接受了上面的答案,然后检查差异是1还是-1,然后检查它是否是一个完整的日历月的差异。21/07/55和20/08/55不是一个完整的月,但是21/07/55和21/07/55应该是。

/// <summary>
/// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
/// </summary>
/// <param name="dateOfBirth">Date of birth user could have amended.</param>
/// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
/// <returns></returns>
public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
{
    DateTime dob, originalDob;
    bool isValid = false;

    if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
    {
        int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));

        switch (diff)
        {
            case 0:
                // We're on the same month, so ok.
                isValid = true;
                break;
            case -1:
                // The month is the previous month, so check if the date makes it a calendar month out.
                isValid = (dob.Day > originalDob.Day);
                break;
            case 1:
                // The month is the next month, so check if the date makes it a calendar month out.
                isValid = (dob.Day < originalDob.Day);
                break;
            default:
                // Either zero or greater than 1 month difference, so not ok.
                isValid = false;
                break;
        }
        if (!isValid)
            return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
    }
    else
    {
        return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
    }
    return Json(true, JsonRequestBehavior.AllowGet);
}

#11


1  

The problem with months is that it isn't really a simple measure - they aren't constant size. You would need to define your rules for what you want to include, and work from there. For example 1 Jan to 1 Feb - you could argue 2 months are involved there, or you could say that is one month. Then what about "1 Jan 20:00" to "1 Feb 00:00" - that isn't quite an entire full month. Is that 0? 1? what about the other way around (1 Jan 00:00 to 1 Feb 20:00)... 1? 2?

几个月来的问题是,这并不是一个简单的衡量标准——它们不是一成不变的。您需要为您想要包含的内容定义规则,并从那里开始工作。比如1月1日到2月1日,你可以说2个月的时间,或者你可以说这是一个月。那么“1月1日20:00”到“2月1日00:00”又如何呢?这是0吗?1 ?另外(1月1日00:00至2月1日20:00)怎么样?1 ?2呢?

First define the rules, then you'll have to code it yourself, I'm afraid...

首先定义规则,然后你必须自己编码,恐怕……

#12


1  

If you want to have a result 1 between 28th Feb and 1st March:

如果你想在2月28日到3月1日之间得到结果1:

DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month

#13


1  

This library calculates the difference of months, considering all parts of DateTime:

考虑到DateTime的所有部分,这个库计算出了月的差异:

// ----------------------------------------------------------------------
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
} // DateDiffSample

#14


1  

Below is actually the most accurate way you can do it, since the definition of "1 Month" changes depending on which month it is, and non of the other answers take this into account! If you want more information about the issue which is not built into the framework, you can read this post: A Real Timespan Object With .Years & .Months (however, reading that post isn't necessary to understand and use the function below, it works 100%, without the inherent inaccuracies of the approximation others love to use - and feel free to replace the .ReverseIt function with the built-in .Reverse function you may have on your framework (it's just here for completeness).

下面是最准确的方法,因为“1个月”的定义会根据月份的不同而变化,其他答案都不会考虑到这一点。如果你想要更多的信息关于这个问题不是内置的框架,您可以阅读这篇文章:一个真正的时间间隔对象不满& .Months(然而,阅读那篇文章是没有必要的理解和使用下面的函数,它是100%,没有固有的不准确近似别人喜欢,随意用内置的.Reverse函数代替.ReverseIt函数你可以在你的框架(这里只是出于完整性的考虑)。

Please note that you can get any number of dates/times accuracy, seconds & minutes, or seconds, minutes and days, anywhere up to years (which would contain 6 parts/segments). If you specify top two and it's over a year old, it will return "1 year and 3 months ago" and won't return the rest because you've requested two segments. if it's only a few hours old, then it will only return "2 hours and 1 minute ago". Of course, same rules apply if you specify 1, 2, 3, 4, 5 or 6 segmets (maxes out at 6 because seconds, minutes, hours, days, months, years only make 6 types). It will also correct grammer issues like "minutes" vs "minute" depending on if it's 1 minute or more, same for all types, and the "string" generated will always be grammatically correct.

请注意,您可以获得任意数量的日期/时间精度,秒和分钟,或秒,分钟和天,直到年(将包含6个部分/段)。如果您指定了top 2,并且它已经存在了一年多,那么它将返回“1年零3个月前”,并且不会返回剩下的部分,因为您已经请求了两个部分。如果它只有几个小时,那么它只会返回“2小时1分钟前”。当然,如果你指定了1、2、3、4、5或6个节段,同样的规则也适用。它还会根据每一分钟或更多的时间来纠正诸如“分钟”和“分钟”之类的语法问题,对所有类型都一样,生成的“字符串”在语法上永远是正确的。

Here are some examples for use: bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)... "3 years, 2 months and 13 days" (won't include hours, minutes and seconds as the top 3 time categories are returned), if however, the date was a newer date, such as something a few days ago, specifying the same segments (3) will return "4 days, 1 hour and 13 minutes ago" instead, so it takes everything into account!

这里有一些使用的示例:ballowsegment可以确定要显示多少段……ie:如果3,那么返回字符串就是(例如)…“3年2个月,13天”(不包括小时、分钟和秒前三次类别返回),但是,如果是一个更新的日期,日期等一些几天前,指定相同的段(3)将返回“4天,1小时13分钟前”相反,它考虑了一切!

if bAllowSegments is 2 it would return "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", but, be reminded that it will NEVER RETURN something like this "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago" as it understands there is no date data in the top 3 segments and ignores them, even if you specify 6 segments, so don't worry :). Of course, if there is a segment with 0 in it, it will take that into account when forming the string, and will display as "3 days and 4 seconds ago" and ignoring the "0 hours" part! Enjoy and please comment if you like.

如果bAllowSegments 2将返回“3年零2个月,如果6(最大值)将返回“3年,2个月13天13小时29分9秒”,但是,被提醒,它永远不会返回这样的“0年,0月,0天,3小时,2分13秒前”,因为它明白没有日期前三段中的数据,忽略了他们,即使你指定6段,所以不要担心:)。当然,如果有一个带0的线段,在形成线段时要考虑到这一点,将显示为“3天4秒前”,忽略“0小时”部分!如果你喜欢请评论。

 Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
  ' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
  ' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
  ' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
  Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
  Dim dtNow = DateTime.Now
  Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)

  rYears = dtNow.Year - dt.Year
  rMonths = dtNow.Month - dt.Month
  If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
  rDays = dtNow.Day - dt.Day
  If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
  rHours = dtNow.Hour - dt.Hour
  If rHours < 0 Then rHours += 24 : rDays -= 1
  rMinutes = dtNow.Minute - dt.Minute
  If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
  rSeconds = dtNow.Second - dt.Second
  If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1

  ' this is the display functionality
  Dim sb As StringBuilder = New StringBuilder()
  Dim iSegmentsAdded As Int16 = 0

  If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1

parseAndReturn:

  ' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
  ' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...

  If sb.ToString = "" Then sb.Append("less than 1 second")

  Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")

 End Function

Of course, you will need a "ReplaceLast" function, which takes a source string, and an argument specifying what needs to be replaced, and another arg specifying what you want to replace it with, and it only replaces the last occurance of that string... i've included my one if you don't have one or dont want to implement it, so here it is, it will work "as is" with no modification needed. I know the reverseit function is no longer needed (exists in .net) but the ReplaceLast and the ReverseIt func are carried over from the pre-.net days, so please excuse how dated it may look (still works 100% tho, been using em for over ten years, can guarante they are bug free)... :). cheers.

当然,您将需要一个“ReplaceLast”函数,该函数接受源字符串,并使用一个参数指定需要替换的内容,并使用另一个arg指定要替换的内容,它只替换该字符串的最后一次出现……如果您没有或不想实现它,我已经包含了我的一个,所以在这里,它将“按原样”工作,不需要修改。我知道reverseit功能不再需要(存在于。net)但ReplaceLast reverseit func进行从-.net之前的日子里,所以请原谅他们看上去如何约会(仍然工作100%,使用em十多年,可以永发错误免费)……:)。欢呼。

<Extension()> _ 
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String 
    ' let empty string arguments run, incase we dont know if we are sending and empty string or not. 
    sReplacable = sReplacable.ReverseIt 
    sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version! 
    Return sReplacable.ReverseIt.ToString 
End Function 

<Extension()> _ 
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String 
    Dim strTempX As String = "", intI As Integer 

    If n > strS.Length Or n = -1 Then n = strS.Length 

    For intI = n To 1 Step -1 
        strTempX = strTempX + Mid(strS, intI, 1) 
    Next intI 

    ReverseIt = strTempX + Right(strS, Len(strS) - n) 

End Function 

#15


0  

If you want the exact number, you can't from just the Timespan, since you need to know which months you're dealing, and whether you're dealing with a leap year, like you said.

如果你想要确切的数字,你不能只看时间,因为你需要知道你在处理哪个月,以及你是否在处理闰年,就像你说的那样。

Either go for an approximate number, or do some fidgetting with the original DateTimes

或者选择一个近似的数字,或者对原始日期进行一些修改

#16


0  

http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html

http://www.astro.uu.nl/ strous / AA / en / reken / juliaansedag.html

If you can get the time converted from a Gregorian Date into Julian day number, you can just create an operator to do comparisons of the zulian day number, which can be type double to get months, days, seconds, etc. Check out the above link for an algorithm for converting from Gregorian to Julian.

如果你能有时间从公历日期转换为儒略日数字,你可以创建一个操作员来做比较的zulian数天,可类型两个月,天,秒,等。看看上面的链接从公历转换到朱利安的算法。

#17


0  

There is no built in way to do this accurately in idiomatic-c#. There are some workarounds, such as this CodeProject example that people have coded though.

在idiomatic-c#中,没有一种方法能够准确地做到这一点。有一些变通方法,比如这个人们编写过代码的CodeProject示例。

#18


0  

If you're dealing with months and years you need something that knows how many days each month has and which years are leap years.

如果你在处理月份和年份,你需要知道每个月有多少天,哪些年份是闰年。

Enter the Gregorian Calendar (and other culture-specific Calendar implementations).

输入公历(以及其他与文化相关的日历实现)。

While Calendar doesn't provide methods to directly calculate the difference between two points in time, it does have methods such as

虽然Calendar并没有提供方法来直接计算两个时间点之间的差异,但是它确实有一些方法。

DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)

#19


0  

DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

#20


0  

The method returns a list that contains 3 element first is year, second is month and end element is day:

方法返回一个包含3个元素的列表,第一个是年份,第二个是月份,最后一个元素是天:

public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
    {
        try
        {
            if (from > to)
                return null;

            var fY = from.Year;
            var fM = from.Month;
            var fD = DateTime.DaysInMonth(fY, fM);

            var tY = to.Year;
            var tM = to.Month;
            var tD = DateTime.DaysInMonth(tY, tM);

            int dY = 0;
            int dM = 0;
            int dD = 0;

            if (fD > tD)
            {
                tM--;

                if (tM <= 0)
                {
                    tY--;
                    tM = 12;
                    tD += DateTime.DaysInMonth(tY, tM);
                }
                else
                {
                    tD += DateTime.DaysInMonth(tY, tM);
                }
            }
            dD = tD - fD;

            if (fM > tM)
            {
                tY--;

                tM += 12;
            }
            dM = tM - fM;

            dY = tY - fY;

            return new List<int>() { dY, dM, dD };
        }
        catch (Exception exception)
        {
            //todo: log exception with parameters in db

            return null;
        }
    }

#21


0  

Here is my contribution to get difference in Months that I've found to be accurate:

以下是我的贡献,在我发现是准确的几个月的差异:

namespace System
{
     public static class DateTimeExtensions
     {
         public static Int32 DiffMonths( this DateTime start, DateTime end )
         {
             Int32 months = 0;
             DateTime tmp = start;

             while ( tmp < end )
             {
                 months++;
                 tmp = tmp.AddMonths( 1 );
             }

             return months;
        }
    }
}

Usage:

用法:

Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );

You can create another method called DiffYears and apply exactly the same logic as above and AddYears instead of AddMonths in the while loop.

您可以创建另一个名为DiffYears的方法,并与上面的逻辑完全相同,并在while循环中使用AddYears而不是AddMonths。

#22


0  

Way late to the game but I imagine this may be helpful to someone. The majority of people tend to measure month to month by date excluding the fact that months come in different variations. Using that frame of thought I created a one liner which compares the dates for us. Using the the following process.

游戏进行得太迟了,但我想这可能对某些人有帮助。大多数人倾向于逐月逐月地测量,而不考虑月份有不同的变化。我用这种思路做了一个简单的线条,为我们比较日期。使用以下流程。

  1. Any # of years over 1 when comparing year will be multiplied by 12, there is no case where this can be equal to less than 1 full year.
  2. 当比较年份时,任何年份都乘以12,没有情况下这可以等于1整年。
  3. If the end year is greater we need to evaluate if the current day is greater or equal to the previous day 2A. If the end day is greater or equal we take the current month and then add 12 months subtract the month of the start month 2B. If the end day is less than the start day we perform the the same as above except we add 1 to the start month before subtracting
  4. 如果结束年份更大,我们需要评估当前日期是否大于或等于前一天2A。如果结束日大于或等于,我们取当前月,然后加上12个月减去开始月2B。如果结束日小于开始日,我们将执行与上面相同的操作,除了在减去之前在开始月增加1
  5. If the end year is not greater we perform the same as 2A/2B, but without adding the 12 months because we do not need to evaluate around the year.

    如果年末不是更大,我们的表现和2A/2B一样,但是没有加上12个月,因为我们不需要在一年左右评估。

        DateTime date = new DateTime(2003, 11, 25);
        DateTime today = new DateTime(2004, 12, 26);
        var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) +  (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));
    

#23


-3  

    DateTime dtEnd = DateTime.Now.AddDays(59);
    DateTime dtBegin = DateTime.Now;
    var diff = (dtEnd-dtBegin).TotalDays;
    lblDateDiff.Text = Math.Floor(diff/30).ToString() + " month('s) and " + (diff%30).ToString() + " days";

returns 1 month('s) and 29 days. Of course it is dependent on months being 30 days.. but it is pretty close to the correct value for days and months between dates.

返回1个月和29天。当然这取决于月份是30天。但在日期之间的几天或几个月,它非常接近正确的值。

#1


188  

You won't be able to get that from a TimeSpan, because a "month" is a variable unit of measure. You'll have to calculate it yourself, and you'll have to figure out how exactly you want it to work.

你无法从TimeSpan中得到,因为“月”是一个可变的度量单位。你必须自己计算它,你必须计算出你想要它如何工作。

For example, should dates like July 5, 2009 and August 4, 2009 yield one month or zero months difference? If you say it should yield one, then what about July 31, 2009 and August 1, 2009? Is that a month? Is it simply the difference of the Month values for the dates, or is it more related to an actual span of time? The logic for determining all of these rules is non-trivial, so you'll have to determine your own and implement the appropriate algorithm.

例如,像2009年7月5日和2009年8月4日这样的日期应该是一个月还是零个月?如果你说它会产生一个,那么2009年7月31日和2009年8月1日呢?那是一个月吗?它仅仅是日期的月值差异,还是与实际的时间跨度有关?确定所有这些规则的逻辑是非常重要的,因此您必须确定自己的规则并实现适当的算法。

If all you want is simply a difference in the months--completely disregarding the date values--then you can use this:

如果你想要的只是几个月的不同——完全不考虑日期的价值——那么你可以用这个:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
}

Note that this returns a relative difference, meaning that if rValue is greater than lValue, then the return value will be negative. If you want an absolute difference, you can use this:

注意,这将返回一个相对差异,这意味着如果rValue大于lValue,那么返回值将为负。如果你想要绝对的区别,你可以用这个:

public static int MonthDifference(this DateTime lValue, DateTime rValue)
{
    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
}

#2


38  

(I realize this is an old question, but...)

(我知道这是个老问题,但是……)

This is relatively painful to do in pure .NET. I'd recommend my own Noda Time library, which is particularly designed for things like this:

这在纯。net中是比较痛苦的。我推荐我自己的野田时间图书馆,它是专门为这样的事情设计的:

LocalDate start = new LocalDate(2009, 10, 6);
LocalDate end = new LocalDate(2009, 12, 25);
Period period = Period.Between(start, end);
int months = period.Months;

(There are other options, e.g. if you only want a count of months even across years, you'd use Period period = Period.Between(start, end, PeriodUnits.Months);)

(还有其他选择,例如,如果你只需要数个月甚至数年,你就会使用Period Period = Period。(开始、结束PeriodUnits.Months);之间)

#3


23  

Maybe you don't want to know about month fractions; What about this code?

也许你不想知道月分数;这段代码呢?


public static class DateTimeExtensions
{
    public static int TotalMonths(this DateTime start, DateTime end)
    {
        return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month);
    }
}

//  Console.WriteLine(
//     DateTime.Now.TotalMonths(
//         DateTime.Now.AddMonths(-1))); // prints "1"


#4


8  

You will have to define what you mean by TotalMonths to start with.
A simple definition puts a month at 30.4 days (365.25 / 12).

你必须从整月开始定义你的意思。一个简单的定义是一个月为30.4天(365.25 / 12)。

Beyond that, any definition including fractions seems useless, and the more common integer value (whole months between dates) also depends on non-standard business rules.

除此之外,任何包含分数的定义似乎都是无用的,更常见的整数值(日期之间的完整月份)也依赖于非标准的业务规则。

#5


8  

You need to work it out yourself off the datetimes. How you deal with the stub days at the end will depend on what you want to use it for.

你需要在约会的时候自己解决这个问题。最后如何处理存根天数将取决于您想要使用它做什么。

One method would be to count month and then correct for days at the end. Something like:

一种方法是计算月份,然后在最后几天进行修正。喜欢的东西:

   DateTime start = new DateTime(2003, 12, 25);
   DateTime end = new DateTime(2009, 10, 6);
   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
   double daysInEndMonth = (end - end.AddMonths(1)).Days;
   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

#6


6  

I've written a very simple extension method on DateTime and DateTimeOffset to do this. I wanted it to work exactly like a TotalMonths property on TimeSpan would work: i.e. return the count of complete months between two dates, ignoring any partial months. Because it's based on DateTime.AddMonths() it respects different month lengths and returns what a human would understand as a period of months.

我已经在DateTime和DateTimeOffset上编写了一个非常简单的扩展方法。我希望它能像TimeSpan上的TotalMonths属性一样工作:即在两个日期之间返回完整的月份计数,忽略任何部分的月份。因为它是基于日期时间。

(Unfortunately you can't implement it as an extension method on TimeSpan because that doesn't retain knowledge of the actual dates used, and for months they're important.)

(不幸的是,您不能将它作为TimeSpan的扩展方法来实现,因为它不能记住实际使用的日期,而且几个月来它们很重要。)

The code and tests are both available on GitHub. The code is very simple:

在GitHub上,代码和测试都是可用的。代码非常简单:

public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
{
    DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
    DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;

    // Start with 1 month's difference and keep incrementing
    // until we overshoot the late date
    int monthsDiff = 1;
    while (earlyDate.AddMonths(monthsDiff) <= lateDate)
    {
        monthsDiff++;
    }

    return monthsDiff - 1;
}

And it passes all these unit test cases:

它通过了所有这些单元测试用例:

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
// 31 Jan to 28 Feb
Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
// Leap year 29 Feb to 29 Mar
Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
// Whole year minus a day
Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
// Whole year
Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
// 29 Feb (leap) to 28 Feb (non-leap)
Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
// 100 years
Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
// Same date
Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
// Past date
Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));

#7


3  

I would do it like this:

我会这样做:

static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;

    dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
    dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));

    while (dtOther.Date > dtThis.Date)
    {
        intReturn++;     
        dtThis = dtThis.AddMonths(1);
    }

    return intReturn;
}

#8


2  

case TipoIntervalo.Mes:
    retorno = inicio.AddMonths(-fim.Month).Month.ToString();
    break;
case TipoIntervalo.Ano:
    retorno = (inicio.Year - fim.Year).ToString();
    break;

#9


2  

There are not a lot of clear answers on this because you are always assuming things.

关于这个问题没有很多明确的答案,因为你总是在假设。

This solution calculates between two dates the months between assuming you want to save the day of month for comparison, (meaning that the day of the month is considered in the calculation)

该解决方案计算两个日期之间的月份,假设您要保存月份的日期以便进行比较(这意味着在计算中考虑月份的日期)

Example, if you have a date of 30 Jan 2012, 29 Feb 2012 will not be a month but 01 March 2013 will.

例如,如果你的日期是2012年1月30日,2012年2月29日不是一个月,而是2013年3月1日。

It's been tested pretty thoroughly, probably will clean it up later as we use it, and takes in two dates instead of a Timespan, which is probably better. Hope this helps out anyone else.

它已经经过了非常彻底的测试,以后我们使用它的时候可能会清理它,并且使用两个日期而不是一个Timespan,这可能更好。希望这能帮助其他人。

private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
{
    int intReturn = 0;
    bool sameMonth = false;

    if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
        intReturn--;

    int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
    int daysinMonth = 0; //used to caputre how many days are in the month

    while (dtOther.Date > dtThis.Date) //while Other date is still under the other
    {
        dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
        daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month

        if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
        {
            if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
                dtThis.AddDays(daysinMonth - dtThis.Day);
            else
                dtThis.AddDays(dayOfMonth - dtThis.Day);
        }
        if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
        {
            if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
                intReturn++;
            sameMonth = true; //sets this to cancel out of the normal counting of month
        }
        if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
            intReturn++;
    }
    return intReturn; //return month
}

#10


2  

Old question I know, but might help someone. I've used @Adam accepted answer above, but then checked if the difference is 1 or -1 then check to see if it is a full calendar month's difference. So 21/07/55 and 20/08/55 would not be a full month, but 21/07/55 and 21/07/55 would be.

我知道这个老问题,但可能会对某人有所帮助。我已经使用@Adam接受了上面的答案,然后检查差异是1还是-1,然后检查它是否是一个完整的日历月的差异。21/07/55和20/08/55不是一个完整的月,但是21/07/55和21/07/55应该是。

/// <summary>
/// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
/// </summary>
/// <param name="dateOfBirth">Date of birth user could have amended.</param>
/// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
/// <returns></returns>
public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
{
    DateTime dob, originalDob;
    bool isValid = false;

    if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
    {
        int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));

        switch (diff)
        {
            case 0:
                // We're on the same month, so ok.
                isValid = true;
                break;
            case -1:
                // The month is the previous month, so check if the date makes it a calendar month out.
                isValid = (dob.Day > originalDob.Day);
                break;
            case 1:
                // The month is the next month, so check if the date makes it a calendar month out.
                isValid = (dob.Day < originalDob.Day);
                break;
            default:
                // Either zero or greater than 1 month difference, so not ok.
                isValid = false;
                break;
        }
        if (!isValid)
            return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
    }
    else
    {
        return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
    }
    return Json(true, JsonRequestBehavior.AllowGet);
}

#11


1  

The problem with months is that it isn't really a simple measure - they aren't constant size. You would need to define your rules for what you want to include, and work from there. For example 1 Jan to 1 Feb - you could argue 2 months are involved there, or you could say that is one month. Then what about "1 Jan 20:00" to "1 Feb 00:00" - that isn't quite an entire full month. Is that 0? 1? what about the other way around (1 Jan 00:00 to 1 Feb 20:00)... 1? 2?

几个月来的问题是,这并不是一个简单的衡量标准——它们不是一成不变的。您需要为您想要包含的内容定义规则,并从那里开始工作。比如1月1日到2月1日,你可以说2个月的时间,或者你可以说这是一个月。那么“1月1日20:00”到“2月1日00:00”又如何呢?这是0吗?1 ?另外(1月1日00:00至2月1日20:00)怎么样?1 ?2呢?

First define the rules, then you'll have to code it yourself, I'm afraid...

首先定义规则,然后你必须自己编码,恐怕……

#12


1  

If you want to have a result 1 between 28th Feb and 1st March:

如果你想在2月28日到3月1日之间得到结果1:

DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month

#13


1  

This library calculates the difference of months, considering all parts of DateTime:

考虑到DateTime的所有部分,这个库计算出了月的差异:

// ----------------------------------------------------------------------
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
} // DateDiffSample

#14


1  

Below is actually the most accurate way you can do it, since the definition of "1 Month" changes depending on which month it is, and non of the other answers take this into account! If you want more information about the issue which is not built into the framework, you can read this post: A Real Timespan Object With .Years & .Months (however, reading that post isn't necessary to understand and use the function below, it works 100%, without the inherent inaccuracies of the approximation others love to use - and feel free to replace the .ReverseIt function with the built-in .Reverse function you may have on your framework (it's just here for completeness).

下面是最准确的方法,因为“1个月”的定义会根据月份的不同而变化,其他答案都不会考虑到这一点。如果你想要更多的信息关于这个问题不是内置的框架,您可以阅读这篇文章:一个真正的时间间隔对象不满& .Months(然而,阅读那篇文章是没有必要的理解和使用下面的函数,它是100%,没有固有的不准确近似别人喜欢,随意用内置的.Reverse函数代替.ReverseIt函数你可以在你的框架(这里只是出于完整性的考虑)。

Please note that you can get any number of dates/times accuracy, seconds & minutes, or seconds, minutes and days, anywhere up to years (which would contain 6 parts/segments). If you specify top two and it's over a year old, it will return "1 year and 3 months ago" and won't return the rest because you've requested two segments. if it's only a few hours old, then it will only return "2 hours and 1 minute ago". Of course, same rules apply if you specify 1, 2, 3, 4, 5 or 6 segmets (maxes out at 6 because seconds, minutes, hours, days, months, years only make 6 types). It will also correct grammer issues like "minutes" vs "minute" depending on if it's 1 minute or more, same for all types, and the "string" generated will always be grammatically correct.

请注意,您可以获得任意数量的日期/时间精度,秒和分钟,或秒,分钟和天,直到年(将包含6个部分/段)。如果您指定了top 2,并且它已经存在了一年多,那么它将返回“1年零3个月前”,并且不会返回剩下的部分,因为您已经请求了两个部分。如果它只有几个小时,那么它只会返回“2小时1分钟前”。当然,如果你指定了1、2、3、4、5或6个节段,同样的规则也适用。它还会根据每一分钟或更多的时间来纠正诸如“分钟”和“分钟”之类的语法问题,对所有类型都一样,生成的“字符串”在语法上永远是正确的。

Here are some examples for use: bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)... "3 years, 2 months and 13 days" (won't include hours, minutes and seconds as the top 3 time categories are returned), if however, the date was a newer date, such as something a few days ago, specifying the same segments (3) will return "4 days, 1 hour and 13 minutes ago" instead, so it takes everything into account!

这里有一些使用的示例:ballowsegment可以确定要显示多少段……ie:如果3,那么返回字符串就是(例如)…“3年2个月,13天”(不包括小时、分钟和秒前三次类别返回),但是,如果是一个更新的日期,日期等一些几天前,指定相同的段(3)将返回“4天,1小时13分钟前”相反,它考虑了一切!

if bAllowSegments is 2 it would return "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", but, be reminded that it will NEVER RETURN something like this "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago" as it understands there is no date data in the top 3 segments and ignores them, even if you specify 6 segments, so don't worry :). Of course, if there is a segment with 0 in it, it will take that into account when forming the string, and will display as "3 days and 4 seconds ago" and ignoring the "0 hours" part! Enjoy and please comment if you like.

如果bAllowSegments 2将返回“3年零2个月,如果6(最大值)将返回“3年,2个月13天13小时29分9秒”,但是,被提醒,它永远不会返回这样的“0年,0月,0天,3小时,2分13秒前”,因为它明白没有日期前三段中的数据,忽略了他们,即使你指定6段,所以不要担心:)。当然,如果有一个带0的线段,在形成线段时要考虑到这一点,将显示为“3天4秒前”,忽略“0小时”部分!如果你喜欢请评论。

 Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
  ' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
  ' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
  ' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
  Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
  Dim dtNow = DateTime.Now
  Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)

  rYears = dtNow.Year - dt.Year
  rMonths = dtNow.Month - dt.Month
  If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
  rDays = dtNow.Day - dt.Day
  If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
  rHours = dtNow.Hour - dt.Hour
  If rHours < 0 Then rHours += 24 : rDays -= 1
  rMinutes = dtNow.Minute - dt.Minute
  If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
  rSeconds = dtNow.Second - dt.Second
  If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1

  ' this is the display functionality
  Dim sb As StringBuilder = New StringBuilder()
  Dim iSegmentsAdded As Int16 = 0

  If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
  If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn

  If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1

parseAndReturn:

  ' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
  ' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...

  If sb.ToString = "" Then sb.Append("less than 1 second")

  Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")

 End Function

Of course, you will need a "ReplaceLast" function, which takes a source string, and an argument specifying what needs to be replaced, and another arg specifying what you want to replace it with, and it only replaces the last occurance of that string... i've included my one if you don't have one or dont want to implement it, so here it is, it will work "as is" with no modification needed. I know the reverseit function is no longer needed (exists in .net) but the ReplaceLast and the ReverseIt func are carried over from the pre-.net days, so please excuse how dated it may look (still works 100% tho, been using em for over ten years, can guarante they are bug free)... :). cheers.

当然,您将需要一个“ReplaceLast”函数,该函数接受源字符串,并使用一个参数指定需要替换的内容,并使用另一个arg指定要替换的内容,它只替换该字符串的最后一次出现……如果您没有或不想实现它,我已经包含了我的一个,所以在这里,它将“按原样”工作,不需要修改。我知道reverseit功能不再需要(存在于。net)但ReplaceLast reverseit func进行从-.net之前的日子里,所以请原谅他们看上去如何约会(仍然工作100%,使用em十多年,可以永发错误免费)……:)。欢呼。

<Extension()> _ 
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String 
    ' let empty string arguments run, incase we dont know if we are sending and empty string or not. 
    sReplacable = sReplacable.ReverseIt 
    sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version! 
    Return sReplacable.ReverseIt.ToString 
End Function 

<Extension()> _ 
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String 
    Dim strTempX As String = "", intI As Integer 

    If n > strS.Length Or n = -1 Then n = strS.Length 

    For intI = n To 1 Step -1 
        strTempX = strTempX + Mid(strS, intI, 1) 
    Next intI 

    ReverseIt = strTempX + Right(strS, Len(strS) - n) 

End Function 

#15


0  

If you want the exact number, you can't from just the Timespan, since you need to know which months you're dealing, and whether you're dealing with a leap year, like you said.

如果你想要确切的数字,你不能只看时间,因为你需要知道你在处理哪个月,以及你是否在处理闰年,就像你说的那样。

Either go for an approximate number, or do some fidgetting with the original DateTimes

或者选择一个近似的数字,或者对原始日期进行一些修改

#16


0  

http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html

http://www.astro.uu.nl/ strous / AA / en / reken / juliaansedag.html

If you can get the time converted from a Gregorian Date into Julian day number, you can just create an operator to do comparisons of the zulian day number, which can be type double to get months, days, seconds, etc. Check out the above link for an algorithm for converting from Gregorian to Julian.

如果你能有时间从公历日期转换为儒略日数字,你可以创建一个操作员来做比较的zulian数天,可类型两个月,天,秒,等。看看上面的链接从公历转换到朱利安的算法。

#17


0  

There is no built in way to do this accurately in idiomatic-c#. There are some workarounds, such as this CodeProject example that people have coded though.

在idiomatic-c#中,没有一种方法能够准确地做到这一点。有一些变通方法,比如这个人们编写过代码的CodeProject示例。

#18


0  

If you're dealing with months and years you need something that knows how many days each month has and which years are leap years.

如果你在处理月份和年份,你需要知道每个月有多少天,哪些年份是闰年。

Enter the Gregorian Calendar (and other culture-specific Calendar implementations).

输入公历(以及其他与文化相关的日历实现)。

While Calendar doesn't provide methods to directly calculate the difference between two points in time, it does have methods such as

虽然Calendar并没有提供方法来直接计算两个时间点之间的差异,但是它确实有一些方法。

DateTime AddWeeks(DateTime time, int weeks)
DateTime AddMonths(DateTime time, int months)
DateTime AddYears(DateTime time, int years)

#19


0  

DateTime start = new DateTime(2003, 12, 25);
DateTime end = new DateTime(2009, 10, 6);
int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
double daysInEndMonth = (end - end.AddMonths(1)).Days;
double months = compMonth + (start.Day - end.Day) / daysInEndMonth;

#20


0  

The method returns a list that contains 3 element first is year, second is month and end element is day:

方法返回一个包含3个元素的列表,第一个是年份,第二个是月份,最后一个元素是天:

public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
    {
        try
        {
            if (from > to)
                return null;

            var fY = from.Year;
            var fM = from.Month;
            var fD = DateTime.DaysInMonth(fY, fM);

            var tY = to.Year;
            var tM = to.Month;
            var tD = DateTime.DaysInMonth(tY, tM);

            int dY = 0;
            int dM = 0;
            int dD = 0;

            if (fD > tD)
            {
                tM--;

                if (tM <= 0)
                {
                    tY--;
                    tM = 12;
                    tD += DateTime.DaysInMonth(tY, tM);
                }
                else
                {
                    tD += DateTime.DaysInMonth(tY, tM);
                }
            }
            dD = tD - fD;

            if (fM > tM)
            {
                tY--;

                tM += 12;
            }
            dM = tM - fM;

            dY = tY - fY;

            return new List<int>() { dY, dM, dD };
        }
        catch (Exception exception)
        {
            //todo: log exception with parameters in db

            return null;
        }
    }

#21


0  

Here is my contribution to get difference in Months that I've found to be accurate:

以下是我的贡献,在我发现是准确的几个月的差异:

namespace System
{
     public static class DateTimeExtensions
     {
         public static Int32 DiffMonths( this DateTime start, DateTime end )
         {
             Int32 months = 0;
             DateTime tmp = start;

             while ( tmp < end )
             {
                 months++;
                 tmp = tmp.AddMonths( 1 );
             }

             return months;
        }
    }
}

Usage:

用法:

Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );

You can create another method called DiffYears and apply exactly the same logic as above and AddYears instead of AddMonths in the while loop.

您可以创建另一个名为DiffYears的方法,并与上面的逻辑完全相同,并在while循环中使用AddYears而不是AddMonths。

#22


0  

Way late to the game but I imagine this may be helpful to someone. The majority of people tend to measure month to month by date excluding the fact that months come in different variations. Using that frame of thought I created a one liner which compares the dates for us. Using the the following process.

游戏进行得太迟了,但我想这可能对某些人有帮助。大多数人倾向于逐月逐月地测量,而不考虑月份有不同的变化。我用这种思路做了一个简单的线条,为我们比较日期。使用以下流程。

  1. Any # of years over 1 when comparing year will be multiplied by 12, there is no case where this can be equal to less than 1 full year.
  2. 当比较年份时,任何年份都乘以12,没有情况下这可以等于1整年。
  3. If the end year is greater we need to evaluate if the current day is greater or equal to the previous day 2A. If the end day is greater or equal we take the current month and then add 12 months subtract the month of the start month 2B. If the end day is less than the start day we perform the the same as above except we add 1 to the start month before subtracting
  4. 如果结束年份更大,我们需要评估当前日期是否大于或等于前一天2A。如果结束日大于或等于,我们取当前月,然后加上12个月减去开始月2B。如果结束日小于开始日,我们将执行与上面相同的操作,除了在减去之前在开始月增加1
  5. If the end year is not greater we perform the same as 2A/2B, but without adding the 12 months because we do not need to evaluate around the year.

    如果年末不是更大,我们的表现和2A/2B一样,但是没有加上12个月,因为我们不需要在一年左右评估。

        DateTime date = new DateTime(2003, 11, 25);
        DateTime today = new DateTime(2004, 12, 26);
        var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) +  (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));
    

#23


-3  

    DateTime dtEnd = DateTime.Now.AddDays(59);
    DateTime dtBegin = DateTime.Now;
    var diff = (dtEnd-dtBegin).TotalDays;
    lblDateDiff.Text = Math.Floor(diff/30).ToString() + " month('s) and " + (diff%30).ToString() + " days";

returns 1 month('s) and 29 days. Of course it is dependent on months being 30 days.. but it is pretty close to the correct value for days and months between dates.

返回1个月和29天。当然这取决于月份是30天。但在日期之间的几天或几个月,它非常接近正确的值。