Расчет разницы в месяцах между двумя датами

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

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

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

Ответ 1

Вы не сможете получить это от 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));
}

Ответ 2

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

Это относительно больно делать в чистом .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);)

Ответ 3

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


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

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

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

Ответ 5

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

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

   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

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

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

Код и тесты являются доступными на 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 difference and keep incrementing
    // until we overshoot the late date
    int monthsDiff = 1;
    while (earlyDate.AddMonths(monthsDiff) <= lateDate)
    {
        monthsDiff++;
    }

    return monthsDiff - 1;
}

И он передает все эти случаи unit test:

// Simple comparison
Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
// Just under 1 month diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
// Just over 1 month 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

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

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

На этот вопрос не так много четких ответов, потому что вы всегда предполагаете что-то.

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

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

Он был довольно тщательно протестирован, возможно, позже будет очищен, когда мы его используем, и использует две даты вместо промежутка времени, что, вероятно, лучше. Надеюсь, это поможет кому-нибудь еще.

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
}

Ответ 9

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

Ответ 10

case IntervalType.Month:
    returnValue = start.AddMonths(-end.Month).Month.ToString();
    break;
case IntervalType.Year:
    returnValue = (start.Year - end.Year).ToString();
    break;

Ответ 11

Принятый ответ отлично работает, когда вы хотите полных месяцев.

Мне понадобились неполные месяцы. Это решение, которое я придумал за неполные месяцы:

    /// <summary>
    /// Calculate the difference in months.
    /// This will round up to count partial months.
    /// </summary>
    /// <param name="lValue"></param>
    /// <param name="rValue"></param>
    /// <returns></returns>
    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
    }

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

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

Ответ 12

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

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

Ответ 13

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

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

Ответ 14

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

Ответ 15

Ниже на самом деле самый точный способ, которым вы можете это сделать, поскольку определение "1 месяц" изменяется в зависимости от того, в каком месяце оно находится, а остальные ответы учитывают это! Если вам нужна дополнительная информация о проблеме, которая не встроена в фреймворк, вы можете прочитать это сообщение: Объект реального времени с .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 

Ответ 16

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

Либо идите для приблизительного числа, либо выполните некоторую фальсификацию с исходными DateTimes

Ответ 17

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

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

Ответ 18

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

Ответ 19

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

Введите Григорианский календарь (и другие культурные Calendar).

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

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

Ответ 20

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;

Ответ 21

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

Ответ 22

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

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.

Ответ 23

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

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

Ответ 24

Рассчитайте количество месяцев между 2 датами:

$date1 = '2017-01-20';
$date2 = '2019-01-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);

Ответ 25

    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 дней.. но это довольно близко к правильному значению дней и месяцев между датами.