tính toán chênh lệch theo tháng giữa hai ngày


128

Trong C # /. NET TimeSpanTotalDays, TotalMinutesv.v. nhưng tôi không thể tìm ra một công thức cho tổng số tháng chênh lệch. Ngày thay đổi mỗi tháng và năm nhuận tiếp tục ném tôi đi. Làm cách nào tôi có thể nhận TotalMonths ?

Chỉnh sửa Xin lỗi vì không rõ ràng hơn: Tôi biết tôi thực sự không thể lấy được thứ này TimeSpannhưng tôi nghĩ sử dụng TotalDaysTotalMinutessẽ là một ví dụ tốt để thể hiện những gì tôi đang tìm kiếm ... ngoại trừ tôi đang cố gắng để có được Tổng số tháng.

Ví dụ: ngày 25 tháng 12 năm 2009 - ngày 6 tháng 10 năm 2009 = 2 TotalMonths. Ngày 6 tháng 10 đến ngày 5 tháng 11 bằng 0 tháng. Vào ngày 6 tháng 11, 1 tháng. Vào ngày 6 tháng 12, 2 tháng


2
Bạn đang mong đợi điều gì cho ngày 25 tháng 12 năm 2009 - ngày 6 tháng 10 năm 2009?
Jeff Moser

2
Làm thế nào để bạn xác định TimeSpan trong tháng?
Aliostad 17/03/2016

1
@Aliostad - Không có ngày, bạn có thể xác định một tháng là 30 ngày và khá chính xác.
ChaosPandion 17/03/2016

Nó đã được hợp nhất với câu hỏi này bởi một mod vì một số lý do.
Jamiec

Trên thực tế, bạn cần phải đọc bài đăng của tôi ở đây, câu trả lời cho câu hỏi này và cung cấp giải pháp được mã hóa, stackoverflow.com/questions/1916353/ trộm bỏ qua các troll (brianary) và chú ý đến cuộc trò chuyện của tôi thông qua các bình luận với supercat. Những tháng r bắt đầu và kết thúc một khoảng thời gian chúng ta gọi là "Tháng mồ côi", và câu hỏi đặt ra là làm thế nào để xác định những tháng mồ côi này theo ngày - khi bạn đã xác định rằng (& bạn muốn xác định nó như thế nào ), phần còn lại chỉ là mã (được bao gồm). Def của tôi được dựa trên những gì tôi nghĩ người dùng của tôi sẽ mong đợi
Erx_VB.NExT.Coder

Câu trả lời:


222

Bạn sẽ không thể có được điều đó từ a TimeSpan, bởi vì "tháng" là một đơn vị đo lường khác nhau. Bạn sẽ phải tự tính toán và bạn sẽ phải tìm ra chính xác bạn muốn nó hoạt động như thế nào.

Ví dụ, ngày như thế nào July 5, 2009August 4, 2009mang lại chênh lệch một tháng hay không tháng? Nếu bạn nói nó nên mang lại một, thì còn July 31, 2009August 1, 2009thì sao? Là rằng một tháng? Nó chỉ đơn giản là sự khác biệt của các Monthgiá trị cho các ngày, hay nó liên quan nhiều hơn đến một khoảng thời gian thực tế? Logic để xác định tất cả các quy tắc này là không tầm thường, vì vậy bạn sẽ phải xác định thuật toán của riêng mình và thực hiện thuật toán thích hợp.

Nếu tất cả những gì bạn muốn chỉ đơn giản là sự khác biệt trong các tháng - hoàn toàn không quan tâm đến các giá trị ngày - thì bạn có thể sử dụng điều này:

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

Lưu ý rằng điều này trả về một sự khác biệt tương đối, có nghĩa là nếu rValuelớn hơn lValue, thì giá trị trả về sẽ âm. Nếu bạn muốn một sự khác biệt tuyệt đối, bạn có thể sử dụng điều này:

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

@Dinah đây chỉ là một xấp xỉ, nếu bạn muốn biết đúng .Month và .Years - Tôi vừa đăng một câu trả lời cho những gì bạn có thể đọc. Mặc dù, theo như gần đúng như vậy, đây là một xấp xỉ tốt (đạo cụ cho Adam Robinson) tuy nhiên bạn nên nhớ rằng nếu bạn sử dụng bất kỳ xấp xỉ nào trong số này thì bạn chỉ vô tình nói dối người dùng của mình.
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder: Cảm ơn các đạo cụ, nhưng trong khi câu trả lời của bạn nói rằng không có câu trả lời nào thực tế rằng một tháng là một đơn vị đo lường khác nhau, có vẻ như hầu hết chúng đều làm được; họ chỉ không sử dụng xấp xỉ cụ thể của bạn. Trong trường hợp, câu đầu tiên trong câu trả lời của tôi chỉ ra rằng đó là biến. Bất kỳ câu trả lời nào, bao gồm của bạn, là một xấp xỉ , đơn giản vì đó không phải là một câu trả lời chính xác. Kết quả "2 tháng" của bạn có thể có nghĩa là những điều khác nhau cho các đầu vào khác nhau, vì vậy đó là một xấp xỉ.
Adam Robinson

Của tôi không phải là một xấp xỉ, nếu hôm nay là ngày 14 tháng 3, thì hai tháng có thể được tính dựa trên thực tế là jan có 31 ngày trong đó và feb có 29 ngày trong đó. Bây giờ, bạn đúng ở chỗ phương pháp của tôi không phải là định nghĩa của tháng "chung", và của bạn là! Tuy nhiên, tôi chỉ áp dụng nếu bạn đang báo cáo những điều như "Nhận xét này đã được đăng x tháng và y ngày AGO", phần "AGO" tạo ra sự khác biệt, bởi vì nó đề cập đến x tháng trước, những tháng x trước đó cần được tính dựa trên số ngày đã có mặt trong x tháng đó! liên kết ....
Erx_VB.NExT.Coder

Điều đó có ý nghĩa? Vì vậy, nếu bạn đang đề cập đến các tháng cụ thể, đã biết, thì phương pháp của tôi chính xác 100% và bạn sẽ là một xấp xỉ, tuy nhiên, nếu bạn nói đến một tháng nói chung, thì gần đúng bạn sẽ là một ý tưởng tốt hơn, và Của tôi sẽ chỉ là một ý tưởng tồi (nó không được thực hiện cho điều đó và sẽ không có điểm nào trong việc sử dụng nó). Đây là liên kết đến bài viết của tôi mô tả vấn đề và cung cấp giải pháp: stackoverflow.com/questions/1916353/
Thẻ

2
Đây có vẻ là logic tương tự được sử dụng bởi hàm DateDiff (tháng, ...) của Sql Server. Nó cũng có ưu điểm là cực kỳ súc tích và dễ giải thích và hiểu. Tôi sẽ giải thích nó như sau ... bạn sẽ phải chuyển bao nhiêu trang trong lịch để chuyển từ ngày này sang ngày khác?
JoelFan

51

(Tôi nhận ra đây là một câu hỏi cũ, nhưng ...)

Điều này là tương đối đau đớn để làm trong .NET thuần túy. Tôi muốn giới thiệu thư viện Noda Time của riêng tôi , được thiết kế đặc biệt cho những thứ như thế này:

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

(Có các tùy chọn khác, ví dụ: nếu bạn chỉ muốn đếm số tháng thậm chí trong nhiều năm, bạn sẽ sử dụng Period period = Period.Between(start, end, PeriodUnits.Months);)


Tôi đã tải xuống thư viện của bạn và tôi đã sao chép mã mà bạn đã viết ở trên, nhưng tôi đang nhận được lỗi thời gian biên dịch. Lỗi 1 Toán tử '-' không thể được áp dụng cho các toán hạng loại 'NodaTime.LocalDate' và 'NodaTime.LocalDate'. Tôi biết bài đăng này từ 5 năm, có điều gì thay đổi từ thời điểm đó, điều này làm cho mã này không hoạt động?
Hakan Fıstık

1
@HakamFostok: Xin lỗi - nó sẽ hoạt động khi 2.0 được phát hành, nhưng cho đến lúc đó bạn cần sử dụng Period.Between. Đã chỉnh sửa mã để nó hoạt động với NodaTime 1.3.1.
Jon Skeet

cảm ơn rất nhiều thư viện NodaTime đã làm chính xác những gì tôi muốn làm. Tôi muốn tính toán không chỉ các tháng giữa hai ngày mà cả các ngày còn lại, và đây là điều mà NodaTime đã thực hiện chính xác, cảm ơn một lần nữa.
Hakan Fıstık

1
@JonSkeet Thư viện đó của bạn thực sự là ma thuật đen. Ngày cắn tôi mọi lúc. Đoạn mã đó đã tiết kiệm cho tôi một lượng lớn thời gian.
onefootswill 18/03/19

28

Có lẽ bạn không muốn biết về phân số tháng; Mã này thì sao?


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
Tôi không hiểu * 100. Có nên là * 12 không?
Ruffles

9

Bạn sẽ phải xác định những gì bạn có nghĩa là TotalMonths để bắt đầu.
Một định nghĩa đơn giản đặt một tháng ở mức 30,4 ngày (365,25 / 12).

Ngoài ra, bất kỳ định nghĩa nào bao gồm các phân số dường như vô dụng và giá trị nguyên phổ biến hơn (cả tháng giữa các ngày) cũng phụ thuộc vào quy tắc kinh doanh không chuẩn.


9

Tôi đã viết một phương pháp mở rộng rất đơn giản DateTimeDateTimeOffsetđể làm điều này. Tôi muốn nó hoạt động chính xác như một TotalMonthstài sản trên TimeSpansẽ hoạt động: tức là trả lại số tháng hoàn thành giữa hai ngày, bỏ qua bất kỳ tháng nào. Bởi vì nó dựa trên DateTime.AddMonths()nó tôn trọng độ dài tháng khác nhau và trả về những gì con người sẽ hiểu là một khoảng thời gian của tháng.

(Thật không may, bạn không thể triển khai nó như một phương thức mở rộng trên TimeSpan vì điều đó không lưu giữ kiến ​​thức về ngày thực tế được sử dụng và trong nhiều tháng chúng rất quan trọng.)

Mã và các bài kiểm tra đều có sẵn trên GitHub . Mã rất đơn giản:

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

Và nó vượt qua tất cả các trường hợp thử nghiệm đơn vị:

// 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
Mộc mạc, nhưng giải pháp tốt nhất. Bản sao và dán. Cảm ơn bạn
Daniel Dolz

8

Bạn cần phải tự giải quyết nó. Cách bạn đối phó với những ngày còn sơ khai ở cuối sẽ phụ thuộc vào những gì bạn muốn sử dụng nó cho.

Một phương pháp sẽ là đếm tháng và sau đó sửa cho ngày cuối cùng. Cái gì đó như:

   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;

Mặc dù vậy, mã đẹp, 1 lỗi: thay vào đó: (như 28 tháng 2 + 1 tháng == 28 tháng 3) :-) // ngày thập phânInEndMonth = (end - end.AddMonths (1)). Days; Tôi đề nghị: số thập phânInEndMonth = DateTime.DaysInMonth (end.Year, end.Month) * -1;
bezieur

3

Tôi sẽ làm như thế này:

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
Đó chắc chắn là một algoritm, nhưng nó có thể được đơn giản hóa rất nhiềureturn (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
Adam Robinson

1
Hai vấn đề: Bạn đang bắt đầu từ 2 Ngày, không phải là TimeSpan. Thứ hai, bạn tính giữa ngày đầu tiên của cả hai tháng, đó là một định nghĩa rất đáng nghi ngờ. Mặc dù đôi khi nó có thể đúng.
Henk Holterman

@Henk: Vâng, tất nhiên điều đó không phải lúc nào cũng đúng, đó là lý do tại sao tôi nói rằng đây là cách tôi sẽ làm chứ không phải ai cũng nên làm. OP không chỉ định cách tính kết quả. @Adam: Wow, tôi nghĩ cách quá phức tạp một lần nữa ... điều đó xảy ra quá thường xuyên với tôi. Cảm ơn đã nhận xét, bạn rõ ràng là đúng, phiên bản của bạn tốt hơn nhiều. Tôi sẽ sử dụng điều này từ bây giờ.
Maximilian Mayerl

@Adam: tại sao bạn không gửi câu trả lời này như một câu trả lời thực tế?! Đây là nhỏ gọn nhất cho đến nay. Rất lắt léo.
Dinah

@Dinah: Tôi không muốn cho rằng đó là những gì bạn thực sự muốn. Nếu có, tôi đã chỉnh sửa câu trả lời trước của mình để đưa vào phương pháp này.
Adam Robinson

3

Không có nhiều câu trả lời rõ ràng về điều này bởi vì bạn luôn giả định mọi thứ.

Giải pháp này tính toán giữa hai ngày các tháng giữa giả sử bạn muốn lưu ngày trong tháng để so sánh, (có nghĩa là ngày trong tháng được xem xét trong tính toán)

Ví dụ: nếu bạn có ngày 30 tháng 1 năm 2012, ngày 29 tháng 2 năm 2012 sẽ không phải là một tháng mà là ngày 01 tháng 3 năm 2013.

Nó đã được kiểm tra khá kỹ lưỡng, có thể sẽ dọn sạch nó sau khi chúng tôi sử dụng và mất hai ngày thay vì Timespan, điều này có lẽ tốt hơn. Hy vọng điều này sẽ giúp bất cứ ai khác.

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

Câu trả lời được chấp nhận hoạt động hoàn hảo khi bạn muốn tháng đầy đủ.

Tôi cần một phần tháng. Đây là giải pháp tôi đã đưa ra trong một phần tháng:

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

Tôi cũng cần một sự khác biệt năm với cùng một nhu cầu cho một phần năm. Đây là giải pháp tôi đã đưa ra:

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

Bạn đã gặp lỗi logic trong YearDifferencechức năng của mình khi lValue.Month < rValue.Month- Tôi đã sửa nó ngay bây giờ, bạn có thể muốn xem lại ...
Stobor

2

Câu hỏi cũ tôi biết, nhưng có thể giúp được ai đó. Tôi đã sử dụng @Adam chấp nhận câu trả lời ở trên, nhưng sau đó kiểm tra xem chênh lệch là 1 hay -1 rồi kiểm tra xem đó có phải là chênh lệch đầy đủ của tháng không. Vì vậy, 21/07/55 và 20/08/55 sẽ không phải là một tháng đầy đủ, nhưng 21/07/55 và 21/07/55 sẽ là.

/// <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
Một mô tả đi kèm với mã cũng sẽ có lợi cho những người đọc khác.
Boeckm

yeah xin vui lòng thêm một số bình luận.
Amar

1

Vấn đề với nhiều tháng là nó không thực sự là một biện pháp đơn giản - chúng không phải là kích thước không đổi. Bạn sẽ cần xác định quy tắc của mình cho những gì bạn muốn bao gồm và làm việc từ đó. Ví dụ: 1 tháng 1 đến 1 tháng 2 - bạn có thể tranh luận 2 tháng có liên quan ở đó hoặc bạn có thể nói đó là một tháng. Sau đó, khoảng "1 tháng 1 20:00" đến "1 tháng 2 00:00" - đó không phải là toàn bộ tháng. Có phải là 0 không? 1? Thế còn cách khác (1 tháng 1 00:00 đến 1 tháng 2 20:00) ... 1? 2?

Trước tiên, hãy xác định các quy tắc, sau đó bạn sẽ phải tự viết mã, tôi sợ ...


1

Nếu bạn muốn có kết quả 1giữa 28th Feb1st March:

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

Đây có vẻ là logic tương tự được sử dụng bởi hàm DateDiff (tháng, ...) của Sql Server. Nó cũng có ưu điểm là cực kỳ súc tích và dễ giải thích và hiểu. Tôi sẽ giải thích nó như sau ... bạn sẽ phải chuyển bao nhiêu trang trong lịch để chuyển từ ngày này sang ngày khác?
JoelFan

1

Thư viện này tính toán sự khác biệt của các tháng, xem xét tất cả các phần của 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

Dưới đây thực sự là cách chính xác nhất mà bạn có thể thực hiện, vì định nghĩa của "1 tháng" thay đổi tùy thuộc vào tháng nào và không có câu trả lời nào khác có tính đến điều này! Nếu bạn muốn biết thêm thông tin về vấn đề không được xây dựng trong khung, bạn có thể đọc bài đăng này: Đối tượng thời gian thực với .Years & .Months (tuy nhiên, đọc bài đăng đó không cần thiết để hiểu và sử dụng chức năng bên dưới, nó hoạt động 100%, không có sự thiếu chính xác vốn có của phép tính gần đúng mà người khác thích sử dụng - và thoải mái thay thế hàm .ReverseIt bằng hàm .Reverse tích hợp mà bạn có thể có trên khung của mình (nó chỉ ở đây để hoàn thiện).

Xin lưu ý rằng bạn có thể nhận được bất kỳ số ngày / lần chính xác, giây & phút, hoặc giây, phút và ngày, bất cứ nơi nào lên đến nhiều năm (sẽ chứa 6 phần / phân đoạn). Nếu bạn chỉ định hai người đứng đầu và hơn một tuổi, nó sẽ trả về "1 năm và 3 tháng trước" và sẽ không trả lại phần còn lại vì bạn đã yêu cầu hai phân đoạn. nếu nó chỉ mới vài giờ, thì nó sẽ chỉ trở lại "2 giờ 1 phút trước". Tất nhiên, các quy tắc tương tự được áp dụng nếu bạn chỉ định 1, 2, 3, 4, 5 hoặc 6 segmets (tối đa là 6 vì giây, phút, giờ, ngày, tháng, năm chỉ tạo ra 6 loại). Nó cũng sẽ sửa các vấn đề ngữ pháp như "phút" so với "phút" tùy thuộc vào việc đó là 1 phút trở lên, giống nhau cho tất cả các loại và "chuỗi" được tạo sẽ luôn đúng về mặt ngữ pháp.

Dưới đây là một số ví dụ để sử dụng: b ALLowSegments xác định có bao nhiêu phân đoạn sẽ hiển thị ... tức là: nếu 3, thì chuỗi trả về sẽ là (ví dụ) ... "3 years, 2 months and 13 days"(sẽ không bao gồm giờ, phút và giây là 3 thời gian hàng đầu các danh mục được trả về), tuy nhiên, ngày là một ngày mới hơn, chẳng hạn như một vài ngày trước, chỉ định các phân đoạn tương tự (3) sẽ trở lại "4 days, 1 hour and 13 minutes ago"thay vào đó, vì vậy nó sẽ tính đến mọi thứ!

nếu b ALLowSegments là 2 thì nó sẽ trả về "3 years and 2 months"và nếu 6 (giá trị tối đa) sẽ trả về "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", nhưng, xin lưu ý rằng nó sẽ NEVER RETURNgiống như thế này "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"vì nó hiểu rằng không có dữ liệu ngày trong 3 phân đoạn hàng đầu và bỏ qua chúng, ngay cả khi bạn chỉ định 6 phân đoạn , đừng lo lắng :). Tất nhiên, nếu có một phân đoạn có 0 trong đó, nó sẽ tính đến điều đó khi tạo chuỗi và sẽ hiển thị dưới dạng "3 days and 4 seconds ago"và bỏ qua phần "0 giờ"! Thưởng thức và xin vui lòng bình luận nếu bạn thích.

 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

Tất nhiên, bạn sẽ cần một hàm "Thay thế", lấy một chuỗi nguồn và một đối số chỉ định những gì cần được thay thế và một đối số khác chỉ định những gì bạn muốn thay thế nó và nó chỉ thay thế lần xuất hiện cuối cùng của chuỗi đó ... Tôi đã bao gồm một cái của tôi nếu bạn không có hoặc không muốn thực hiện nó, vì vậy ở đây, nó sẽ hoạt động "như hiện tại" mà không cần sửa đổi. Tôi biết chức năng Reverseit không còn cần thiết nữa (tồn tại trong .net) nhưng chức năng REPLast và ReverseIt được thực hiện từ những ngày trước.net, vì vậy xin vui lòng xem ngày đó có vẻ như thế nào (vẫn hoạt động 100% tho, đang sử dụng trong hơn mười năm, có thể đảm bảo họ không có lỗi) ... :). chúc mừng

<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

Nếu bạn muốn có con số chính xác, bạn không thể chỉ từ Timespan, vì bạn cần biết tháng nào bạn giao dịch và liệu bạn có đang xử lý một năm nhuận hay không, như bạn đã nói.

Hoặc là lấy số gần đúng hoặc thực hiện một số thao tác với DateTimes ban đầu



0

Không có cách nào được xây dựng để thực hiện điều này một cách chính xác trong thành ngữ-c #. Có một số cách giải quyết, chẳng hạn như ví dụ CodeProject mà mọi người đã mã hóa.


0

Nếu bạn đang đối phó với tháng và năm, bạn cần một cái gì đó biết mỗi tháng có bao nhiêu ngày và năm nào là năm nhuận.

Nhập Lịch Gregorian (và các triển khai Lịch cụ thể về văn hóa khác ).

Mặc dù Lịch không cung cấp các phương thức để tính trực tiếp sự khác biệt giữa hai thời điểm, nhưng nó có các phương thức như

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

Phương thức trả về một danh sách chứa 3 phần tử đầu tiên là năm, thứ hai là tháng và phần tử kết thúc là ngày:

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

Đây là đóng góp của tôi để có được sự khác biệt trong các Tháng mà tôi thấy là chính xác:

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

Sử dụng:

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

Bạn có thể tạo một phương thức khác gọi là DiffYears và áp dụng chính xác logic tương tự như trên và AddYears thay vì AddMonths trong vòng lặp while.


0

Đến trò chơi muộn nhưng tôi tưởng tượng điều này có thể hữu ích cho ai đó. Phần lớn mọi người có xu hướng đo từng tháng theo từng ngày, ngoại trừ thực tế là các tháng có các biến thể khác nhau. Sử dụng khung suy nghĩ đó tôi đã tạo ra một lớp lót so sánh ngày tháng cho chúng ta. Sử dụng quy trình sau.

  1. Bất kỳ số năm nào trên 1 khi so sánh năm sẽ được nhân với 12, không có trường hợp nào số này có thể bằng dưới 1 năm.
  2. Nếu năm cuối lớn hơn, chúng ta cần đánh giá xem ngày hiện tại lớn hơn hay bằng ngày 2A trước đó. Nếu ngày kết thúc lớn hơn hoặc bằng, chúng tôi lấy tháng hiện tại và sau đó cộng 12 tháng trừ đi tháng của tháng bắt đầu 2B. Nếu ngày kết thúc ít hơn ngày bắt đầu, chúng tôi sẽ thực hiện giống như trên trừ khi chúng tôi thêm 1 vào tháng bắt đầu trước khi trừ
  3. Nếu năm cuối không lớn hơn, chúng tôi thực hiện giống như 2A / 2B, nhưng không thêm 12 tháng vì chúng tôi không cần phải đánh giá quanh năm.

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

Chết bởi chim nhạn?
SpaceBison

0

Tôi đưa ra câu trả lời này cũng sử dụng một phương pháp mở rộng , nhưng nó có thể trả về kết quả tích cực hoặc tiêu cực.

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

Một vài bài kiểm tra:

// 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

Kết hợp hai trong số các câu trả lời ở trên, một phương pháp mở rộng khác là:

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

Cảm ơn @AdamRobinson và @MarkWhittaker


-1

Tính không có tháng giữa 2 ngày:

$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
Đây là PHP, không phải C #.
Thu hút
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.