calculando la diferencia en meses entre dos fechas

En C # /. NET TimeSpan tiene TotalDays , TotalMinutes , etc. pero no puedo deducir una fórmula para la diferencia de meses totales. Los días variables por mes y los años bisiestos me siguen tirando. ¿Cómo puedo obtener TotalMonths ?

Editar Lo siento por no ser más claro: Sé que no puedo obtener esto de TimeSpan pero pensé que usar TotalDays y TotalMinutes sería un buen ejemplo para express lo que estaba buscando … excepto que estoy tratando de obtener Total Months .

Ejemplo: 25 de diciembre de 2009 – 6 de octubre de 2009 = 2 meses totales. 6 de octubre al 5 de noviembre es igual a 0 meses. El 6 de noviembre, 1 mes. El 6 de diciembre, 2 meses

No podrá obtenerlo de un TimeSpan , porque un “mes” es una unidad de medida variable. Tendrá que calcularlo usted mismo, y tendrá que descubrir cómo exactamente quiere que funcione.

Por ejemplo, ¿las fechas como July 5, 2009 y August 4, 2009 arrojan una diferencia de un mes o cero meses? Si dice que debería producir uno, ¿qué hay July 31, 2009 y August 1, 2009 ? ¿Es eso un mes? ¿Es simplemente la diferencia de los valores del Month para las fechas, o está más relacionado con un lapso de tiempo real? La lógica para determinar todas estas reglas no es trivial, por lo que tendrá que determinar la suya e implementar el algoritmo apropiado.

Si todo lo que quiere es simplemente una diferencia en los meses, sin tener en cuenta los valores de fecha, puede usar esto:

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

Tenga en cuenta que esto devuelve una diferencia relativa, lo que significa que si rValue es mayor que lValue , el valor de retorno será negativo. Si quieres una diferencia absoluta, puedes usar esto:

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

(Me doy cuenta de que esta es una vieja pregunta, pero …)

Esto es relativamente doloroso de hacer en .NET puro. Recomiendo mi propia biblioteca Noda Time , que está especialmente diseñada para cosas como esta:

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

(Hay otras opciones, por ejemplo, si solo desea un recuento de meses, incluso entre años, usaría Period period = Period.Between(start, end, PeriodUnits.Months); )

Tal vez no quieras saber sobre las fracciones del mes; ¿Qué pasa con este código?

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

Para empezar, deberá definir lo que quiere decir con TotalMonths.
Una definición simple pone un mes en 30.4 días (365.25 / 12).

Más allá de eso, cualquier definición que incluya fracciones parece inútil, y el valor entero más común (meses enteros entre fechas) también depende de reglas comerciales no estándar.

Necesita resolverlo usted mismo fuera de los horarios. La forma en que maneje los días de talón al final dependerá de para qué lo quiera usar.

Un método sería contar el mes y luego corregirlo por días al final. Algo como:

  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; 

He escrito un método de extensión muy simple en DateTime y DateTimeOffset para hacer esto. Quería que funcionara exactamente como funcionaría una propiedad de TotalMonths en TimeSpan : es decir, devolver el recuento de meses completos entre dos fechas, ignorando cualquier mes parcial. Debido a que está basado en DateTime.AddMonths() , respeta las diferentes duraciones de los meses y devuelve lo que un humano podría entender como un período de meses.

(Desafortunadamente no puedes implementarlo como un método de extensión en TimeSpan porque eso no retiene el conocimiento de las fechas reales utilizadas, y durante meses son importantes).

El código y las pruebas están disponibles en GitHub . El código es muy simple:

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

Y pasa todos estos casos de prueba de unidad:

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

Lo haría así:

 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; } 
 case TipoIntervalo.Mes: retorno = inicio.AddMonths(-fim.Month).Month.ToString(); break; case TipoIntervalo.Ano: retorno = (inicio.Year - fim.Year).ToString(); break; 

No hay muchas respuestas claras sobre esto porque siempre estás asumiendo cosas.

Esta solución calcula entre dos fechas los meses entre asumir que desea guardar el día del mes para la comparación, (lo que significa que el día del mes se considera en el cálculo)

Por ejemplo, si tiene una fecha del 30 de enero de 2012, el 29 de febrero de 2012 no será un mes sino el 1 de marzo de 2013.

Ha sido probado bastante a fondo, probablemente lo limpiará más adelante cuando lo usemos, y toma dos fechas en lugar de un Timespan, que probablemente sea mejor. Espero que esto ayude a alguien más.

 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 } 

La vieja pregunta que sé, pero podría ayudar a alguien. He usado la respuesta aceptada por @Adam anteriormente, pero luego verifiqué si la diferencia es 1 o -1, luego verifico si se trata de la diferencia de un mes calendario completo. Entonces, el 21/07/55 y el 20/08/55 no serían un mes completo, sino el 21/07/55 y el 21/07/55.

 ///  /// Amended date of birth cannot be greater than or equal to one month either side of original date of birth. ///  /// Date of birth user could have amended. /// Original date of birth to compare against. ///  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); } 

El problema con los meses es que no es realmente una medida simple, no son de tamaño constante. Debería definir sus reglas para lo que desea incluir, y trabajar desde allí. Por ejemplo, del 1 de enero al 1 de febrero, podría argumentar que 2 meses están involucrados allí, o podría decir que es un mes. Entonces, ¿qué pasa con “1 de enero a las 20:00” a “1 de febrero a las 00:00”? Eso no es todo un mes completo. Es eso 0? 1? ¿Qué pasa al revés (1 de enero de 00:00 a 1 de febrero a las 20:00) … 1? 2?

Primero defina las reglas, entonces tendrá que codificarlo usted mismo, me temo …

Si quiere tener un resultado 1 entre el 28th Feb y el 1st March :

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

Esta biblioteca calcula la diferencia de meses, considerando todas las partes de 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 

A continuación, se muestra la forma más precisa de hacerlo, ya que la definición de “1 mes” cambia según el mes en que se encuentre y ninguna de las demás respuestas tiene esto en cuenta. Si desea obtener más información sobre el problema que no está incorporada en el marco, puede leer esta publicación: Un objeto de tiempo real con .Years & .Months (sin embargo, leer esa publicación no es necesario para comprender y utilizar la siguiente función, funciona al 100%, sin las imprecisiones inherentes de la aproximación que a otros les encanta usar, y siéntete libre de reemplazar la función .ReverseIt con la función .Reverse incorporada que puedes tener en tu marco (está aquí para completarse).

Tenga en cuenta que puede obtener cualquier número de fechas / tiempos de precisión, segundos y minutos, o segundos, minutos y días, en cualquier lugar hasta años (que contendría 6 partes / segmentos). Si especifica los dos primeros y tiene más de un año, devolverá “1 año y 3 meses atrás” y no devolverá el rest porque solicitó dos segmentos. si solo tiene unas pocas horas, solo volverá “hace 2 horas y 1 minuto”. Por supuesto, se aplican las mismas reglas si especifica 1, 2, 3, 4, 5 o 6 segmentos (máximo en 6 porque segundos, minutos, horas, días, meses, años solo hacen 6 tipos). También corregirá problemas de gramáticas como “minutos” versus “minutos” dependiendo de si es de 1 minuto o más, igual para todos los tipos, y la “cadena” generada siempre será gtwigticalmente correcta.

Estos son algunos ejemplos de uso: bSellowSegments identifica cuántos segmentos mostrar … es decir: si es 3, entonces la cadena de retorno sería (como ejemplo) … "3 years, 2 months and 13 days" (no incluirá horas, minutos y segundos cuando se devuelven las 3 mejores categorías de tiempo), si la fecha era más reciente, como algo hace unos días, al especificar los mismos segmentos (3) se devolverá "4 days, 1 hour and 13 minutes ago" cambio, ¡así que todo tiene en cuenta!

si bAllowSegments es 2, devolvería "3 years and 2 months" y si 6 (valor máximo) devolvería "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds" , pero recuerde que lo hará NEVER RETURN algo como esto "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago" ya que entiende que no hay datos de fecha en los 3 primeros segmentos y los ignora, incluso si especifica 6 segmentos, por lo no te preocupes :). Por supuesto, si hay un segmento con 0 en él, lo tendrá en cuenta al formar la cuerda, y se mostrará como "3 days and 4 seconds ago" e ignorará la parte “0 horas”. Disfruta y por favor comenta si quieres.

  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 

Por supuesto, necesitará una función “ReemplazarÚltima”, que toma una cadena de origen, y un argumento que especifica lo que necesita ser reemplazado, y otra arg que especifica con qué desea reemplazarla, y solo reemplaza la última ocurrencia de esa cadena … he incluido el mío si no tienes uno o no quieres implementarlo, así que aquí está, funcionará “tal cual” sin necesidad de modificaciones. Sé que la función reverseit ya no es necesaria (existe en .net) pero los funcionamientos ReplaceLast y ReverseIt se transfieren de los días de pre.net, así que disculpe qué tan anticuada puede parecer (todavía funciona al 100%, ha estado usando em por más de diez años, puede garantizar que están libres de errores) … :). aclamaciones.

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

La respuesta aceptada funciona perfectamente cuando quiere meses completos.

Necesitaba meses parciales. Esta es la solución que se me ocurrió durante meses parciales:

  ///  /// Calculate the difference in months. /// This will round up to count partial months. ///  ///  ///  ///  public static int MonthDifference(DateTime lValue, DateTime rValue) { var yearDifferenceInMonths = (lValue.Year - rValue.Year) * 12; var monthDifference = lValue.Month - rValue.Month; return yearDifferenceInMonths + monthDifference + (lValue.Day > rValue.Day ? 1 : 0); // If end day is greater than start day, add 1 to round up the partial month } 

También necesitaba una diferencia de un año con la misma necesidad de años parciales. Aquí está la solución que se me ocurrió:

  ///  /// Calculate the differences in years. /// This will round up to catch partial months. ///  ///  ///  ///  public static int YearDifference(DateTime lValue, DateTime rValue) { return lValue.Year - rValue.Year + (lValue.Month > rValue.Month // Partial month, same year ? 1 : lValue.Day > rValue.Day // Partial month, same year and month ? 1 : 0); } 

Si quiere el número exacto, no puede hacerlo solo desde el Timespan, ya que necesita saber qué meses está tratando y si está lidiando con un año bisiesto, como dijo.

O elija un número aproximado o haga algunos retoques con el DateTimes original.

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

Si puede convertir el tiempo de una fecha gregoriana en un número de día juliano , puede crear un operador para hacer comparaciones del número del día zuliano, que puede ser de tipo doble para obtener meses, días, segundos, etc. Vea lo anterior enlace para un algoritmo para convertir de Gregorian a Julian.

No hay una forma integrada de hacerlo con precisión en idiomatic-c #. Hay algunas soluciones, como este ejemplo de CodeProject que las personas han codificado.

Si está lidiando con meses y años, necesita algo que sepa cuántos días tiene cada mes y qué años son bisiestos.

Ingrese el calendario gregoriano (y otras implementaciones de calendario específicas de cada cultura).

Si bien el calendario no proporciona métodos para calcular directamente la diferencia entre dos puntos en el tiempo, sí tiene métodos como

 DateTime AddWeeks(DateTime time, int weeks) DateTime AddMonths(DateTime time, int months) DateTime AddYears(DateTime time, int years) 
 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; 

El método devuelve una lista que contiene 3 elementos primero es año, segundo es mes y el elemento final es día:

 public static List 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() { dY, dM, dD }; } catch (Exception exception) { //todo: log exception with parameters in db return null; } } 

Aquí está mi contribución para obtener la diferencia en los meses que he encontrado que son precisos:

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

Uso:

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

Puede crear otro método llamado DiffYears y aplicar exactamente la misma lógica que la anterior y AddYears en lugar de AddMonths en el ciclo while.

Hasta tarde en el juego, pero me imagino que esto puede ser útil para alguien. La mayoría de las personas tiende a medir mes a mes por fecha, excluyendo el hecho de que los meses vienen en diferentes variaciones. 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. 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
  3. 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.

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