menghitung selisih bulan antara dua tanggal


128

Dalam C # /. NET TimeSpanmemiliki TotalDays,, TotalMinutesdll. Tetapi saya tidak dapat menemukan rumus untuk perbedaan total bulan. Hari-hari variabel per bulan dan tahun kabisat terus mengusir saya. Bagaimana saya bisa mendapatkan TotalMonths ?

Sunting Maaf karena tidak lebih jelas: Saya tahu saya tidak bisa benar-benar mendapatkan ini dari TimeSpantetapi saya pikir menggunakan TotalDaysdan TotalMinutesakan menjadi contoh yang baik untuk mengungkapkan apa yang saya cari ... kecuali saya sedang mencoba untuk mendapatkan Total Bulan.

Contoh: 25 Des 2009 - 6 Okt 2009 = 2 Total Bulan. 6 Oktober hingga 5 November sama dengan 0 bulan. Pada 6 November, 1 bulan. Pada 6 Desember, 2 bulan


2
Apa yang Anda harapkan untuk 25 Des 2009 - 6 Okt 2009?
Jeff Moser

2
Bagaimana Anda mendefinisikan TimeSpan dalam bulan?
Aliostad

1
@Liliad - Tanpa tanggal, Anda dapat mendefinisikan bulan sebagai 30 hari dan cukup akurat.
ChaosPandion

Itu digabung dengan pertanyaan ini oleh seorang mod untuk beberapa alasan.
Jamiec

Sebenarnya, Anda perlu membaca posting saya di sini, yang menjawab pertanyaan ini & memberikan solusi kode, stackoverflow.com/questions/1916358/… abaikan troll (brianary) & perhatikan percakapan saya melalui komentar dengan supercat. Bulan-bulan yang dimulai pada awal & akhir rentang waktu yang kami sebut "Bulan Yatim", & pertanyaannya adalah bagaimana menentukan bulan-bulan yatim dalam bentuk hari - setelah Anda menentukannya (& bagaimana Anda ingin mendefinisikannya) ), sisanya hanya kode (yang termasuk). Def saya. didasarkan pada apa yang saya pikir pengguna saya harapkan
Erx_VB.NExT.Coder

Jawaban:


222

Anda tidak akan dapat memperolehnya dari a TimeSpan, karena "bulan" adalah satuan ukuran variabel. Anda harus menghitung sendiri, dan Anda harus mencari tahu bagaimana tepatnya Anda ingin itu berfungsi.

Misalnya, haruskah tanggal menyukai July 5, 2009dan August 4, 2009menghasilkan perbedaan satu bulan atau nol bulan? Jika Anda mengatakan itu harus menghasilkan satu, lalu bagaimana dengan July 31, 2009dan August 1, 2009? Apakah itu sebulan? Apakah hanya perbedaan Monthnilai untuk tanggal, atau lebih terkait dengan rentang waktu yang sebenarnya? Logika untuk menentukan semua aturan ini adalah non-sepele, jadi Anda harus menentukan sendiri dan mengimplementasikan algoritma yang sesuai.

Jika semua yang Anda inginkan hanyalah perbedaan dalam bulan - sama sekali mengabaikan nilai tanggal - maka Anda dapat menggunakan ini:

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

Perhatikan bahwa ini mengembalikan perbedaan relatif, yang berarti bahwa jika rValuelebih besar dari lValue, maka nilai pengembalian akan negatif. Jika Anda menginginkan perbedaan mutlak, Anda dapat menggunakan ini:

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

@Dinah ini hanya perkiraan, jika Anda ingin tahu benar. Bulan dan. Tahun - Saya baru saja mengirim jawaban untuk apa yang dapat Anda baca. Meskipun, sejauh perkiraan demikian, ini adalah perkiraan yang baik (props untuk Adam Robinson) namun Anda harus ingat bahwa jika Anda menggunakan salah satu dari perkiraan ini, Anda hanya secara tidak sengaja berbohong kepada pengguna Anda.
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder: Terima kasih atas alat peraga, tetapi sementara jawaban Anda menyatakan bahwa tidak ada jawaban yang memperhitungkan fakta bahwa satu bulan adalah satuan ukuran variabel yang diperhitungkan, tampaknya sebagian besar dari mereka melakukannya; mereka hanya tidak menggunakan perkiraan khusus Anda. Contohnya, kalimat pertama dalam jawaban saya menunjukkan bahwa itu variabel. Setiap jawaban, termasuk Anda, adalah perkiraan , hanya karena itu bukan jawaban yang tepat. Hasil "2 bulan" Anda dapat memiliki arti yang berbeda untuk input yang berbeda, sehingga merupakan perkiraan.
Adam Robinson

tambang saya bukan perkiraan, jika hari ini adalah 14 Maret, maka dua bulan sebelumnya dihitung berdasarkan fakta bahwa jan memiliki 31 hari di dalamnya, dan Februari memiliki 29 hari di dalamnya. sekarang, Anda benar karena metode saya bukan definisi bulan "umum", dan milik Anda adalah! Namun, milik saya hanya berlaku jika Anda melaporkan hal-hal seperti "Komentar ini diposting x bulan dan y hari AGO", bagian "AGO" membuat perbedaan, karena mengacu pada x bulan sebelumnya, x bulan sebelumnya perlu dihitung berdasarkan berapa hari yang hadir dalam x bulan itu! tautan ....
Erx_VB.NExT.Coder

Apakah itu masuk akal? jadi jika Anda merujuk pada bulan-bulan tertentu yang diketahui, maka metode saya 100% akurat dan Anda akan menjadi perkiraan, namun, jika Anda merujuk pada satu bulan secara umum, perkiraan Anda akan menjadi ide yang lebih baik, dan saya hanya akan menjadi ide yang buruk (tidak dibuat untuk itu dan tidak akan ada gunanya menggunakannya). Berikut ini tautan ke artikel saya yang menjelaskan masalah dan memberikan solusi: stackoverflow.com/questions/1916358/…
Erx_VB.NExT.Coder

2
Ini tampaknya menjadi logika yang sama yang digunakan oleh fungsi Sql Server DateDiff (bulan, ...). Ini juga memiliki keuntungan karena sangat ringkas dan mudah untuk dijelaskan dan dipahami. Saya akan menjelaskannya sebagai berikut ... berapa banyak halaman dalam kalender yang harus Anda putar untuk beralih dari satu tanggal ke yang lain?
JoelFan

51

(Aku sadar ini pertanyaan lama, tapi ...)

Ini relatif menyakitkan untuk dilakukan di .NET murni. Saya akan merekomendasikan perpustakaan Noda Time saya sendiri , yang dirancang khusus untuk hal-hal seperti ini:

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

(Ada opsi lain, misalnya jika Anda hanya ingin hitungan bulan bahkan beberapa tahun, Anda akan menggunakan Period period = Period.Between(start, end, PeriodUnits.Months);)


Saya mengunduh perpustakaan Anda dan saya menyalin kode yang Anda tulis di atas, tetapi saya menerima kesalahan waktu kompilasi. Kesalahan 1 Operator '-' tidak dapat diterapkan ke operan tipe 'NodaTime.LocalDate' dan 'NodaTime.LocalDate'. Saya tahu posting ini dari 5 tahun, apakah ada yang berubah dari waktu itu, yang membuat kode ini tidak berfungsi?
Hakan Fıstık

1
@HakamFostok: Maaf - ini akan berfungsi ketika 2.0 dirilis, tetapi sampai saat itu Anda perlu menggunakannya Period.Between. Telah mengedit kode sehingga akan berfungsi dengan NodaTime 1.3.1.
Jon Skeet

terima kasih banyak perpustakaan NodaTime melakukan apa yang ingin saya lakukan. Saya ingin menghitung tidak hanya bulan-bulan antara dua tanggal tetapi juga hari-hari yang tersisa, dan ini yang telah dilakukan NodaTime dengan tepat, terima kasih lagi.
Hakan Fıstık

1
@ JonSkeet Perpustakaan Anda itu benar-benar ilmu hitam. Tanggal menggigit saya sepanjang waktu. Cuplikan kode itu menghemat banyak waktu.
onefootswill

28

Mungkin Anda tidak ingin tahu tentang fraksi bulan; Bagaimana dengan kode ini?


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
Saya tidak mengerti * 100. Apakah seharusnya * 12?
Ruffles

9

Anda harus mendefinisikan apa yang Anda maksud dengan TotalMonths untuk memulai.
Sebuah definisi sederhana menempatkan satu bulan pada 30,4 hari (365.25 / 12).

Di luar itu, definisi apa pun termasuk fraksi tampaknya tidak berguna, dan nilai integer yang lebih umum (seluruh bulan antara tanggal) juga tergantung pada aturan bisnis yang tidak standar.


9

Saya telah menulis metode ekstensi yang sangat sederhana DateTimedan DateTimeOffsetuntuk melakukan ini. Saya ingin itu bekerja persis seperti TotalMonthsproperti yang TimeSpanakan bekerja: yaitu mengembalikan hitungan bulan lengkap antara dua tanggal, mengabaikan setiap bulan parsial. Karena didasarkan pada DateTime.AddMonths()itu menghormati panjang bulan yang berbeda dan mengembalikan apa yang manusia akan mengerti sebagai periode bulan.

(Sayangnya Anda tidak dapat menerapkannya sebagai metode ekstensi di TimeSpan karena itu tidak mempertahankan pengetahuan tentang tanggal aktual yang digunakan, dan selama berbulan-bulan mereka penting.)

Kode dan tes keduanya tersedia di GitHub . Kode ini sangat sederhana:

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

Dan itu melewati semua kasus uji unit ini:

// 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
Pedesaan, tetapi solusi terbaik. Salinan dan disisipkan. Terima kasih
Daniel Dolz

8

Anda harus menyelesaikannya sendiri di luar waktu data. Bagaimana Anda berurusan dengan hari rintisan pada akhirnya akan tergantung pada apa Anda ingin menggunakannya.

Salah satu metode adalah menghitung bulan dan kemudian mengoreksi hari di akhir. Sesuatu seperti:

   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;

Kode yang bagus, meskipun, 1 bug: sebaliknya: (28 Februari + 1 bulan == 28 Maret) :-) // decimal daysInEndMonth = (end-end.AddMonths (1)). Days; Saya sarankan: desimal daysInEndMonth = DateTime.DaysInMonth (end.Year, end.Month) * -1;
bezieur

3

Saya akan melakukannya seperti ini:

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
Itu tentu saja satu algoritm, tetapi bisa sangat disederhanakan menjadireturn (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
Adam Robinson

1
Dua masalah: Anda mulai dari 2 Tanggal, bukan TimeSpan. Kedua, Anda menghitung antara tanggal 1 dari kedua bulan, itu adalah definisi yang sangat dipertanyakan. Meskipun terkadang bisa benar.
Henk Holterman

@Enk: Ya, tentu saja itu tidak selalu benar, itu sebabnya saya mengatakan bahwa ini adalah bagaimana saya akan melakukannya, bukan bagaimana orang seharusnya melakukannya. OP tidak menentukan bagaimana hasil harus dihitung. @ Adam: Wow, saya pikir terlalu rumit lagi ... itu terlalu sering terjadi pada saya. Terima kasih atas komentarnya, Anda jelas benar, versi Anda jauh lebih baik. Saya akan menggunakan ini mulai sekarang.
Maximilian Mayerl

@ Adam: mengapa Anda tidak mengirimkan ini sebagai jawaban yang sebenarnya ?! Ini yang paling ringkas sejauh ini. Sangat licin.
Dinah

@Dinah: Saya tidak ingin menganggap itu yang sebenarnya Anda inginkan. Jika ya, saya telah mengedit jawaban saya sebelumnya untuk memasukkan pendekatan ini.
Adam Robinson

3

Tidak ada banyak jawaban yang jelas tentang ini karena Anda selalu mengasumsikan sesuatu.

Solusi ini menghitung antara dua tanggal bulan antara asumsi Anda ingin menyimpan hari bulan untuk perbandingan, (artinya hari bulan dipertimbangkan dalam perhitungan)

Contoh, jika Anda memiliki tanggal 30 Jan 2012, 29 Feb 2012 tidak akan menjadi sebulan tetapi 01 Maret 2013

Ini sudah diuji cukup menyeluruh, mungkin akan membersihkannya nanti saat kita menggunakannya, dan mengambil dua tanggal bukannya Timespan, yang mungkin lebih baik. Semoga ini bisa membantu orang lain.

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

Jawaban yang diterima bekerja dengan sempurna ketika Anda ingin bulan penuh.

Saya membutuhkan sebagian bulan. Ini adalah solusi yang saya buat selama beberapa bulan:

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

Saya juga membutuhkan perbedaan tahun dengan kebutuhan yang sama untuk tahun parsial. Inilah solusi yang saya buat:

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

Anda memiliki bug logika dalam YearDifferencefungsi Anda ketika lValue.Month < rValue.Month- Saya telah memperbaikinya sekarang, Anda mungkin ingin mengulas ...
Stobor

2

Pertanyaan lama yang saya tahu, tetapi mungkin bisa membantu seseorang. Saya telah menggunakan @Adam menerima jawaban di atas, tetapi kemudian memeriksa apakah perbedaannya adalah 1 atau -1 lalu memeriksa untuk melihat apakah perbedaan bulan penuh pada kalender. Jadi 21/07/55 dan 20/08/55 tidak akan menjadi satu bulan penuh, tetapi 21/07/55 dan 21/07/55 akan menjadi.

/// <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
Deskripsi untuk menggunakan kode akan bermanfaat bagi pembaca lain juga.
Boeckm

ya tolong tambahkan beberapa komentar.
Amar

1

Masalah dengan bulan adalah bahwa itu bukan ukuran yang sederhana - mereka bukan ukuran konstan. Anda perlu menetapkan aturan untuk hal yang ingin Anda sertakan, dan bekerja dari sana. Misalnya 1 Jan hingga 1 Feb - Anda bisa berdebat 2 bulan terlibat di sana, atau bisa dibilang satu bulan. Lalu bagaimana dengan "1 Jan 20:00" hingga "1 Feb 00:00" - itu belum sebulan penuh. Apakah itu 0? 1? bagaimana dengan sebaliknya (1 Jan 00:00 hingga 1 Feb 20:00) ... 1? 2?

Pertama-tama tentukan aturannya, maka Anda harus membuat kode sendiri, saya khawatir ...


1

Jika Anda ingin memiliki hasil 1antara 28th Febdan 1st March:

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

Ini tampaknya menjadi logika yang sama yang digunakan oleh fungsi Sql Server DateDiff (bulan, ...). Ini juga memiliki keuntungan karena sangat ringkas dan mudah untuk dijelaskan dan dipahami. Saya akan menjelaskannya sebagai berikut ... berapa banyak halaman dalam kalender yang harus Anda putar untuk beralih dari satu tanggal ke yang lain?
JoelFan

1

Pustaka ini menghitung selisih bulan, dengan mempertimbangkan semua bagian 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

Di bawah ini sebenarnya adalah cara paling akurat yang dapat Anda lakukan, karena definisi "1 Bulan" berubah tergantung pada bulan itu, dan bukan jawaban lain yang memperhitungkannya! Jika Anda ingin informasi lebih lanjut tentang masalah yang tidak ada dalam kerangka kerja, Anda dapat membaca posting ini: Objek Timespan Nyata Dengan .Years & .Months (namun, membaca posting itu tidak perlu untuk memahami dan menggunakan fungsi di bawah ini, ini bekerja 100%, tanpa ketidakakuratan bawaan dari perkiraan yang suka digunakan orang lain - dan jangan ragu untuk mengganti fungsi .ReverseIt dengan built-in. Fungsi reverse yang mungkin Anda miliki pada kerangka kerja Anda (hanya ada di sini untuk kelengkapan).

Harap dicatat bahwa Anda bisa mendapatkan sejumlah tanggal / waktu akurasi, detik & menit, atau detik, menit dan hari, di mana saja hingga bertahun-tahun (yang akan mengandung 6 bagian / segmen). Jika Anda menentukan dua teratas dan lebih dari satu tahun, itu akan mengembalikan "1 tahun dan 3 bulan yang lalu" dan tidak akan mengembalikan sisanya karena Anda telah meminta dua segmen. jika hanya beberapa jam, maka itu hanya akan mengembalikan "2 jam dan 1 menit yang lalu". Tentu saja, aturan yang sama berlaku jika Anda menentukan 1, 2, 3, 4, 5 atau 6 segmet (maksimal 6 karena detik, menit, jam, hari, bulan, tahun hanya membuat 6 jenis). Itu juga akan memperbaiki masalah grammer seperti "menit" vs "menit" tergantung pada apakah itu 1 menit atau lebih, sama untuk semua jenis, dan "string" yang dihasilkan akan selalu benar secara tata bahasa.

Berikut adalah beberapa contoh untuk digunakan: bAllowSegments mengidentifikasi berapa banyak segmen yang ditampilkan ... yaitu: jika 3, maka kembalikan string akan menjadi (sebagai contoh) ... "3 years, 2 months and 13 days"(tidak akan memasukkan jam, menit dan detik sebagai 3 waktu teratas) kategori dikembalikan), namun jika tanggalnya adalah tanggal yang lebih baru, seperti sesuatu beberapa hari yang lalu, tentukan segmen yang sama (3) yang akan dikembalikan "4 days, 1 hour and 13 minutes ago"sebagai gantinya, sehingga memperhitungkan semuanya!

jika bAllowSegments adalah 2 ia akan kembali "3 years and 2 months"dan jika 6 (nilai maksimum) akan kembali "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", tetapi, diingatkan bahwa itu akan menjadi NEVER RETURNsesuatu seperti ini "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"karena memahami tidak ada data tanggal di 3 segmen teratas dan mengabaikannya, bahkan jika Anda menentukan 6 segmen , jadi jangan khawatir :). Tentu saja, jika ada segmen dengan 0 di dalamnya, itu akan memperhitungkannya saat membentuk string, dan akan ditampilkan sebagai "3 days and 4 seconds ago"dan mengabaikan bagian "0 jam"! Nikmati dan berikan komentar jika Anda mau.

 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

Tentu saja, Anda akan memerlukan fungsi "ReplaceLast", yang mengambil string sumber, dan argumen yang menentukan apa yang perlu diganti, dan argumen lain yang menentukan dengan apa Anda ingin menggantinya, dan itu hanya menggantikan kejadian terakhir dari string itu. ... saya telah memasukkan yang saya miliki jika Anda tidak memilikinya atau tidak ingin mengimplementasikannya, jadi ini dia, itu akan berfungsi "sebagaimana adanya" tanpa modifikasi yang diperlukan. Saya tahu fungsi reverseit tidak lagi diperlukan (ada di .net) tetapi fungsi ReplaceLast dan ReverseIt dijalankan dari hari-hari pra-.net, jadi mohon maafkan bagaimana tanggalnya kelihatannya (masih berfungsi 100% tho, telah menggunakan em selama lebih dari sepuluh tahun, dapat menjamin mereka bebas bug) ... :). Bersulang.

<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

Jika Anda menginginkan angka pastinya, Anda tidak bisa hanya dari Timespan, karena Anda perlu tahu bulan apa yang Anda hadapi, dan apakah Anda berurusan dengan tahun kabisat, seperti yang Anda katakan.

Baik pergi untuk perkiraan angka, atau melakukan beberapa kegelisahan dengan DateTimes asli



0

Tidak ada cara dibangun untuk melakukan ini secara akurat dalam idiomatic-c #. Ada beberapa solusi, seperti contoh CodeProject ini yang telah dikodekan oleh orang.


0

Jika Anda berurusan dengan bulan dan tahun, Anda perlu sesuatu yang tahu berapa hari yang dimiliki setiap bulan dan tahun mana yang lebih cepat.

Masukkan Kalender Gregorian (dan implementasi Kalender khusus budaya lainnya ).

Meskipun Kalender tidak menyediakan metode untuk secara langsung menghitung perbedaan antara dua titik dalam waktu, itu memang memiliki metode seperti

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

Metode mengembalikan daftar yang berisi 3 elemen pertama adalah tahun, kedua adalah bulan dan elemen akhir adalah hari:

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

Inilah kontribusi saya untuk mendapatkan perbedaan dalam Bulan yang menurut saya akurat:

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

Pemakaian:

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

Anda dapat membuat metode lain yang disebut DiffYears dan menerapkan logika yang persis sama seperti di atas dan AddYears daripada AddMonths di loop sementara.


0

Jauh terlambat ke permainan tapi saya membayangkan ini mungkin bermanfaat bagi seseorang. Mayoritas orang cenderung mengukur bulan ke bulan berdasarkan tanggal, tidak termasuk fakta bahwa bulan datang dalam variasi yang berbeda. Dengan menggunakan kerangka pemikiran itu, saya membuat satu garis yang membandingkan tanggal untuk kami. Menggunakan proses berikut.

  1. Setiap # tahun ke atas 1 ketika membandingkan tahun akan dikalikan dengan 12, tidak ada kasus di mana ini bisa sama dengan kurang dari 1 tahun penuh.
  2. Jika akhir tahun lebih besar kita perlu mengevaluasi apakah hari ini lebih besar atau sama dengan hari 2A sebelumnya. Jika hari akhir lebih besar atau sama dengan kita ambil bulan saat ini dan kemudian tambahkan 12 bulan kurangi bulan bulan mulai 2B. Jika hari akhir kurang dari hari awal kami melakukan hal yang sama seperti di atas kecuali kami menambahkan 1 ke bulan awal sebelum mengurangi
  3. Jika akhir tahun tidak lebih besar kami melakukan yang sama dengan 2A / 2B, tetapi tanpa menambahkan 12 bulan karena kami tidak perlu mengevaluasi sekitar tahun.

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

Kematian oleh ternary?
SpaceBison

0

Pendapat saya tentang jawaban ini juga menggunakan metode ekstensi , tetapi dapat mengembalikan hasil positif atau negatif.

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

Beberapa tes:

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

Menggabungkan dua jawaban di atas, metode ekstensi lain adalah:

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

Terima kasih kepada @AdamRobinson dan @MarkWhittaker


-1

Hitung tidak ada bulan antara 2 tanggal:

$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
Ini PHP, bukan C #.
AFract
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.