обчислення різниці в місяцях між двома датами


128

У C # /. NET TimeSpanє TotalDays, TotalMinutesі т.д., але я не можу визначити формулу загальної різниці місяців. Змінні дні на місяць та високосні роки продовжують мене відкидати. Як я можу отримати TotalMonths ?

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

Приклад: 25 грудня 2009 р. - 6 жовтня 2009 р. = 2 Загальні місяці. 6 жовтня по 5 листопада дорівнює 0 місяців. 6 листопада, 1 місяць. 6 грудня, 2 місяці


2
Що ви очікуєте на 25 грудня 2009 р. - 6 жовтня 2009 р.?
Джефф Мозер

2
Як Ви визначаєте TimeSpan у місяцях?
Аліостад

1
@Aliostad - Без дат можна визначити місяць як 30 днів і бути досить точним.
ChaosPandion

З цим питанням мода чомусь була об'єднана модою.
Jamiec

Насправді, вам потрібно прочитати тут мій пост, який відповідає на це запитання і надає закодоване рішення, stackoverflow.com/questions/1916358/… ігноруйте тролів (браріан) та зверніть увагу на мою розмову через коментарі з суперкатом. Місяці, які починаються на початку та в кінці періоду часу, ми називаємо "Сиротами-місяцями", і питання зводиться до того, як визначити ці осиротілі місяці за днями - як тільки ти визначив це (і як ти хочеш це визначити ), решта - лише код (який входить у комплект). Моя деф. ґрунтується на тому, що, на мою думку, очікують мої користувачі
Erx_VB.NExT.Coder

Відповіді:


222

Ви не зможете отримати це з 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));
}

@Dinah це лише наближення, якщо ви хочете дізнатися правду. Місяць і .Years - я щойно опублікував відповідь за те, що ви можете прочитати. Хоча, наскільки це наближення, це хороше наближення (підкріплення Адама Робінсона), проте слід пам’ятати, що якщо ви використовуєте будь-яке з цих наближень, ви ненавмисно брешите своїх користувачів.
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder: Дякую за реквізит, але хоча у вашій відповіді зазначено, що жодна з відповідей не враховує те, що місяць є змінною одиницею вимірювання, здається, що більшість з них це робить; вони просто не використовують ваше конкретне наближення. У конкретному випадку, саме перше речення в моїй відповіді вказує на те, що воно є змінним. Будь-яка відповідь, включаючи вашу, - це наближення , просто тому, що це не точна відповідь. Ваш результат "2 місяці" може означати різні речі для різних входів, тому це наближення.
Адам Робінсон

моє не є наближенням, хоча, якщо сьогодні 14 березня, то два первісні місяці обчислюються виходячи з того, що в Джані було 31 день, а феб в ньому 29 днів. тепер ви правильні, що мій метод - це не визначення "загального" місяця, а ваш - це! Однак моє застосовується лише якщо ви повідомляєте про такі речі, як "Цей коментар було розміщено x місяців і y днів AGO", "AGO" частина має значення, оскільки його посилання на попередні x місяці, ці попередні x місяці потрібно обчислити виходячи з того, скільки днів було в тих х місяцях! посилання ....
Erx_VB.NExT.Coder

Чи має це сенс? тож якщо ви маєте на увазі конкретні, відомі місяці, то мій метод є 100% точним, і ви будете наближеним, однак, якщо ви маєте на увазі місяць взагалі, ви наближення були б кращою ідеєю, і моя просто була б поганою ідеєю (вона не створена для цього і не було б сенсу її використовувати). Ось посилання на мою статтю з описом проблеми та надання рішення: stackoverflow.com/questions/1916358 / ...
Erx_VB.NExT.Coder

2
Здається, це та сама логіка, яку використовує функція сервера Sql DateDiff (місяць, ...). Він також має перевагу в тому, що він є надзвичайно стислим і легким для пояснення та розуміння. Я б пояснив це так ... скільки сторінок у календарі вам доведеться перегорнути, щоб перейти від однієї дати до іншої?
JoelFan

51

(Я усвідомлюю, що це старе питання, але ...)

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


Я завантажив вашу бібліотеку і скопіював код, який ви написали вище, але отримую помилку часу компіляції. Помилка 1 Оператор '-' не може бути застосована до операндів типу 'NodaTime.LocalDate' та 'NodaTime.LocalDate'. Я знаю цю посаду з 5 років, чи змінилася якась річ з того часу, через що цей код не працює?
Hakan Fıstık

1
@HakamFostok: Вибачте - він працюватиме, коли вийде 2.0, але до цього вам потрібно скористатися Period.Between. Відредагували код, щоб він працював з NodaTime 1.3.1.
Джон Скіт

велике спасибі бібліотека NodaTime зробила саме те, що я хочу зробити. Я хотів підрахувати не лише місяці між двома датами, а й інші дні, і це саме те, що саме зробив NodaTime, ще раз дякую.
Hakan Fıstık

1
@JonSkeet Ваша бібліотека - це справді чорна магія. Фініки кусають мене весь час. Цей фрагмент коду врятував мені величезну кількість часу.
onefootswill

28

Можливо, ви не хочете знати про місячні частки; Що з цим кодом?


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

9

Вам потрібно буде визначити, що ви маєте на увазі під TotalMonths для початку.
Просте визначення ставить місяць на 30,4 дня (365,25 / 12).

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


9

Я написав дуже простий метод розширення 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'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)));

3
Сільське, але найкраще рішення. Копії та вклеювання. Дякую
Даніель Долз

8

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

Одним із методів було б підрахувати місяць, а потім виправити дні наприкінці. Щось на зразок:

   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;

Хороший код, хоча, 1 помилка: замість цього: (як 28 лютого + 1 місяць == 28 березня) :-) // десяткових днівInEndMonth = (кінець - кінець.AddMonths (1)). Днів; Я пропоную: десяткові дніInEndMonth = DateTime.DaysInMonth (end.Year, end.Month) * -1;
bezieur

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
Це, безумовно, один алгоритм, але його можна значно спроститиreturn (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
Адам Робінсон

1
Дві проблеми: Ви починаєте з 2 дат, а не з TimeSpan. По-друге, ви обчислюєте між першим з обох місяців, це дуже сумнівне визначення. Хоча іноді це може бути правильним.
Хенк Холтерман

@Henk: Так, звичайно, це не завжди правильно, тому я сказав, що саме так я б це зробив, а не як це хтось повинен робити. В ОП не вказано, як слід розраховувати результат. @Adam: Вау, я знову думав, що це занадто складно ... це занадто часто трапляється зі мною. Дякую за коментар, ви, очевидно, праві, ваша версія набагато краща. Я буду використовувати це відтепер.
Максиміліан Майерл

@Adam: чому ти не подаєш це як фактичну відповідь ?! Це найбільш компактний поки що. Дуже струнка.
Діна

@Dinah: Я не хотіла припускати, що це те, що ти насправді хотів. Якщо це так, я відредагував свою попередню відповідь, щоб включити такий підхід.
Адам Робінсон

3

На це не так багато чітких відповідей, оскільки ти завжди припускаєш речі.

Це рішення обчислює між двома датами, місяцями між припущенням, що ви бажаєте зберегти день місяця для порівняння (мається на увазі, що день місяця враховується в обчисленні)

Наприклад, якщо у вас дата 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
}

3

Прийнята відповідь прекрасно працює, коли потрібно повних місяців.

Мені потрібні були часткові місяці. Це рішення, яке я придумав за часткові місяці:

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

У вас була помилка логіки у вашій YearDifferenceфункції, коли lValue.Month < rValue.Month- я це виправив, можливо, ви захочете переглянути ...
Stobor

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

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

2
Опис із кодом буде корисним і для інших читачів.
Boeckm

так, будь ласка, додайте коментар.
Амар

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

Здається, це та сама логіка, яку використовує функція сервера Sql DateDiff (місяць, ...). Він також має перевагу в тому, що він є надзвичайно стислим і легким для пояснення та розуміння. Я б пояснив це так ... скільки сторінок у календарі вам доведеться перегорнути, щоб перейти від однієї дати до іншої?
JoelFan

1

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

1

Нижче насправді найточніший спосіб це зробити, оскільки визначення поняття "1 місяць" змінюється залежно від місяця, а інші відповіді беруть це до уваги! Якщо ви хочете отримати більше інформації про проблему, яка не вбудована в рамки, ви можете прочитати цю публікацію: Реальний об’єкт часу з .Years & .Months (втім, читати цей пост не потрібно розуміти і використовувати функцію нижче, вона працює на 100%, без притаманних неточностей наближення, які інші люблять використовувати - і сміливо заміняйте функцію .ReverseIt на вбудовану функцію.

Зверніть увагу, що ви можете отримувати будь-яку кількість точності дат / разів, секунд і хвилин або секунд, хвилин і днів, де завгодно до років (що міститиме 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", яка бере рядок джерела, та аргумент, що вказує, що потрібно замінити, та інший аргумент із зазначенням того, на що ви хочете його замінити, і він замінює лише останню появу цього рядка ... я включив свій, якщо у вас його немає або не хочете його реалізовувати, тож ось він працює, "як є", не потребуючи жодних змін. Я знаю, що функція зворотного перегляду більше не потрібна (існує в .net), але функція ReplaceLast і ReverseIt переносяться з попередніх днів, тому, будь ласка, вибачте, як це може виглядати (все ще працює 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 

0

Якщо ви хочете вказати точну кількість, ви не можете лише з Timespan, оскільки вам потрібно знати, які місяці ви маєте справу, і чи маєте ви справу з високосним роком, як ви сказали.

Або перейдіть на приблизну кількість, або виконайте кілька фітінг з оригінальним DateTimes


0

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

Якщо ви можете отримати час, перетворений з григоріанської дати в номер дня Джуліана , ви можете просто створити оператора для порівняння номера зулійського дня, який можна набрати подвійно, щоб отримати місяці, дні, секунди тощо. Перевірте вище посилання на алгоритм перетворення з Григоріана на Джуліана.



0

Якщо ви маєте справу з місяцями та роками, вам потрібно щось, що знає, скільки днів має кожен місяць і які роки є високосними.

Введіть Григоріанський календар (та інші реалізовані для культури календарі ).

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

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

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;

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. Якщо кінцевий рік більший, нам потрібно оцінити, чи поточний день більший чи рівний попередньому дню 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)));

Смерть потрійним?
SpaceBison

0

Моя відповідь також використовує метод розширення , але це може повернути позитивний чи негативний результат.

public static int MonthsBefore(this DateTime dt1, DateTime dt2)
{
    (DateTime early, DateTime late, bool dt2After) = dt2 > dt1 ? (dt1,dt2,true) : (dt2,dt1,false);
    DateTime tmp; // Save the result so we don't repeat work
    int months = 1;
    while ((tmp = early.AddMonths(1)) <= late)
    {
        early = tmp;
        months++;
    }
    return (months-1)*(dt2After ? 1 : -1);
}

Пара тестів:

// Just under 1 month's diff
Assert.AreEqual(0, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 1, 31)));
// Just over 1 month's diff
Assert.AreEqual(1, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 2, 2)));    
// Past date returns NEGATIVE
Assert.AreEqual(-6, new DateTime(2012, 1, 1).MonthsBefore(new DateTime(2011, 6, 10)));

0

Поєднуючи два відповіді вище, інший метод розширення:

public static int ElapsedMonths(this DateTime date1, DateTime date2)
{
    DateTime earlierDate = (date1 > date2) ? date2 : date1;
    DateTime laterDate = (date1 > date2) ? date1 : date2;
    var eMonths = (laterDate.Month - earlierDate.Month) + 12 * (laterDate.Year - earlierDate.Year) - 
                                            ((earlierDate.Day > laterDate.Day) ? 1 : 0);
    return eMonths;
}

Завдяки @AdamRobinson та @MarkWhittaker


-1

Розрахуйте кількість місяців між двома датами:

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

1
Це PHP, а не C #.
AFract
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.