두 날짜 사이의 월 차이 계산


128

C # /. NET TimeSpan에는 TotalDays, TotalMinutes등이 있지만 총 개월 차이에 대한 공식을 알 수는 없습니다. 한 달에 다양한 날과 윤년이 계속 저를 던져 버립니다. TotalMonths를 어떻게받을 수 있습니까 ?

편집 더 명확하지 않은 것에 대해 죄송합니다 : 실제로 이것을 얻을 수는 TimeSpan없지만 사용 TotalDays하고 있다고 생각 TotalMinutes하고 총 달을 얻는 것을 제외하고는 내가 찾고있는 것을 표현하는 좋은 예가 될 것입니다 ...

예 : 2009 년 12 월 25 일-2009 년 10 월 6 일 = 총 2 개월 10 월 6 일부터 11 월 5 일까지는 0 개월입니다. 11 월 6 일, 1 개월 2 개월 12 월 6 일


2
2009 년 12 월 25 일-2009 년 10 월 6 일에 무엇을 기대하십니까?
Jeff Moser

2
개월 단위로 TimeSpan을 어떻게 정의합니까?
Aliostad

1
@Aliostad-날짜가 없으면 월을 30 일로 정의하고 매우 정확할 수 있습니다.
ChaosPandion

그것은 어떤 이유로 mod에 의해이 질문과 병합되었습니다.
Jamiec

실제로, 나는이 질문에 답하고 코딩 된 솔루션, stackoverflow.com/questions/1916358/을 제공하는 여기 내 게시물을 읽을 필요가 있습니다 ... 트롤을 무시하고 수퍼 캣으로 의견을 통해 대화에주의를 기울이십시오. 우리가 "고아 개월"이라고 부르는 시간 범위의 시작과 끝에서 r 개월과 질문은 이러한 고아 개월을 일 단위로 정의하는 방법으로 귀착됩니다. ), 나머지는 코드입니다 (포함됨). 내 방어력. 내 사용자가 기대하는 것입니다 무슨 생각을 기반으로
Erx_VB.NExT.Coder

답변:


222

TimeSpan"월"은 가변 측정 단위이므로을 ( 를) 얻을 수 없습니다 . 직접 계산해야하며 작동 방식을 정확히 파악해야합니다.

예를 들어, 날짜 좋아해야 July 5, 2009하고 August 4, 2009한 달 제로 개월 차이를 산출? 당신은 무엇에 대해 다음 중 하나를 양보해야한다고 경우 July 31, 2009August 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 당신이 진정한 .Month와 .Years를 알고 싶다면 근사치입니다. 나는 당신이 읽을 수있는 것에 대한 답변을 방금 게시했습니다. 근사치에 이르기까지 이것은 좋은 근사치이지만 (Adam Robinson의 소품) 이러한 근사치 중 하나를 사용하면 의도 치 않게 사용자에게 거짓말한다는 것을 명심해야합니다.
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder : 소품에 감사드립니다.하지만 당신의 대답에 따르면 한 달이 가변적 인 측정 단위라는 사실을 답으로 생각하지는 않지만 대부분의 사람들은 그렇지 않습니다. 그들은 단지 당신의 특정 근사치를 사용하지 않습니다. 내 대답의 첫 번째 문장은 변수임을 나타냅니다. 귀하의 답변 은 정확한 답변이 아니기 때문에 근사치 입니다. "2 개월"결과는 입력에 따라 다른 것을 의미 할 수 있으므로 근사치입니다.
Adam Robinson

그러나 오늘이 3 월 14 일인 경우에는 1 월이 31 일이고 2 월이 29 일이라는 사실에 근거하여 2 개월의 월이 계산됩니다. 지금, 당신은 내 방법이 "일반적인"달의 정의가 아니라는 점에서 맞습니다. 그러나 "이 의견은 x 개월 및 y 일 AGO에 게시되었습니다"와 같은 내용을보고하는 경우에만 적용됩니다. "AGO"부분은 이전 x 개월을 참조하는 이전 x 개월을 참조해야하기 때문에 차이가 있습니다. 그 x 개월에 며칠이 있었는지에 따라! link ....
Erx_VB.NExT.Coder

말이 돼? 그래서 당신이 알려진 특정 개월을 언급한다면 내 방법은 100 % 정확하고 근사치 일 것입니다. 그러나 일반적으로 한 달을 언급한다면 근사치가 더 좋습니다. 광산은 단지 나쁜 생각 일 것입니다 (그것은 그것을 위해 만들어지지 않았으며 그것을 사용할 때 아무런 의미가 없습니다). 다음은 문제를 설명하고 솔루션을 제공하는 내 기사 링크입니다. stackoverflow.com/questions/1916358/…
Erx_VB.NExT.Coder

2
이것은 Sql Server DateDiff (month, ...) 함수에서 사용하는 것과 동일한 논리 인 것 같습니다. 또한 매우 간결하고 설명하고 이해하기 쉽다는 장점이 있습니다. 나는 다음과 같이 설명 할 것입니다 ... 캘린더의 한 페이지에서 다른 페이지로 몇 페이지를 넘겨야합니까?
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에서 작동하도록 코드를 편집했습니다.
Jon Skeet

덕분에 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

나는에 아주 간단한 확장 메서드를 작성했습니다 DateTimeDateTimeOffset이 작업을 수행하는. 나는 그것이 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
소박하지만 최고의 솔루션. 복사하여 붙여 넣습니다. 감사합니다
Daniel Dolz

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 버그 : 대신 : (2 월 28 일 + 1 개월 == 3 월 28 일) :-) // decimal daysInEndMonth = (end-end.AddMonths (1)). Days; 다음과 같이 제안합니다. decimal daysInEndMonth = 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);
Adam Robinson

1
두 가지 문제 : TimeSpan이 아닌 2 개의 Date에서 시작합니다. 둘째, 두 달의 1 일 사이를 계산하면 매우 의심스러운 정의입니다. 때때로 옳을 수도 있지만.
Henk Holterman

@Henk : 그렇습니다. 물론 항상 옳지는 않습니다. 이것이 제가 어떻게해야하는지가 아니라 어떻게해야한다고 말한 이유입니다. OP는 결과 계산 방법을 지정하지 않았습니다. @Adam : 와우, 나는 너무 복잡하지만 다시 생각했다. 의견 주셔서 감사합니다, 당신은 분명히 맞아, 당신의 버전은 훨씬 낫다. 나는 이것을 지금부터 사용할 것입니다.
Maximilian Mayerl

@ 아담 : 이것을 실제 답변으로 제출하지 않겠습니까?! 이것은 지금까지 가장 컴팩트합니다. 매우 매끄 럽습니다.
Dinah

@ 디나 ​​: 나는 그것이 당신이 실제로 원하는 것이라고 가정하고 싶지 않았습니다. 그렇다면이 접근법을 포함하도록 이전 답변을 편집했습니다.
Adam Robinson

3

당신은 항상 물건을 가정하기 때문에 이것에 대한 명확한 대답은 많지 않습니다.

이 솔루션은 비교를 위해 월을 저장한다고 가정 할 때 두 월 사이의 월을 계산합니다 (달의 날짜가 계산에서 고려 됨).

예를 들어, 2012 년 1 월 30 일의 날짜 인 경우 2012 년 2 월 29 일은 한 달이 아니라 2013 년 3 월 1 일입니다.

그것은 꽤 철저하게 테스트되었으며, 아마도 우리가 그것을 사용할 때 정리 할 것이고 아마도 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

예, 논평을 추가하십시오.
Amar

1

몇 달의 문제는 실제로 간단한 척도가 아니며 크기가 일정하지 않다는 것입니다. 포함시키려는 규칙을 정의하고 거기서부터 작업해야합니다. 예를 들어 1 월 1 일에서 2 월 1 일까지-2 개월이 포함되어 있다고 주장하거나 1 개월이라고 말할 수 있습니다. 그렇다면 "1 월 1 일 20:00"부터 "2 월 1 일 00:00"은 한 달 전체가 아닙니다. 0입니까? 1? 다른 방법은 어떻습니까 (1 월 1 일 00:00부터 2 월 1 일 20:00까지) ... 1? 2?

먼저 규칙을 정의한 다음 직접 코드를 작성해야합니다.


1

1사이 에 결과를 표시하려는 경우 : 28th Feb1st March

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

이것은 Sql Server DateDiff (month, ...) 함수에서 사용하는 것과 동일한 논리 인 것 같습니다. 또한 매우 간결하고 설명하고 이해하기 쉽다는 장점이 있습니다. 나는 다음과 같이 설명 할 것입니다 ... 캘린더의 한 페이지에서 다른 페이지로 몇 페이지를 넘겨야합니까?
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 함수를 기본 제공 .Reverse 함수로 자유롭게 교체하십시오 (완전히 여기에 있습니다).

최대 몇 년 (6 개 부품 / 세그먼트 포함)의 날짜 / 시간 정확도, 초 및 분 또는 초, 분 및 일 수에 관계없이 얻을 수 있습니다. 상위 2 개를 지정하고 1 년이 지난 경우 "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"당신이 6 개 세그먼트를 지정하는 경우에도, 상위 3 개 세그먼트와 무시 그들을에는 날짜 데이터가없는 이해로 걱정하지 마십시오 :). 물론 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 기능은 .net 이전 일부터 이월되므로 날짜가 어떻게 생겼는지 변명하십시오 (여전히 100 % 작동, 사용 중임) 10 년 이상 동안, 버그가 없다고 보증 할 수 있습니다) : ...). 건배.

<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

몇 달과 몇 년을 다루는 경우 매월 며칠이 지났으며 윤년인지 알고있는 것이 필요합니다.

그레고리력 및 다른 문화권 별 달력 구현을 입력하십시오 .

Calendar는 두 시점의 차이를 직접 계산하는 방법을 제공하지 않지만 다음과 같은 방법을 가지고 있습니다.

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라는 다른 메소드를 작성하고 while 루프에서 AddMonths 대신 위와 동일한 로직을 정확히 적용 할 수 있습니다.


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

삼항에 의한 죽음?
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
이것은 C #이 아닌 PHP입니다.
16:36에
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.