Разница в месяцах между двумя датами
Как рассчитать разницу в месяцах между двумя датами в C#?
Есть ли эквивалент VB DateDiff()
метод в C#. Мне нужно найти разницу в месяцах между двумя датами, которые разделены годами. В документации сказано, что я могу использовать TimeSpan
лайк:
TimeSpan ts = date1 - date2;
но это дает мне данные в днях. Я не хочу делить это число на 30, потому что не каждый месяц равен 30 дням, и так как два значения операндов довольно сильно отличаются друг от друга, я боюсь, что деление на 30 может дать мне неправильное значение.
Какие-либо предложения?
45 ответов
Предполагая, что день месяца не имеет значения (т. Е. Разница между 2011.1.1 и 2010.12.31 равна 1), где date1 > date2 дает положительное значение, а date2> date1 отрицательное значение
((date1.Year - date2.Year) * 12) + date1.Month - date2.Month
Или, если вы хотите приблизительное количество "средних месяцев" между двумя датами, следующее должно работать для всех, кроме очень огромных разниц в датах.
date1.Subtract(date2).Days / (365.25 / 12)
Обратите внимание: если вы должны использовать последнее решение, тогда в ваших модульных тестах должен быть указан самый широкий диапазон дат, с которым ваше приложение предназначено для работы, и соответствующим образом подтверждать результаты расчета.
Обновление (с благодарностью Gary)
При использовании метода "средних месяцев" чуть более точное число для "среднего числа дней в году" составляет 365,2425.
Вот комплексное решение для возврата DateTimeSpan
, похожий на TimeSpan
за исключением того, что он включает все компоненты даты в дополнение к компонентам времени.
Использование:
void Main()
{
DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
var dateSpan = DateTimeSpan.CompareDates(compareTo, now);
Console.WriteLine("Years: " + dateSpan.Years);
Console.WriteLine("Months: " + dateSpan.Months);
Console.WriteLine("Days: " + dateSpan.Days);
Console.WriteLine("Hours: " + dateSpan.Hours);
Console.WriteLine("Minutes: " + dateSpan.Minutes);
Console.WriteLine("Seconds: " + dateSpan.Seconds);
Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}
Выходы:
Лет: 1
Месяцев: 5
Дней: 27
Часы работы: 1
Минут: 36
Секунд: 50
Миллисекунды: 0
Для удобства я сосредоточил логику в DateTimeSpan
структура, но вы можете переместить метод CompareDates
везде, где вы считаете нужным. Также обратите внимание, что не имеет значения, какая дата предшествует другой.
public struct DateTimeSpan
{
private readonly int years;
private readonly int months;
private readonly int days;
private readonly int hours;
private readonly int minutes;
private readonly int seconds;
private readonly int milliseconds;
public DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
{
this.years = years;
this.months = months;
this.days = days;
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
this.milliseconds = milliseconds;
}
public int Years { get { return years; } }
public int Months { get { return months; } }
public int Days { get { return days; } }
public int Hours { get { return hours; } }
public int Minutes { get { return minutes; } }
public int Seconds { get { return seconds; } }
public int Milliseconds { get { return milliseconds; } }
enum Phase { Years, Months, Days, Done }
public static DateTimeSpan CompareDates(DateTime date1, DateTime date2)
{
if (date2 < date1)
{
var sub = date1;
date1 = date2;
date2 = sub;
}
DateTime current = date1;
int years = 0;
int months = 0;
int days = 0;
Phase phase = Phase.Years;
DateTimeSpan span = new DateTimeSpan();
int officialDay = current.Day;
while (phase != Phase.Done)
{
switch (phase)
{
case Phase.Years:
if (current.AddYears(years + 1) > date2)
{
phase = Phase.Months;
current = current.AddYears(years);
}
else
{
years++;
}
break;
case Phase.Months:
if (current.AddMonths(months + 1) > date2)
{
phase = Phase.Days;
current = current.AddMonths(months);
if (current.Day < officialDay && officialDay <= DateTime.DaysInMonth(current.Year, current.Month))
current = current.AddDays(officialDay - current.Day);
}
else
{
months++;
}
break;
case Phase.Days:
if (current.AddDays(days + 1) > date2)
{
current = current.AddDays(days);
var timespan = date2 - current;
span = new DateTimeSpan(years, months, days, timespan.Hours, timespan.Minutes, timespan.Seconds, timespan.Milliseconds);
phase = Phase.Done;
}
else
{
days++;
}
break;
}
}
return span;
}
}
Если вам нужно точное число полных месяцев, всегда положительное (2000-01-15, 2000-02-14 возвращает 0), учитывая, что полный месяц - это когда вы достигаете того же дня в следующем месяце (что-то вроде расчета возраста)
public static int GetMonthsBetween(DateTime from, DateTime to)
{
if (from > to) return GetMonthsBetween(to, from);
var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));
if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
{
return monthDiff - 1;
}
else
{
return monthDiff;
}
}
Изменить причину: старый код был неправильным в некоторых случаях, таких как:
new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },
Test cases I used to test the function:
var tests = new[]
{
new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 1), Result = 0 },
new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 1, 2), Result = 0 },
new { From = new DateTime(1900, 1, 2), To = new DateTime(1900, 1, 1), Result = 0 },
new { From = new DateTime(1900, 1, 1), To = new DateTime(1900, 2, 1), Result = 1 },
new { From = new DateTime(1900, 2, 1), To = new DateTime(1900, 1, 1), Result = 1 },
new { From = new DateTime(1900, 1, 31), To = new DateTime(1900, 2, 1), Result = 0 },
new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 9, 30), Result = 0 },
new { From = new DateTime(1900, 8, 31), To = new DateTime(1900, 10, 1), Result = 1 },
new { From = new DateTime(1900, 1, 1), To = new DateTime(1901, 1, 1), Result = 12 },
new { From = new DateTime(1900, 1, 1), To = new DateTime(1911, 1, 1), Result = 132 },
new { From = new DateTime(1900, 8, 31), To = new DateTime(1901, 8, 30), Result = 11 },
};
Я проверил использование этого метода в VB.NET через MSDN, и кажется, что он имеет много применений. В C# такого встроенного метода нет. (Даже если это не очень хорошая идея) вы можете вызывать VB в C#.
- добавлять
Microsoft.VisualBasic.dll
к вашему проекту в качестве ссылки - использование
Microsoft.VisualBasic.DateAndTime.DateDiff
в вашем коде
Используйте время Noda:
LocalDate start = new LocalDate(2013, 1, 5);
LocalDate end = new LocalDate(2014, 6, 1);
Period period = Period.Between(start, end, PeriodUnits.Months);
Console.WriteLine(period.Months); // 16
Чтобы получить разницу в месяцах (включая начало и конец включительно), независимо от дат:
DateTime start = new DateTime(2013, 1, 1);
DateTime end = new DateTime(2014, 2, 1);
var diffMonths = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
Мне просто нужно было что-то простое, чтобы обслужить, например, даты трудоустройства, где вводится только месяц / год, поэтому хотел, чтобы работали разные годы и месяцы. Это то, что я использую, здесь только для полезности
public static YearsMonths YearMonthDiff(DateTime startDate, DateTime endDate) {
int monthDiff = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month) + 1;
int years = (int)Math.Floor((decimal) (monthDiff / 12));
int months = monthDiff % 12;
return new YearsMonths {
TotalMonths = monthDiff,
Years = years,
Months = months
};
}
Вот мой вклад, чтобы получить разницу в месяцах, которые я считаю точными:
namespace System
{
public static class DateTimeExtensions
{
public static Int32 DiffMonths( this DateTime start, DateTime end )
{
Int32 months = 0;
DateTime tmp = start;
while ( tmp < end )
{
months++;
tmp = tmp.AddMonths( 1 );
}
return months;
}
}
}
Использование:
Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );
Вы можете создать другой метод с именем DiffYears и применить точно такую же логику, что и выше, и AddYears вместо AddMonths в цикле while.
Вы можете использовать Noda Time https://nodatime.org/
LocalDate start = new LocalDate(2010, 1, 5);
LocalDate end = new LocalDate(2012, 6, 1);
Period period = Period.Between(start, end, PeriodUnits.Months);
Console.WriteLine(period.Months);
Вы можете использовать класс DateDiff библиотеки периодов времени для.NET:
// ----------------------------------------------------------------------
public void DateDiffSample()
{
DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
DateDiff dateDiff = new DateDiff( date1, date2 );
// differences
Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
// > DateDiff.Months: 16
// elapsed
Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
// > DateDiff.ElapsedMonths: 4
// description
Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
// > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample
Мое понимание общей разницы в месяцах между двумя датами имеет неотъемлемую и дробную части (дата имеет значение).
Неотъемлемой частью является полная месячная разница.
Дробная часть для меня - это разница в процентах от дня (до полных дней месяца) между начальным и конечным месяцами.
public static class DateTimeExtensions
{
public static double TotalMonthsDifference(this DateTime from, DateTime to)
{
//Compute full months difference between dates
var fullMonthsDiff = (to.Year - from.Year)*12 + to.Month - from.Month;
//Compute difference between the % of day to full days of each month
var fractionMonthsDiff = ((double)(to.Day-1) / (DateTime.DaysInMonth(to.Year, to.Month)-1)) -
((double)(from.Day-1)/ (DateTime.DaysInMonth(from.Year, from.Month)-1));
return fullMonthsDiff + fractionMonthsDiff;
}
}
С этим расширением таковы результаты:
2/29/2000 TotalMonthsDifference 2/28/2001 => 12
2/28/2000 TotalMonthsDifference 2/28/2001 => 12.035714285714286
01/01/2000 TotalMonthsDifference 01/16/2000 => 0.5
01/31/2000 TotalMonthsDifference 01/01/2000 => -1.0
01/31/2000 TotalMonthsDifference 02/29/2000 => 1.0
01/31/2000 TotalMonthsDifference 02/28/2000 => 0.9642857142857143
01/31/2001 TotalMonthsDifference 02/28/2001 => 1.0
Эта простая статическая функция вычисляет долю месяцев между двумя датами, например
- 1.1. до 31.1. = 1.0
- 1.4. до 15.4. = 0,5
- 16.4. до 30,4. = 0,5
- 1.3. до 1.4. = 1 + 1/30
Функция предполагает, что первая дата меньше второй даты. Чтобы иметь дело с отрицательными временными интервалами, можно легко изменить функцию, введя знак и замену переменной в начале.
public static double GetDeltaMonths(DateTime t0, DateTime t1)
{
DateTime t = t0;
double months = 0;
while(t<=t1)
{
int daysInMonth = DateTime.DaysInMonth(t.Year, t.Month);
DateTime endOfMonth = new DateTime(t.Year, t.Month, daysInMonth);
int cutDay = endOfMonth <= t1 ? daysInMonth : t1.Day;
months += (cutDay - t.Day + 1) / (double) daysInMonth;
t = new DateTime(t.Year, t.Month, 1).AddMonths(1);
}
return Math.Round(months,2);
}
Я работал над проектом, который растянулся на годы и месяцы. Вот решение, которое может помочь.
/// <summary>
/// Get the total months between two date. This will count whole months and not care about the day.
/// </summary>
/// <param name="firstDate">First date.</param>
/// <param name="lastDate">Last date.</param>
/// <returns>Number of month apart.</returns>
private static int GetTotalMonths(DateOnly firstDate, DateOnly lastDate)
{
int yearsAppart = lastDate.Year - firstDate.Year;
int monthsAppart = lastDate.Month - firstDate.Month;
return (yearsAppart * 12) + monthsAppart;
}
private static int GetTotalMonths(DateTime firstDate, DateTime lastDate)
{
return GetTotalMonths(DateOnly.FromDateTime(firstDate), DateOnly.FromDateTime(lastDate));
}
Наиболее точный способ - это вернуть разницу в месяцах по доле:
private double ReturnDiffereceBetweenTwoDatesInMonths(DateTime startDateTime, DateTime endDateTime)
{
double result = 0;
double days = 0;
DateTime currentDateTime = startDateTime;
while (endDateTime > currentDateTime.AddMonths(1))
{
result ++;
currentDateTime = currentDateTime.AddMonths(1);
}
if (endDateTime > currentDateTime)
{
days = endDateTime.Subtract(currentDateTime).TotalDays;
}
return result + days/endDateTime.GetMonthDays;
}
Есть 3 случая: тот же год, предыдущий год и другие годы.
Если день месяца не имеет значения...
public int GetTotalNumberOfMonths(DateTime start, DateTime end)
{
// work with dates in the right order
if (start > end)
{
var swapper = start;
start = end;
end = swapper;
}
switch (end.Year - start.Year)
{
case 0: // Same year
return end.Month - start.Month;
case 1: // last year
return (12 - start.Month) + end.Month;
default:
return 12 * (3 - (end.Year - start.Year)) + (12 - start.Month) + end.Month;
}
}
Это сработало для того, что мне было нужно. В моем случае день месяца не имел значения, потому что это всегда последний день месяца.
public static int MonthDiff(DateTime d1, DateTime d2){
int retVal = 0;
if (d1.Month<d2.Month)
{
retVal = (d1.Month + 12) - d2.Month;
retVal += ((d1.Year - 1) - d2.Year)*12;
}
else
{
retVal = d1.Month - d2.Month;
retVal += (d1.Year - d2.Year)*12;
}
//// Calculate the number of years represented and multiply by 12
//// Substract the month number from the total
//// Substract the difference of the second month and 12 from the total
//retVal = (d1.Year - d2.Year) * 12;
//retVal = retVal - d1.Month;
//retVal = retVal - (12 - d2.Month);
return retVal;
}
однострочное решение
Во-первых, проверьте, относятся ли обе даты к текущему году, если нет месяцев целых лет, а затем добавьте месяцы с начала и конца года.
DateTime dateFrom = new DateTime(2019, 2, 1);
DateTime dateTo = new DateTime(2021, 5, 25);
С первого месяца
var monthCount = dateFrom.Year != dateTo.Year ? ((dateTo.Year - dateFrom.Year - 1) * 12) + (13 - dateFrom.Month + dateTo.Month) : dateTo.Month - dateFrom.Month + 1;
результат = 28
Без первого месяца
monthCount = dateFrom.Year != dateTo.Year ? ((dateTo.Year - dateFrom.Year - 1) * 12) + (12 - dateFrom.Month + dateTo.Month) : dateTo.Month - dateFrom.Month;
результат = 27
Вот простое решение, которое работает по крайней мере для меня. Это, вероятно, не самый быстрый, потому что он использует классную функцию AddTon th DateTime в цикле:
public static int GetMonthsDiff(DateTime start, DateTime end)
{
if (start > end)
return GetMonthsDiff(end, start);
int months = 0;
do
{
start = start.AddMonths(1);
if (start > end)
return months;
months++;
}
while (true);
}
На этот вопрос не так много четких ответов, потому что вы всегда предполагаете что-то.
Это решение вычисляет между двумя датами месяцы между ними, предполагая, что вы хотите сохранить день месяца для сравнения (это означает, что день месяца учитывается при расчете)
Например, если у вас дата 30 января 2012 года, 29 февраля 2012 года будет не месяцем, а 01 марта 2013 года.
Он был довольно тщательно протестирован, возможно, позже мы его очистим, но здесь:
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
}
Безумный метод, который считает все дни, такой сверхточный
вспомогательный класс:
public class DaysInMonth
{
public int Days { get; set; }
public int Month { get; set; }
public int Year { get; set; }
public bool Full { get; set; }
}
функция:
public static List<DaysInMonth> MonthsDelta(DateTime start, DateTime end)
{
var dates = Enumerable.Range(0, 1 + end.Subtract(start).Days)
.Select(offset => start.AddDays(offset))
.ToArray();
DateTime? prev = null;
int days = 0;
List < DaysInMonth > list = new List<DaysInMonth>();
foreach (DateTime date in dates)
{
if (prev != null)
{
if(date.Month!=prev.GetValueOrDefault().Month)
{
DaysInMonth daysInMonth = new DaysInMonth();
daysInMonth.Days = days;
daysInMonth.Month = prev.GetValueOrDefault().Month;
daysInMonth.Year = prev.GetValueOrDefault().Year;
daysInMonth.Full = DateTime.DaysInMonth(daysInMonth.Year, daysInMonth.Month) == daysInMonth.Days;
list.Add(daysInMonth);
days = 0;
}
}
days++;
prev = date;
}
//------------------ add last
if (days > 0)
{
DaysInMonth daysInMonth = new DaysInMonth();
daysInMonth.Days = days;
daysInMonth.Month = prev.GetValueOrDefault().Month;
daysInMonth.Year = prev.GetValueOrDefault().Year;
daysInMonth.Full = DateTime.DaysInMonth(daysInMonth.Year, daysInMonth.Month) == daysInMonth.Days;
list.Add(daysInMonth);
}
return list;
}
Вы можете иметь функцию примерно так.
Например, с 2012/12/27 по 2012/12/29 становится 3 дня. Аналогичным образом, с 2012/12/15 по 2013/15/15 становится 2 месяца, потому что до 2013/01/14 это 1 месяц. с 15го месяца начался 2й месяц
Вы можете удалить "=" во втором условии if, если вы не хотите включать в расчет оба дня. т.е. с 2012/12/15 по 2013/15/15 составляет 1 месяц.
public int GetMonths(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
{
throw new Exception("Start Date is greater than the End Date");
}
int months = ((endDate.Year * 12) + endDate.Month) - ((startDate.Year * 12) + startDate.Month);
if (endDate.Day >= startDate.Day)
{
months++;
}
return months;
}
Наверняка кто-то это сделал))
Метод расширения возвращает количество полных месяцев между заданными датами. В каком бы порядке ни были получены даты, всегда будет возвращено натуральное число. Никаких приблизительных расчетов, как в «правильном» ответе.
/// <summary>
/// Returns the difference between dates in months.
/// </summary>
/// <param name="current">First considered date.</param>
/// <param name="another">Second considered date.</param>
/// <returns>The number of full months between the given dates.</returns>
public static int DifferenceInMonths(this DateTime current, DateTime another)
{
DateTime previous, next;
if (current > another)
{
previous = another;
next = current;
}
else
{
previous = current;
next = another;
}
return
(next.Year - previous.Year) * 12 // multiply the difference in years by 12 months
+ next.Month - previous.Month // add difference in months
+ (previous.Day <= next.Day ? 0 : -1); // if the day of the next date has not reached the day of the previous one, then the last month has not yet ended
}
Но если вы все же хотите получить дробные части месяцев, вам просто нужно добавить еще одно слагаемое в возвращаемое значение:
+ (next.Day - previous.Day) / DateTime.DaysInMonth(previous.Year, previous.Month)
Основываясь на превосходной работе DateTimeSpan, проделанной выше, я немного нормализовал код; это, кажется, работает довольно хорошо:
public class DateTimeSpan
{
private DateTimeSpan() { }
private DateTimeSpan(int years, int months, int days, int hours, int minutes, int seconds, int milliseconds)
{
Years = years;
Months = months;
Days = days;
Hours = hours;
Minutes = minutes;
Seconds = seconds;
Milliseconds = milliseconds;
}
public int Years { get; private set; } = 0;
public int Months { get; private set; } = 0;
public int Days { get; private set; } = 0;
public int Hours { get; private set; } = 0;
public int Minutes { get; private set; } = 0;
public int Seconds { get; private set; } = 0;
public int Milliseconds { get; private set; } = 0;
public static DateTimeSpan CompareDates(DateTime StartDate, DateTime EndDate)
{
if (StartDate.Equals(EndDate)) return new DateTimeSpan();
DateTimeSpan R = new DateTimeSpan();
bool Later;
if (Later = StartDate > EndDate)
{
DateTime D = StartDate;
StartDate = EndDate;
EndDate = D;
}
// Calculate Date Stuff
for (DateTime D = StartDate.AddYears(1); D < EndDate; D = D.AddYears(1), R.Years++) ;
if (R.Years > 0) StartDate = StartDate.AddYears(R.Years);
for (DateTime D = StartDate.AddMonths(1); D < EndDate; D = D.AddMonths(1), R.Months++) ;
if (R.Months > 0) StartDate = StartDate.AddMonths(R.Months);
for (DateTime D = StartDate.AddDays(1); D < EndDate; D = D.AddDays(1), R.Days++) ;
if (R.Days > 0) StartDate = StartDate.AddDays(R.Days);
// Calculate Time Stuff
TimeSpan T1 = EndDate - StartDate;
R.Hours = T1.Hours;
R.Minutes = T1.Minutes;
R.Seconds = T1.Seconds;
R.Milliseconds = T1.Milliseconds;
// Return answer. Negate values if the Start Date was later than the End Date
if (Later)
return new DateTimeSpan(-R.Years, -R.Months, -R.Days, -R.Hours, -R.Minutes, -R.Seconds, -R.Milliseconds);
return R;
}
}
Это из моей собственной библиотеки, вернет разницу месяцев между двумя датами.
public static int MonthDiff(DateTime d1, DateTime d2)
{
int retVal = 0;
// Calculate the number of years represented and multiply by 12
// Substract the month number from the total
// Substract the difference of the second month and 12 from the total
retVal = (d1.Year - d2.Year) * 12;
retVal = retVal - d1.Month;
retVal = retVal - (12 - d2.Month);
return retVal;
}
public static int PayableMonthsInDuration(DateTime StartDate, DateTime EndDate)
{
int sy = StartDate.Year; int sm = StartDate.Month; int count = 0;
do
{
count++;if ((sy == EndDate.Year) && (sm >= EndDate.Month)) { break; }
sm++;if (sm == 13) { sm = 1; sy++; }
} while ((EndDate.Year >= sy) || (EndDate.Month >= sm));
return (count);
}
Это решение для расчета аренды / подписки, где разница не означает вычитание, а промежуток между этими двумя датами.
Я написал функцию для достижения этой цели, потому что другие способы не работали для меня.
public string getEndDate (DateTime startDate,decimal monthCount)
{
int y = startDate.Year;
int m = startDate.Month;
for (decimal i = monthCount; i > 1; i--)
{
m++;
if (m == 12)
{ y++;
m = 1;
}
}
return string.Format("{0}-{1}-{2}", y.ToString(), m.ToString(), startDate.Day.ToString());
}
В моем случае требуется рассчитать полный месяц от даты начала до дня, предшествующего этому дню, в следующем месяце или от начала до конца месяца.
Пример: с 1 января по 31 января 2008 года - полный месяц
Пример 2: с 01.05.2008 по 04.02.2017 - полный месяц
поэтому на основе этого вот мое решение:
public static DateTime GetMonthEnd(DateTime StartDate, int MonthsCount = 1)
{
return StartDate.AddMonths(MonthsCount).AddDays(-1);
}
public static Tuple<int, int> CalcPeriod(DateTime StartDate, DateTime EndDate)
{
int MonthsCount = 0;
Tuple<int, int> Period;
while (true)
{
if (GetMonthEnd(StartDate) > EndDate)
break;
else
{
MonthsCount += 1;
StartDate = StartDate.AddMonths(1);
}
}
int RemainingDays = (EndDate - StartDate).Days + 1;
Period = new Tuple<int, int>(MonthsCount, RemainingDays);
return Period;
}
Использование:
Tuple<int, int> Period = CalcPeriod(FromDate, ToDate);
Примечание: в моем случае требовалось вычислить оставшиеся дни после полных месяцев, поэтому, если это не ваш случай, вы можете проигнорировать результат дней или даже вы можете изменить метод return с кортежа на целое число.
Вот гораздо более краткое решение с использованием VB.Net DateDiff только для года, месяца, дня. Вы также можете загрузить библиотеку DateDiff в C#.
дата1 должна быть <= дата2
VB.NET
Dim date1 = Now.AddDays(-2000)
Dim date2 = Now
Dim diffYears = DateDiff(DateInterval.Year, date1, date2) - If(date1.DayOfYear > date2.DayOfYear, 1, 0)
Dim diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - If(date1.Day > date2.Day, 1, 0)
Dim diffDays = If(date2.Day >= date1.Day, date2.Day - date1.Day, date2.Day + (Date.DaysInMonth(date1.Year, date1.Month) - date1.Day))
C#
DateTime date1 = Now.AddDays(-2000);
DateTime date2 = Now;
int diffYears = DateDiff(DateInterval.Year, date1, date2) - date1.DayOfYear > date2.DayOfYear ? 1 : 0;
int diffMonths = DateDiff(DateInterval.Month, date1, date2) - diffYears * 12 - date1.Day > date2.Day ? 1 : 0;
int diffDays = date2.Day >= date1.Day ? date2.Day - date1.Day : date2.Day + (System.DateTime.DaysInMonth(date1.Year, date1.Month) - date1.Day);
Это в ответ на ответ Кирка Уолла. У меня недостаточно очков репутации, чтобы ответить на комментарий...
Мне понравилось решение Кирка, и я собирался бесстыдно сорвать его и использовать в своем коде, но когда я просмотрел его, я понял, что это слишком сложно. Ненужные переключения и циклы, а также открытый конструктор, который бессмысленно использовать.
Вот мой переписать:
public class DateTimeSpan {
private DateTime _date1;
private DateTime _date2;
private int _years;
private int _months;
private int _days;
private int _hours;
private int _minutes;
private int _seconds;
private int _milliseconds;
public int Years { get { return _years; } }
public int Months { get { return _months; } }
public int Days { get { return _days; } }
public int Hours { get { return _hours; } }
public int Minutes { get { return _minutes; } }
public int Seconds { get { return _seconds; } }
public int Milliseconds { get { return _milliseconds; } }
public DateTimeSpan(DateTime date1, DateTime date2) {
_date1 = (date1 > date2) ? date1 : date2;
_date2 = (date2 < date1) ? date2 : date1;
_years = _date1.Year - _date2.Year;
_months = (_years * 12) + _date1.Month - _date2.Month;
TimeSpan t = (_date2 - _date1);
_days = t.Days;
_hours = t.Hours;
_minutes = t.Minutes;
_seconds = t.Seconds;
_milliseconds = t.Milliseconds;
}
public static DateTimeSpan CompareDates(DateTime date1, DateTime date2) {
return new DateTimeSpan(date1, date2);
}
}
Использование1, почти то же самое:
void Main()
{
DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
var dateSpan = new DateTimeSpan(compareTo, now);
Console.WriteLine("Years: " + dateSpan.Years);
Console.WriteLine("Months: " + dateSpan.Months);
Console.WriteLine("Days: " + dateSpan.Days);
Console.WriteLine("Hours: " + dateSpan.Hours);
Console.WriteLine("Minutes: " + dateSpan.Minutes);
Console.WriteLine("Seconds: " + dateSpan.Seconds);
Console.WriteLine("Milliseconds: " + dateSpan.Milliseconds);
}
Использование2, аналогично:
void Main()
{
DateTime compareTo = DateTime.Parse("8/13/2010 8:33:21 AM");
DateTime now = DateTime.Parse("2/9/2012 10:10:11 AM");
Console.WriteLine("Years: " + DateTimeSpan.CompareDates(compareTo, now).Years);
Console.WriteLine("Months: " + DateTimeSpan.CompareDates(compareTo, now).Months);
Console.WriteLine("Days: " + DateTimeSpan.CompareDates(compareTo, now).Days);
Console.WriteLine("Hours: " + DateTimeSpan.CompareDates(compareTo, now).Hours);
Console.WriteLine("Minutes: " + DateTimeSpan.CompareDates(compareTo, now).Minutes);
Console.WriteLine("Seconds: " + DateTimeSpan.CompareDates(compareTo, now).Seconds);
Console.WriteLine("Milliseconds: " + DateTimeSpan.CompareDates(compareTo, now).Milliseconds);
}