iki tarih arasındaki ay farkının hesaplanması


128

C # '/. NET TimeSpanvardır TotalDays, TotalMinutesvb ama toplam aylık fark için bir formül bilemiyorum. Aylık değişken günler ve artık yıllar beni atmaya devam ediyor. TotalMonths'u nasıl edinebilirim ?

Edit Daha açık olmadığım için üzgünüm: Bunu gerçekten anlayamadığımı biliyorum TimeSpanama kullanmayı düşündüm TotalDaysve TotalMinutesaradığımı ifade etmek için iyi bir örnek olur ... Toplam Ayları almaya çalışıyorum dışında.

Örnek: 25 Aralık 2009 - 6 Ekim 2009 = 2 ToplamAy. 6 Ekim - 5 Kasım, 0 aya eşittir. 6 Kasım 1 ay. 6 Aralık 2 ay


2
25 Aralık 2009 - 6 Ekim 2009 için ne bekliyorsunuz?
Jeff Moser

2
TimeSpan'ı ay cinsinden nasıl tanımlarsınız?
Aliostad

1
@Aliostad - Tarihler olmadan bir ayı 30 gün olarak tanımlayabilir ve oldukça doğru olabilirsiniz.
ChaosPandion

Bu soru ile bazı nedenlerden dolayı bir mod ile birleştirildi.
Jamiec

Aslında, bu soruyu yanıtlayan ve kodlanmış bir çözüm sunan yazımı buradan okumanız gerekiyor, stackoverflow.com/questions/1916358/… trolleri (brianary) görmezden gelin ve supercat ile yapılan yorumlar aracılığıyla sohbetime dikkat edin. "Orphaned Months" olarak adlandırdığımız bir zaman aralığının başında ve sonunda olan aylar ve soru, bu öksüz ayları gün cinsinden nasıl tanımlayacağımızla ilgilidir - bunu belirledikten sonra (ve onu nasıl tanımlamak istediğinizi) ), geri kalanı sadece koddur (dahildir). Benim def. kullanıcılarımın bekleyeceğini düşündüğüm şeye dayanıyor
Erx_VB.NExT.Coder

Yanıtlar:


222

Bunu a'dan alamazsınız TimeSpançünkü "ay" değişken bir ölçü birimidir. Bunu kendiniz hesaplamanız gerekecek ve tam olarak nasıl çalışmasını istediğinizi bulmanız gerekecek.

Örneğin, tarihler bir ay mı yoksa sıfır ay mı gibi olmalı July 5, 2009ve vermeli August 4, 2009? Bir tane vermesi gerektiğini söylersen, peki ya July 31, 2009ve August 1, 2009? Mı böyle bir ay? Bu sadece Monthtarihlerin değerlerinin farkı mı yoksa gerçek bir zaman aralığı ile mi daha alakalı? Tüm bu kuralları belirleme mantığı önemsiz değildir, bu nedenle kendinizinkini belirlemeniz ve uygun algoritmayı uygulamanız gerekir.

Tek istediğiniz sadece aylar arasındaki bir farksa - tarih değerlerini tamamen göz ardı ederek - o zaman şunu kullanabilirsiniz:

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

Bunun göreceli bir fark döndürdüğünü, yani rValuedeğerinden büyükse lValuedönüş değerinin negatif olacağını unutmayın. Mutlak bir fark istiyorsanız, bunu kullanabilirsiniz:

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

@Dinah bu sadece bir tahmin, eğer doğru Ay ve Yılları bilmek istiyorsanız, okuyabileceğiniz bir cevap gönderdim. Yaklaşımlara gelince, bu iyi bir yaklaşım olsa da (Adam Robinson'a göre), ancak bu yaklaşımlardan herhangi birini kullanırsanız, kullanıcılarınıza istemeden yalan söylediğinizi unutmamalısınız.
Erx_VB.NExT.Coder

@ Erx_VB.NExT.Coder: Destekler için teşekkürler, ancak cevabınız yanıtların hiçbirinin bir ayın değişken ölçü birimi olmadığı gerçeğini dikkate almadığını belirtirken, çoğu öyle görünüyor; sadece sizin özel yaklaşımınızı kullanmazlar. Örnek olarak, cevabımdaki ilk cümle bunun değişken olduğunu gösteriyor. Herhangi cevap, sizinki dahil, yaklaşık bir değerdir kesin bir cevap değil çünkü. "2 aylık" sonucunuz, farklı girdiler için farklı anlamlar ifade edebilir, bu nedenle bu bir tahmindir.
Adam Robinson

Benimki ise bir tahmin değil, eğer bugün 14 Mart ise, o zaman iki önceki ay, Ocak'ta 31 gün ve Şubat'ta 29 gün olduğu gerçeğine göre hesaplanır. şimdi, benim yöntemimin "genel" ayın tanımı olmadığı ve sizinki olduğu konusunda haklısınız! Ancak, benimki yalnızca "Bu yorum x ay ve y gün ÖNCE gönderildi" gibi şeyler bildiriyorsanız geçerlidir, "AGO" bölümü fark yaratır, çünkü önceki x aya atıfta bulunulduğunda, bu önceki x ayların hesaplanması gerekir o x ayda kaç gün bulunduğuna göre! bağlantı ....
Erx_VB.NExT.Coder

bu mantıklı mı? Bu nedenle, belirli, bilinen aylardan bahsediyorsanız, benim yöntemim% 100 doğrudur ve bir tahmin olursunuz, ancak genel olarak bir aydan bahsediyorsanız, tahmininiz daha iyi bir fikir olur ve benimki sadece kötü bir fikir olur (bunun için yapılmaz ve onu kullanmanın bir anlamı olmaz). Sorunu açıklayan ve bir çözüm sunan makalemin bağlantısı: stackoverflow.com/questions/1916358/…
Erx_VB.NExT.Coder

2
Bu, Sql Server DateDiff (ay, ...) işlevi tarafından kullanılan mantıkla aynı görünüyor. Aynı zamanda son derece kısa ve açıklanması ve anlaşılması kolay olma avantajına da sahiptir. Bunu şu şekilde açıklayabilirim ... Bir tarihten diğerine gitmek için takvimde kaç sayfa çevirmeniz gerekir?
JoelFan

51

(Bunun eski bir soru olduğunun farkındayım, ama ...)

Bu, saf .NET'te yapılması nispeten zahmetlidir. Özellikle bunun gibi şeyler için tasarlanmış kendi Noda Time kitaplığımı tavsiye ederim :

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

(Başka seçenekler de vardır, örneğin yıllar geçse bile yalnızca birkaç ay istiyorsanız, kullanırsınız Period period = Period.Between(start, end, PeriodUnits.Months);)


Kitaplığınızı indirdim ve yukarıda yazdığınız kodu kopyaladım, ancak derleme zamanı hatası alıyorum. Hata 1 Operatörü '-', 'NodaTime.LocalDate' ve 'NodaTime.LocalDate' türündeki işlenenlere uygulanamaz. Bu yazıyı 5 yıldan beri biliyorum, o zamandan beri bu kodun çalışmamasına neden olan herhangi bir şey değişti mi?
Hakan Fıstık

1
@HakamFostok: Üzgünüm - 2.0 yayınlandığında çalışacak, ancak o zamana kadar kullanmanız gerekiyor Period.Between. Kodu, NodaTime 1.3.1 ile çalışacak şekilde düzenlediniz.
Jon Skeet

çok teşekkürler NodaTime kütüphanesi tam olarak yapmak istediğimi yaptı. Sadece iki tarih arasındaki ayları değil, kalan günleri de hesaplamak istedim ve NodaTime'ın yaptığı tam olarak bu, tekrar teşekkürler.
Hakan Fıstık

1
@JonSkeet Bu kitaplığınız gerçekten kara büyü. Tarihler beni her zaman ısırır. Bu kod parçası bana çok zaman kazandırdı.
onefoot18

28

Belki ay kesirlerini bilmek istemezsiniz; Peki ya bu kod?


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'ü anlamıyorum. * 12 mi olmalı?
Ruffles

9

Başlamak için TotalMonths ile ne demek istediğinizi tanımlamanız gerekecek.
Basit bir tanım, bir ayı 30,4 gün (365,25 / 12) olarak belirler.

Bunun ötesinde, kesirler de dahil olmak üzere herhangi bir tanım işe yaramaz ve daha yaygın olan tamsayı değeri (tarihler arasındaki tam aylar) da standart olmayan iş kurallarına bağlıdır.


9

Üzerine çok basit bir uzatma yöntemi yazdım DateTimeve DateTimeOffsetbunu yapmak için. Tam olarak bir TotalMonthsmülkün çalışacağı gibi çalışmasını istedim TimeSpan: yani, kısmi ayları göz ardı ederek iki tarih arasındaki tam ayların sayısını iade edin. Çünkü buna dayanıyor çünkü DateTime.AddMonths()farklı ay uzunluklarına saygı duyuyor ve bir insanın aylar olarak anlayacağı şeyi geri getiriyor.

(Maalesef bunu TimeSpan üzerinde bir uzatma yöntemi olarak uygulayamazsınız çünkü bu kullanılan gerçek tarihlerin bilgisini korumaz ve aylarca önemlidir.)

Kod ve testler GitHub'da mevcuttur . Kod çok basit:

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

Ve tüm bu birim test durumlarını geçer:

// 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
Rustik ama en iyi çözüm. Kopyalar ve yapıştırılır. Teşekkür ederim
Daniel Dolz

8

Randevu saatlerinde bunu kendiniz çözmeniz gerekir. Sonunda koçan günleriyle nasıl başa çıkacağınız, onu ne için kullanmak istediğinize bağlı olacaktır.

Bir yöntem ayı saymak ve ardından sondaki günleri düzeltmektir. Gibi bir şey:

   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;

Güzel kod olsa da, 1 hata: onun yerine: (28 Şubat + 1 ay == 28 Mart) :-) // ondalık günInEndMonth = (end - end.AddMonths (1)). Days; Öneririm: decimal daysInEndMonth = DateTime.DaysInMonth (end.Year, end.Month) * -1;
bezieur

3

Bunu şöyle yapardım:

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
Bu kesinlikle bir algoritmadır, ancak büyük ölçüde basitleştirilebilirreturn (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
Adam Robinson

1
İki sorun: TimeSpan değil, 2 Dates'ten başlıyorsunuz. İkinci olarak, her iki ayın 1'i arasında hesap yaparsınız, bu çok şüpheli bir tanımdır. Bazen doğru olsa da.
Henk Holterman

@Henk: Evet, elbette bu her zaman doğru değil, bu yüzden ben böyle yaparım dedim, kimse nasıl yapmalı değil. OP, sonucun nasıl hesaplanacağını belirtmedi. @Adam: Vay canına, yine çok karmaşık olduğunu düşündüm ... bu bana çok sık oluyor. Yorumunuz için teşekkürler, kesinlikle haklısınız, sürümünüz çok daha iyi. Bundan sonra bunu kullanacağım.
Maximilian Mayerl

@Adam: Neden bunu gerçek bir cevap olarak göndermiyorsun ?! Bu şimdiye kadarki en kompakt olanı. Çok şık.
Dinah

@Dinah: Aslında istediğinin bu olduğunu varsaymak istemedim. Öyleyse, önceki cevabımı bu yaklaşımı içerecek şekilde düzenledim.
Adam Robinson

3

Bu konuda çok fazla net cevap yok çünkü her zaman bir şeyleri varsayıyorsunuz.

Bu çözüm, karşılaştırma için ayın gününü kaydetmek istediğinizi varsayarak iki tarih arasındaki ayları hesaplar (bu, hesaplamada ayın gününün dikkate alındığı anlamına gelir)

Örneğin, 30 Ocak 2012 tarihiniz varsa, 29 Şubat 2012 bir ay değil, 01 Mart 2013 olacaktır.

Oldukça kapsamlı bir şekilde test edildi, muhtemelen kullandığımızda daha sonra temizleyecek ve bir Timespan yerine iki tarih alacak ki bu muhtemelen daha iyi. Umarım bu başkalarına yardımcı olur.

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

Kabul edilen cevap, tam ay istediğinizde mükemmel çalışır.

Kısmi aylara ihtiyacım vardı. Kısmi aylarda bulduğum çözüm bu:

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

Ayrıca kısmi yıllar için aynı ihtiyaçla bir yıl farkına ihtiyacım vardı. İşte bulduğum çözüm:

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

YearDifferenceFonksiyonunuzda mantık hatası vardı lValue.Month < rValue.Month- Bunu şimdi düzelttim, gözden geçirmek isteyebilirsiniz ...
Stobor

2

Bildiğim eski soru ama birine yardımcı olabilir. Yukarıda @Adam kabul edilen cevabı kullandım, ancak farkın 1 mi yoksa -1 mi olduğunu kontrol ettim ve tam bir takvim ayı farkı olup olmadığını kontrol et. Yani 21/07/55 ve 20/08/55 tam bir ay olmayacak, 21/07/55 ve 21/07/55 olacaktır.

/// <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
Kodla birlikte yapılacak bir açıklama diğer okuyucular için de faydalı olacaktır.
Boeckm

evet lütfen biraz yorum ekleyin.
Amar

1

Aylarla ilgili sorun, bunun gerçekten basit bir ölçü olmaması - sabit boyutta olmamasıdır. Dahil etmek istedikleriniz için kurallarınızı belirlemeniz ve oradan çalışmanız gerekir. Örneğin 1 Ocak - 1 Şubat - oraya 2 ay dahil olduğunu iddia edebilirsiniz veya bunun bir ay olduğunu söyleyebilirsiniz. Peki ya "1 Ocak 20:00" ile "1 Şubat 00:00" arası - bu tam bir ay değil. Bu 0 mı? 1? peki ya tam tersi (1 Ocak 00:00 - 1 Şubat 20:00) ... 1? 2?

Önce kuralları tanımlayın, sonra kendiniz kodlamanız gerekecek, korkarım ...


1

Ve 1arasında bir sonuç almak istiyorsanız : 28th Feb1st March

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

Bu, Sql Server DateDiff (ay, ...) işlevi tarafından kullanılan mantıkla aynı görünüyor. Ayrıca, son derece kısa ve açıklanması ve anlaşılması kolay olma avantajına da sahiptir. Bunu şu şekilde açıklayabilirim ... Bir tarihten diğerine gitmek için takvimde kaç sayfa çevirmeniz gerekir?
JoelFan

1

Bu kitaplık, DateTime'ın tüm bölümlerini göz önünde bulundurarak ayların farkını hesaplar:

// ----------------------------------------------------------------------
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 Ay" tanımı, hangi ay olduğuna göre değiştiğinden ve diğer cevapların hiçbiri bunu hesaba katmadığı için, bunu yapmanın en doğru yolu aslında aşağıdadır! Çerçeveye dahil edilmeyen konu hakkında daha fazla bilgi istiyorsanız, şu yazıyı okuyabilirsiniz: .Years & .Months ile Gerçek Zaman Aralığı Nesnesi (ancak, aşağıdaki işlevi anlamak ve kullanmak için bu yazıyı okumak gerekli değildir, diğerlerinin kullanmayı sevdiği yaklaşımın doğasında var olan yanlışlıklar olmadan% 100 çalışır - ve .ReverseIt işlevini yerleşik .Reverse işleviyle değiştirmekten çekinmeyin (tamlık için burada).

Lütfen, yıllara kadar herhangi bir yerde tarih / saat doğruluğu, saniye ve dakika veya saniye, dakika ve gün (6 parça / segment içerir) alabileceğinizi unutmayın. İlk ikiyi belirtirseniz ve bir yıldan eskiyse, "1 yıl ve 3 ay önce" döndürür ve geri kalanını iki segment talep ettiğiniz için iade etmez. yalnızca birkaç saatlikse, yalnızca "2 saat ve 1 dakika önce" döndürülür. Tabii ki, 1, 2, 3, 4, 5 veya 6 segment belirtirseniz aynı kurallar geçerlidir (6'da maksimuma çıkar çünkü saniye, dakika, saat, gün, ay, yıl sadece 6 tür yapar). Ayrıca 1 dakika veya daha fazla olmasına bağlı olarak "dakika" ve "dakika" gibi gramer sorunlarını düzeltir, tüm türler için aynıdır ve oluşturulan "dize" her zaman dilbilgisi açısından doğru olacaktır.

İşte kullanım için bazı örnekler: bAllowSegments, kaç segmentin gösterileceğini tanımlar ... yani: 3 ise, dönüş dizesi (örnek olarak) "3 years, 2 months and 13 days"olacaktır ... (ilk 3 kez saat, dakika ve saniye içermez kategoriler döndürülür), ancak tarih birkaç gün önceki gibi daha yeni bir tarihse, "4 days, 1 hour and 13 minutes ago"bunun yerine aynı segmentleri (3) belirtmek geri dönecektir , bu nedenle her şeyi hesaba katar!

bAllowSegments 2 ise, geri döner "3 years and 2 months"ve 6 (maksimum değer) dönerse "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds", ancak ilk 3 segmentte tarih verisi olmadığını anladığı ve 6 segment belirleseniz bile bunları yok saydığı için bunun NEVER RETURNgibi bir şey olacağı hatırlatılır. "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago", bu yüzden endişelenme :). Elbette, içinde 0 olan bir segment varsa, dizeyi oluştururken bunu dikkate alacak "3 days and 4 seconds ago"ve "0 saat" kısmı olarak ve yok sayarak gösterecektir ! Keyfini çıkarın ve isterseniz yorum yapın.

 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

Elbette, bir kaynak dizeyi alan bir "ReplaceLast" işlevine ve neyin değiştirilmesi gerektiğini belirten bir bağımsız değişkene ve onu neyle değiştirmek istediğinizi belirten başka bir bağımsız değişkene ihtiyacınız olacak ve yalnızca o dizenin son geçtiği yeri değiştirecek ... eğer sahip değilseniz veya uygulamak istemiyorsanız benimkini dahil ettim, işte burada, hiçbir değişiklik gerekmeden "olduğu gibi" çalışacaktır. Tersine çevirme işlevine artık gerek olmadığını biliyorum (.net'te var), ancak ReplaceLast ve ReverseIt işlevi ön-net günlerden taşınıyor, bu yüzden lütfen nasıl eski göründüğünü özür dilerim (hala% 100 çalışıyor, kullanıyor em, on yıldan fazla bir süredir hatasız olduklarını garanti edebilirler) ... :). şerefe.

<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

Tam sayıyı istiyorsanız, sadece Timespan'dan yapamazsınız, çünkü hangi aylarla uğraştığınızı ve dediğiniz gibi artık bir yılla mı uğraştığınızı bilmeniz gerekir.

Ya yaklaşık bir sayı için gidin ya da orijinal DateTimes ile biraz kıpırdama yapın



0

Bunu idiomatic-c # ile doğru bir şekilde yapmanın bir yolu yoktur. Yine de insanların kodladığı bu CodeProject örneği gibi bazı geçici çözümler vardır .


0

Aylar ve yıllarla uğraşıyorsanız, her ayın kaç günü olduğunu ve hangi yılların artık yıl olduğunu bilen bir şeye ihtiyacınız vardır.

Enter Gregoryen Takvim (ve diğer kültür özgü Takvim uygulamalarını).

Takvim, zamandaki iki nokta arasındaki farkı doğrudan hesaplamak için yöntemler sağlamazken, aşağıdaki gibi yöntemleri vardır:

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

Yöntem, ilk olarak yıl, ikincisi ay ve bitiş öğesi gün olmak üzere 3 öğe içeren bir liste döndürür:

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

İşte doğru bulduğum Aylar arasındaki farkı elde etmedeki katkım:

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

Kullanımı:

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

DiffYears adında başka bir yöntem oluşturabilir ve while döngüsünde yukarıdaki ile aynı mantığı ve AddMonths yerine AddYears'ı uygulayabilirsiniz.


0

Oyuna çok geç kaldım ama bunun birine yardımcı olabileceğini düşünüyorum. İnsanların çoğu, ayların farklı varyasyonlarda olduğu gerçeğini hariç tutarak, aydan aya ölçüm yapma eğilimindedir. Bu düşünce çerçevesini kullanarak, bizim için tarihleri ​​karşılaştıran tek bir satır oluşturdum. Aşağıdaki işlemi kullanarak.

  1. Yıl karşılaştırılırken 1'in üzerindeki herhangi bir yıl sayısı 12 ile çarpılacaktır, bunun 1 tam yıldan az olabileceği bir durum yoktur.
  2. Yıl sonu daha büyükse, mevcut günün önceki 2A gününden daha büyük veya ona eşit olup olmadığını değerlendirmemiz gerekir. Bitiş günü daha büyük veya eşitse, içinde bulunulan ayı alırız ve ardından 12 ay ekleriz, başlangıç ​​ayının ayını 2B çıkarırız. Bitiş günü başlangıç ​​gününden daha azsa, çıkarma işleminden önce başlangıç ​​ayına 1 eklememiz dışında yukarıdakinin aynısını gerçekleştiririz
  3. Yıl sonu daha büyük değilse, 2A / 2B ile aynı şeyi yapıyoruz, ancak 12 ayı eklemeden, çünkü yıl boyunca değerlendirmemize gerek yok.

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

Üçlü ölüm mü?
SpaceBison

0

Bu yanıtı benimsem de bir uzatma yöntemi kullanıyor , ancak olumlu veya olumsuz bir sonuç döndürebilir.

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

Birkaç test:

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

Yukarıdaki cevaplardan ikisini birleştirirsek, başka bir uzatma yöntemi şudur:

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 ve @MarkWhittaker'a teşekkürler


-1

2 tarih arasındaki ay sayısını hesaplayın:

$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
Bu PHP, C # değil.
AFract
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.