Получите количество календарных недель между двумя датами в С#
В целях этого вопроса предположим, что пользователь будет из США и будет использовать стандартный григорианский календарь. Итак, календарная неделя начинается в воскресенье и заканчивается в субботу.
То, что я пытаюсь сделать, - определить количество календарных недель, которые существуют между двумя датами. Прекрасный пример моей проблемы существует в октябре 2010 года. Между 10/16 и 10/31 существует 4 календарных недели.
- 10 октября - 16 октября
- 17 октября - 23 октября
- 24 октября - 30 октября
- 31 октября - 6 ноября
Я бы предпочел держаться подальше от любой жестко закодированной логики, например:
if (Day == DayOfWeek.Saturday && LastDayOfMonth == 31) { ... }
Может ли кто-нибудь думать об логическом способе сделать это?
ОБНОВЛЕНИЕ:
Спасибо за все замечательные ответы, после некоторого рассмотрения здесь используется решение, которое я использовал:
//get the start and end dates of the current pay period
DateTime currentPeriodStart = SelectedPeriod.Model.PeriodStart;
DateTime currentPeriodEnd = SelectedPeriod.Model.PeriodEnd;
//get the first sunday & last saturday span that encapsulates the current pay period
DateTime firstSunday = DayExtensions.SundayBeforePeriodStart(currentPeriodStart);
DateTime lastSaturday = DayExtensions.SaturdayAfterPeriodEnd(currentPeriodEnd);
//get the number of calendar weeks in the span
int numberOfCalendarWeeks = DayExtensions.CalendarWeeks(firstSunday, lastSaturday);
И вот методы из вспомогательного класса:
/// <summary>
/// Get the first Sunday before the pay period start date
/// </summary>
/// <param name="periodStartDate">Date of the pay period start date</param>
/// <returns></returns>
public static DateTime SundayBeforePeriodStart(DateTime periodStartDate)
{
DateTime firstDayOfWeekBeforeStartDate;
int daysBetweenStartDateAndPreviousFirstDayOfWeek = (int)periodStartDate.DayOfWeek - (int)DayOfWeek.Sunday;
if (daysBetweenStartDateAndPreviousFirstDayOfWeek >= 0)
{
firstDayOfWeekBeforeStartDate = periodStartDate.AddDays(-daysBetweenStartDateAndPreviousFirstDayOfWeek);
}
else
{
firstDayOfWeekBeforeStartDate = periodStartDate.AddDays(-(daysBetweenStartDateAndPreviousFirstDayOfWeek + 7));
}
return firstDayOfWeekBeforeStartDate;
}
/// <summary>
/// Get the first Saturday after the period end date
/// </summary>
/// <param name="periodEndDate">Date of the pay period end date</param>
/// <returns></returns>
public static DateTime SaturdayAfterPeriodEnd(DateTime periodEndDate)
{
DateTime lastDayOfWeekAfterEndDate;
int daysBetweenEndDateAndFollowingLastDayOfWeek = (int)DayOfWeek.Saturday - (int)periodEndDate.DayOfWeek;
if (daysBetweenEndDateAndFollowingLastDayOfWeek >= 0)
{
lastDayOfWeekAfterEndDate = periodEndDate.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek);
}
else
{
lastDayOfWeekAfterEndDate = periodEndDate.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek + 7);
}
return lastDayOfWeekAfterEndDate;
}
/// <summary>
/// Get the calendar weeks between 2 dates
/// </summary>
/// <param name="d1">First day of date span</param>
/// <param name="d2">Last day of date span</param>
/// <returns></returns>
public static int CalendarWeeks(DateTime d1, DateTime d2)
{
return 1 + (int)((d2 - d1).TotalDays / 7);
}
И если вам интересно, вот что я делаю с датами:
//create an array of all the sundays in this span
DateTime[] _sundays = new DateTime[numberOfCalendarWeeks];
//put the first sunday in the period
_sundays[0] = firstSunday;
//step through each week and get each sunday until you reach the last saturday
for (int i = 1; i <= numberOfCalendarWeeks - 1; i++)
{
DateTime d = new DateTime();
d = firstSunday.AddDays(i * 7);
_sundays[i] = d;
}
for (int i = 0; i <= _sundays.Length-1; i++)
{
//bind my view model with each sunday.
}
Ответы
Ответ 1
Вот общее решение, которое, как я считаю, должно работать для любого выбора дней начала и окончания недели. Вы можете упростить его для своего случая, но этот код дает вам возможность изменить начало и конец недели (например, с понедельника по воскресенье), если это станет необходимым. Это не редкость в приложениях расчета заработной платы для определения календарной недели для изменения.
DateTime periodStart = new DateTime(2010, 10, 17);
DateTime periodEnd = new DateTime(2010, 11, 14);
const DayOfWeek FIRST_DAY_OF_WEEK = DayOfWeek.Monday;
const DayOfWeek LAST_DAY_OF_WEEK = DayOfWeek.Sunday;
const int DAYS_IN_WEEK = 7;
DateTime firstDayOfWeekBeforeStartDate;
int daysBetweenStartDateAndPreviousFirstDayOfWeek = (int)periodStart.DayOfWeek - (int)FIRST_DAY_OF_WEEK;
if (daysBetweenStartDateAndPreviousFirstDayOfWeek >= 0)
{
firstDayOfWeekBeforeStartDate = periodStart.AddDays(-daysBetweenStartDateAndPreviousFirstDayOfWeek);
}
else
{
firstDayOfWeekBeforeStartDate = periodStart.AddDays(-(daysBetweenStartDateAndPreviousFirstDayOfWeek + DAYS_IN_WEEK));
}
DateTime lastDayOfWeekAfterEndDate;
int daysBetweenEndDateAndFollowingLastDayOfWeek = (int)LAST_DAY_OF_WEEK - (int)periodEnd.DayOfWeek;
if (daysBetweenEndDateAndFollowingLastDayOfWeek >= 0)
{
lastDayOfWeekAfterEndDate = periodEnd.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek);
}
else
{
lastDayOfWeekAfterEndDate = periodEnd.AddDays(daysBetweenEndDateAndFollowingLastDayOfWeek + DAYS_IN_WEEK);
}
int calendarWeeks = 1 + (int)((lastDayOfWeekAfterEndDate - firstDayOfWeekBeforeStartDate).TotalDays / DAYS_IN_WEEK);
Ответ 2
То, как я буду заниматься этим, - получить начало недели для любой даты. Используя это, вы вычтите результат начальной даты из результата конечной даты. Ваша разница в день будет всегда кратной 7, поэтому разделите и добавьте 1 (0 дней = > 1 неделя, 7 дней = > 2 недели и т.д.). Это сообщит вам, сколько календарных недель было покрыто или представлено любыми двумя датами.
static int GetWeeksCovered(DateTime startDate, DateTime endDate)
{
if (endDate < startDate)
throw new ArgumentException("endDate cannot be less than startDate");
return (GetBeginningOfWeek(endDate).Subtract(GetBeginningOfWeek(startDate)).Days / 7) + 1;
}
static DateTime GetBeginningOfWeek(DateTime date)
{
return date.AddDays(-1 * (int)date.DayOfWeek).Date;
}
- 16-окт-2010 и 16-окт-2010 = > 1 неделя (или представлены).
- 16-окт-2010 и 31-окт-2010 = > 4 недели, согласно спецификации.
Ответ 3
Помните, что на этой неделе расчеты проводятся по-разному в разных культурах, и нет ошибки, если вы видите неделю 53!
using System.Globalization;
CultureInfo cultInfo = CultureInfo.CurrentCulture;
int weekNumNow = cultInfo.Calendar.GetWeekOfYear(DateTime.Now,
cultInfo.DateTimeFormat.CalendarWeekRule,
cultInfo.DateTimeFormat.FirstDayOfWeek);
Ответ 4
private static int weekDifference(DateTime startDate, DateTime endDate)
{
int monthsApart = 12 * (startDate.Year - endDate.Year) + startDate.Month - endDate.Month;
return Math.Abs(monthsApart*4);
}
Cant См. лучший способ с места в карьер.
Ответ 5
private static int weekDifference(DateTime startDate, DateTime endDate)
{
const int firstDayOfWeek = 0; // Sunday
int wasteDaysStart = (7+startDate.DatOfWeek-firstDayOfWeek)%7;
return (int)(((endDate-startDate).TotalDays() + wasteDaysStart + 6)/7);
}
Предупреждение: непроверенный код. Пожалуйста, проверьте и удалите примечание.
Ответ 6
Следующее, похоже, работает для любого диапазона дат. Это должно быть культурно здоровым и должно учитывать високосные годы/дни или другие календарные странности:
private static int getWeeksSpannedBy(DateTime first, DateTime last)
{
var calendar = CultureInfo.CurrentCulture.Calendar;
var weekRule = CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule;
var firstDayOfWeek = DayOfWeek.Sunday;
int lastWeek = calendar.GetWeekOfYear(last, weekRule, firstDayOfWeek);
int firstWeek = calendar.GetWeekOfYear(first, weekRule, firstDayOfWeek);
int weekDiff = lastWeek - firstWeek + 1;
return weekDiff;
}
static void Main(string[] args)
{
int weeks1 = getWeeksSpannedBy(new DateTime(2010, 1, 3), new DateTime(2010, 1, 9));
int weeks2 = getWeeksSpannedBy(new DateTime(2010, 10, 16), new DateTime(2010, 10, 31));
int weeks3 = getWeeksSpannedBy(new DateTime(2008, 2, 1), new DateTime(2008, 2, 29));
int weeks4 = getWeeksSpannedBy(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29));
Console.WriteLine("Weeks Difference #1: " + weeks1);
Console.WriteLine("Weeks Difference #2: " + weeks2);
Console.WriteLine("Weeks Difference #3: " + weeks3);
Console.WriteLine("Weeks Difference #4: " + weeks4);
Console.ReadLine();
}
Распечатывает следующее, что верно для всех диапазонов дат, прошлого, настоящего или будущего (високосный год 2008 и 2012 гг. имеют 5 недель с 1 февраля по 29 февраля):
Разница недель №1:1
Разница недель №2: 4
Разница недель № 3: 5
Разница недель № 4: 5
Ответ 7
Суббота - последний день недели, да?
public int CalendarWeeks(DateTime from, DateTime to) {
// number of multiples of 7
// (rounded up, since 15 days would span at least 3 weeks)
// and if we end on a day before we start, we know it another week
return (int)Math.Ceiling(to.Subtract(from).Days / 7.0) +
(to.DayOfWeek <= from.DayOfWeek) ? 1 : 0;
}