вычисление разницы в месяцах между двумя датами - PullRequest
116 голосов
/ 06 октября 2009

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

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

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

Ответы [ 24 ]

2 голосов
/ 07 ноября 2012
case TipoIntervalo.Mes:
    retorno = inicio.AddMonths(-fim.Month).Month.ToString();
    break;
case TipoIntervalo.Ano:
    retorno = (inicio.Year - fim.Year).ToString();
    break;
1 голос
/ 17 марта 2011

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

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

1 голос
/ 15 февраля 2012

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

Обратите внимание, что вы можете получить любое количество значений даты / времени, секунд и минут или секунд, минут и дней, где угодно, вплоть до лет (которые будут содержать 6 частей / сегментов). Если вы укажете два верхних и ему больше года, он вернет «1 год и 3 месяца назад» и не вернет остаток, потому что вы запросили два сегмента. если ему всего несколько часов, то он вернет только «2 часа и 1 минуту назад». Конечно, те же правила применяются, если вы укажете 1, 2, 3, 4, 5 или 6 сегментов (максимум равен 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 перенесены с дней до.net, поэтому, пожалуйста, извините, как это может выглядеть устаревшим (все еще работает на 100%, используя Их уже более десяти лет, можно гарантировать, что они не содержат ошибок) ... :). веселит.

<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 
1 голос
/ 17 марта 2011

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

DateTime date1, date2;
int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month
1 голос
/ 08 мая 2011

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

Рассчитать количество месяцев между 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);
0 голосов
/ 08 июня 2017

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

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

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

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 голосов
/ 01 июня 2015

Метод возвращает список, содержащий 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 голосов
/ 06 октября 2009

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

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

...