2009-10-06 5 views
99

В C# /. NET TimeSpan имеет TotalDays, TotalMinutes и т. Д., Но я не могу найти формулу для общей разницы месяцев. Меняются переменные дни в месяц и високосные годы. Как я могу получить TotalMonths?расчет разницы в месяцах между двумя датами

Редактировать Извините за не более ясно: я знаю, что я не могу на самом деле получить это от TimeSpan, но я думал, что с помощью TotalDays и TotalMinutes бы хороший пример, чтобы выразить то, что я искал ... кроме того я m пытается получить Total Months.

Пример: 25 декабря 2009 г. - 6 октября 2009 г. = 2 ВсегоМонты. 6 октября по 5 ноября составляет 0 месяцев. 6 ноября, 1 месяц. 6 декабря, 2 месяца

+2

Что вы ожидаете на 25 декабря 2009 - 6 окт 2009? –

+2

Как вы определяете TimeSpan в месяцах? – Aliostad

+1

@Aliostad - Без дат вы можете определить месяц как 30 дней и быть довольно точным. – ChaosPandion

ответ

185

Вы не сможете получить это с помощью TimeSpan, так как «месяц» является переменной единицей измерения. Вам придется рассчитать его сами, и вам придется выяснить, как именно вы хотите, чтобы он работал.

Например, должны ли даты, подобные July 5, 2009, и August 4, 2009 получить разницу в один месяц или ноль месяцев? Если вы скажете, что он должен дать один, то как насчет July 31, 2009 и August 1, 2009? Есть что в месяц? Разве это просто разница значений Month для дат, или это больше связано с фактическим периодом времени? Логика определения всех этих правил является нетривиальной, поэтому вам нужно будет определить свой собственный и реализовать соответствующий алгоритм.

Если все, что вы хотите, это просто разница в месяцах - совершенно не обращая внимания на значение даты - то вы можете использовать это:

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

Обратите внимание, что это возвращает относительную разницу, а это означает, что если rValue является больше lValue, тогда возвращаемое значение будет отрицательным. Если вы хотите, абсолютное значение, вы можете использовать это:

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

@ Dinah это просто приближение, если вы хотите узнать истинные .Month и .Years - я только что опубликовал ответ для того, что вы можете прочитать. Хотя, насколько это возможно, это хорошее приближение (реквизит Адама Робинсона), однако вы должны иметь в виду, что если вы используете какое-либо из этих приближений, вы просто непреднамеренно лжете своим пользователям. –

+0

@ Erx_VB.NExT.Coder: Спасибо за реквизиты, но пока ваш ответ гласит, что ни один из ответов не учитывает тот факт, что месяц является переменной единицей измерения, кажется, что большинство из них это делают; они просто не используют ваше конкретное приближение. В самом деле, самое первое предложение в моем ответе указывает, что оно переменное. Любой ответ, включая ваш *, является приближением *, просто потому, что это не точный ответ. Результат «2 месяца» может означать разные вещи для разных входов, поэтому это приближение. –

+0

шахта - это не приближение, хотя, если сегодня это 14 марта, то два прошедших месяца рассчитываются на основании того, что у него было 31 день, а в нем 29 дней. теперь вы правы в том, что мой метод не является определением «общего» месяца, а ваш! Тем не менее, моя применима только в том случае, если вы сообщаете о таких вещах, как «Этот комментарий был опубликован x месяцев и лет AG», часть «AGO» делает разницу, потому что, ссылаясь на предыдущие x месяцев, эти предыдущие x месяцев должны быть рассчитаны в зависимости от того, сколько дней было в эти х месяцев! link .... –

8

Вы должны определить, что вы имеете в виду TotalMonths, чтобы начать с.
Простое определение помещает месяц в 30.4 дней (365.25/12).

Помимо этого любое определение, включающее фракции, кажется бесполезным, а более общее целочисленное значение (целые месяцы между датами) также зависит от нестандартных бизнес-правил.

0

Если вам нужен точный номер, вы не можете использовать только Timespan, так как вам нужно знать, в какие месяцы вы имеете дело, и имеете ли вы дело с високосным годом, как вы сказали.

Либо пойти на приблизительное количество, или сделать некоторые fidgetting с оригинальным DateTimes

3

Я хотел бы сделать это следующим образом:

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

Это, безусловно, один алгоритм, но он может быть значительно упрощен для возврата (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year); ' –

+1

Две проблемы: вы начинаете с 2 дат, а не TimeSpan. Во-вторых, вы рассчитываете между 1-м из двух месяцев, что является очень сомнительным определением. Хотя иногда это может быть правильно. –

+0

@ Хенк: Да, конечно, это не всегда так, поэтому я сказал, что так и буду делать, а не как это делать. ОП не указывал, как должен быть рассчитан результат. @Adam: Ого, я думал, что слишком сложно снова ... это случается слишком часто для меня. Спасибо за комментарий, вы, очевидно, правы, ваша версия намного лучше. Я буду использовать это с этого момента. –

8

Вы должны работать это себя от в DateTimes. Как вы справляетесь со временем окончания в конце будет зависеть от того, что вы хотите использовать его.

Одним из способов было бы считать месяц, а затем исправить дни в конце.Что-то вроде:

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; 
+0

хороший код..А вся идея, как получить дни тоже? – GorillaApe

+0

Хороший код, хотя, 1 ошибка: вместо: (как 28 февраля + 1 месяц == 28 марта) :-) // decimal daysInEndMonth = (end-end.AddMonths (1)). Days; Я предлагаю: decimal daysInEndMonth = DateTime.DaysInMonth (end.Year, end.Month) * -1; – bezieur

0

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

Если вы можете получить время, преобразованное из даты григорианской в ​​Julian day number, вы можете просто создать оператор, чтобы сделать сравнение числа Зульского дня, который может быть типом двойного получить месяцы, дни, секунды и т. д. Проверьте приведенную выше ссылку на алгоритм преобразования от григорианского языка к Джулиану.

23

Возможно, вы не хотите знать о месяцах; Как насчет этого кода?

 

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" 

 
+1

Я не понимаю * 100. Должно ли это быть * 12? – Ruffles

+0

вы абсолютно правы; fixed, ty –

1

Проблема с месяцами заключается в том, что это не совсем простая мера - они не являются постоянным размером. Вам нужно будет определить свои правила для того, что вы хотите включить, и работать оттуда. Например, с 1 января по 1 февраля - вы можете утверждать, что там участвуют 2 месяца, или вы можете сказать, что это один месяц. Тогда как насчет «1 января 20:00» до «1 февраля 00:00» - это не совсем полный месяц. Это 0? 1? как насчет другого пути (1 января 00:00 до 1 февраля 20:00) ... 1? 2?

Сначала определяют правила, то вы будете иметь код это самостоятельно, я боюсь ...

1

Если вы хотите иметь результат 1 между 28th Feb и 1st March:

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

Это похоже на ту же логику, которая используется функцией Sql Server DateDiff (месяц, ...). Это также имеет то преимущество, что оно очень краткие и легкие для объяснения и понимания. Я бы объяснил это следующим образом: сколько страниц в календаре вам нужно было бы перейти, чтобы перейти от одной даты к другой? – JoelFan

0

Нет встроенного способа сделать это точно в idiomatic-C#. Есть некоторые обходные пути, такие как this CodeProject example, которые люди закодировали.

1

This библиотека вычисляет разницу месяцев, принимая во внимание все части 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 
0

Если вы имеете дело с месяцами и годами вам нужно что-то, что знает, сколько дней каждый месяц имеет и какие годы являются високосными года.

Введите Gregorian Calendar (и другие виды культуры Calendar).

Хотя календарь не предоставляет методы для прямого вычисления разности между двумя точками во время, у него есть такие методы, как

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

Старый вопрос, который я знаю, но может кому-то помочь. Я использовал принятый ответ @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); 
} 
1

Ниже на самом деле наиболее точным способом вы можете сделать это, так как определение «1 месяц» меняется в зависимости от того, какой месяц она есть, и не другие ответы принять это во внимание! Если вы хотите получить дополнительную информацию о проблеме, которая не встроена в фреймворк, вы можете прочитать это сообщение: A Real Timespan Object With .Years & .Months (однако чтение этого сообщения не обязательно для понимания и использования функции ниже, оно работает на 100%, без присущих неточностей из приближения, которые другие любят использовать, и не стесняйтесь заменить функцию .ReverseIt встроенной функцией .Reverse, которую вы можете иметь в своей структуре (она здесь только для полноты).

Обратите внимание, что вы можете получить любое количество дат/времени, секунд & минут, секунд, минут и дней в любом месте до года (в котором будет 6 частей/сегментов). Если вы укажете два лучших, и вам будет больше года, он вернется «1 год и 3 месяца назад» и не вернет остальных, потому что вы запросили два сегмента. если ему всего несколько часов, тогда он вернется только «2 часа и 1 минута назад». Конечно, те же правила применяются, если вы укажете 1, 2, 3, 4, 5 или 6 segmets (максимум на 6, поскольку секунды, минуты, часы, дни, месяцы, годы составляют только 6 типов). Он также будет исправлять проблемы с грамматикой, такие как «минуты» и «минута», в зависимости от того, будет ли она 1 минута или более, одинаковой для всех типов, и генерируемая строка будет всегда грамматически корректной.

Вот некоторые примеры использования: bAllowSegments определяет, сколько сегментов, чтобы показать ... то есть: если 3, то вернуться строка будет (в качестве примера) ... "3 years, 2 months and 13 days" (не будет включать в себя часы, минуты и секунды, когда вернутся 3 категории времени), если, однако, дата была более новой датой, например, что-то несколько дней назад, указав те же сегменты (3), вместо этого вернет "4 days, 1 hour and 13 minutes ago", поэтому это учитывает все!

если bAllowSegments 2 он вернется "3 years and 2 months" и если 6 (максимальное значение) будет возвращать "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", но напомнить, что это будет NEVER RETURN что-то вроде этого "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago", как он понимает, что нет данных даты в 3 верхних сегментах и ​​игнорируют их, даже если вы укажете 6 сегментов, так что не волнуйтесь :). Конечно, если в нем есть сегмент с 0, он учитывает это при формировании строки и будет отображаться как "3 days and 4 seconds ago" и игнорирует часть «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 

Конечно, вам нужна функция «ReplaceLast», которая принимает строку источника, и аргумент, указывающий, что необходимо заменить, а другой аргумент с указанием того, что вы хотите, чтобы заменить его, и это только заменяет последнее заполнение этой строки ... я включил мой, если у вас его нет или он не хочет его реализовывать, так что вот он, он будет работать «как есть» без каких-либо изменений. Я знаю, что функция reverseit больше не нужна (существует в .net), но функции ReplaceLast и ReverseIt переносятся с дней pre -.net, поэтому, пожалуйста, извините, как это датируется (все еще работает 100% tho, используется 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 
2
case TipoIntervalo.Mes: 
    retorno = inicio.AddMonths(-fim.Month).Month.ToString(); 
    break; 
case TipoIntervalo.Ano: 
    retorno = (inicio.Year - fim.Year).ToString(); 
    break; 
+2

Описание для перехода с кодом было бы полезно и для других читателей. – Boeckm

+0

Да, пожалуйста, добавьте комментарий. – Amar

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

Есть не много четких ответов на это, потому что вы всегда предполагая вещи.

Это решение вычисляет между двумя датами месяцы между предполагая, что вы хотите сохранить день месяца для сравнения, (что означает, что день месяца считается в расчете)

Например, если у вас свидание от 30 января 2012 года, 29 февраля 2012 года будет не месяц, а 01 марта 2013 года.

Он был протестирован довольно тщательно, вероятно, он будет очищать его позже, когда мы его используем, и занимает две даты вместо 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 
} 
6

Я написал очень простой метод расширения на DateTime и DateTimeOffset, чтобы сделать это. Я хотел, чтобы это работало точно так же, как и свойство TotalMonths на TimeSpan: i.e.возвратите отсчет полных месяцев между двумя датами, игнорируя любые частичные месяцы. Поскольку он основан на DateTime.AddMonths(), он уважает разные месяцы и возвращает то, что человек понимает как период месяцев.

(К сожалению, вы не можете реализовать его в качестве метода расширения на TimeSpan, потому что не сохраняют знания о фактических датах, используемых, и в течение нескольких месяцев они важны.)

код и тесты являются available on 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; 
} 

И он проходит все эти блок тестовых данных:

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

Сельский, но лучшее решение. Копии и вставка. Спасибо –

38

(Я понимаю, что это старый вопрос, но ...)

Это относительно больно делать в чистом .NET. Я бы рекомендовал мою собственную Noda Time библиотеку, которая специально разработана для таких вещей, как это:

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

(Есть и другие варианты, например, если вы хотите только количество месяцев, даже через годы, вы бы использовать Period period = Period.Between(start, end, PeriodUnits.Months);)

+0

Я скачал вашу библиотеку, и я скопировал код, который вы написали выше, но я получаю ошибку времени компиляции. Ошибка Оператор '-' не может применяться к операндам типа «NodaTime.LocalDate» и «NodaTime.LocalDate». Я знаю этот пост с 5 лет, изменилось ли какое-то изменение с того времени, и этот код не работает? –

+1

@HakamFostok: Извините - он будет работать, когда выпущен 2.0, но до этого вам нужно использовать 'Period.Between'. Изменили код, чтобы он работал с NodaTime 1.3.1. –

+0

благодарит много, что библиотека NodaTime сделала именно то, что я хочу сделать. Я хотел рассчитать не только месяцы между двумя датами, но и оставшиеся дни, и это то, что NodaTime сделала точно, спасибо снова. –

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

возвращает 1 месяц (ов) и 29 дней. Конечно, это зависит от того, сколько месяцев составляет 30 дней .. но это довольно близко к правильному значению дней и месяцев между датами.

+0

Цель состоит в том, чтобы найти правильное значение, а не закрытое. – D4rkTiger

0

Метод возвращает список, содержащий 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; 
     } 
    } 
0

Вот мой вклад, чтобы получить разницу в месяцах, что я нашел, чтобы быть точным :

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

Использование:

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

Вы можете создать еще один метод, называемый DiffYears и применять те же логики, что и выше, и AddYears вместо AddMonths в цикле while.

0

Пройти поздно, но я думаю, это может быть полезно кому-то. Большинство людей, как правило, измеряют месяц за месяцем по дате, исключая тот факт, что месяцы приходят в разных вариантах. Используя эту мысль, я создал один лайнер, который сравнивает даты для нас. Используя следующий процесс.

  1. Любое число лет старше 1 года при сравнении года умножается на 12, нет случаев, когда это может быть равно менее 1 года.
  2. Если конечный год больше, нам нужно оценить, больше ли текущий день или равен предыдущему дню 2A. Если конечный день больше или равен, мы берем текущий месяц, а затем добавляем 12 месяцев, вычитаем месяц начала месяца 2B. Если конечный день меньше начального дня, мы выполняем то же самое, что и выше, за исключением того, что добавим 1 к стартовому месяцу до вычитания
  3. Если конечный год не больше, мы выполняем то же самое, что и 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))); 
    
+0

Смерть тройной? – SpaceBison

 Смежные вопросы

  • Нет связанных вопросов^_^