Подсчитать количество понедельников в заданном диапазоне дат - PullRequest
22 голосов
/ 29 октября 2008

Учитывая диапазон дат, мне нужно знать, сколько понедельников (или вторников, сред и т. Д.) В этом диапазоне.

Я сейчас работаю в C #.

Ответы [ 14 ]

40 голосов
/ 30 октября 2008

Попробуйте это:

static int CountDays(DayOfWeek day, DateTime start, DateTime end)
{
    TimeSpan ts = end - start;                       // Total duration
    int count = (int)Math.Floor(ts.TotalDays / 7);   // Number of whole weeks
    int remainder = (int)(ts.TotalDays % 7);         // Number of remaining days
    int sinceLastDay = (int)(end.DayOfWeek - day);   // Number of days since last [day]
    if (sinceLastDay < 0) sinceLastDay += 7;         // Adjust for negative days since last [day]

    // If the days in excess of an even week are greater than or equal to the number days since the last [day], then count this one, too.
    if (remainder >= sinceLastDay) count++;          

    return count;
}
20 голосов
/ 29 октября 2008

Поскольку вы используете C #, если вы используете C # 3.0, вы можете использовать LINQ.

Предполагается, что у вас есть Array / List / IQueryable и т. Д., Которые содержат ваши даты в виде типов DateTime:

DateTime[] dates = { new DateTime(2008,10,6), new DateTime(2008,10,7)}; //etc....

var mondays = dates.Where(d => d.DayOfWeek == DayOfWeek.Monday); // = {10/6/2008}

Добавлено:

Не уверен, что вы имели в виду их группирование и подсчет, но вот как это сделать и в LINQ:

var datesgrouped = from d in dates
                   group d by d.DayOfWeek into grouped
                   select new { WeekDay = grouped.Key, Days = grouped };

foreach (var g in datesgrouped)
{
    Console.Write (String.Format("{0} : {1}", g.WeekDay,g.Days.Count());
}
17 голосов
/ 29 октября 2008

Забавно смотреть на разные алгоритмы для вычисления дня недели, и @Gabe Hollombe, указав на WP по этому вопросу, была отличной идеей (и я помню, как реализовал Zeller's Congruence в COBOL около двадцати лет назад) , но это было скорее по линии вручения кому-то чертежа часов, когда все спрашивали, который час.

В C #:

    private int CountMondays(DateTime startDate, DateTime endDate)
    {
        int mondayCount = 0;

        for (DateTime dt = startDate; dt < endDate; dt = dt.AddDays(1.0))
        {
            if (dt.DayOfWeek == DayOfWeek.Monday)
            {
                mondayCount++;
            }
        }

        return mondayCount;
    }

Это, конечно, не оценивает конечную дату для "понедельника", поэтому, если это было желательно, сделайте оценку цикла for

dt < endDate.AddDays(1.0)
5 голосов
/ 29 октября 2008

Вот какой-то псевдокод:

DifferenceInDays(Start, End) / 7   // Integer division discarding remainder
+ 1 if DayOfWeek(Start) <= DayImLookingFor
+ 1 if DayOfWeek(End)   >= DayImLookingFor
- 1

Где DifferenceInDays возвращает End - Start в днях, а DayOfWeek возвращает день недели в виде целого числа. Неважно, что использует отображение DayOfWeek, если оно увеличивается и совпадает с DayImLookingFor.

Обратите внимание, что этот алгоритм предполагает, что диапазон дат включительно. Если End не должен входить в диапазон, вам придется немного изменить алгоритм.

Перевод на C # оставлен читателю в качестве упражнения.

3 голосов
/ 06 июля 2015

Я нашел несколько более простой способ решения этой проблемы с помощью linq.

public static int NumberOfFridays(DateTime start, DateTime end) 
{ 
    return start.GetDaysInBetween(end, inclusive: true).Count(d => d.DayOfWeek == DayOfWeek.Friday); 
} 

Надеюсь, это поможет.

3 голосов
/ 29 октября 2008

Какой-то конкретный язык и, следовательно, формат даты?

Если даты представлены в виде количества дней, то большая часть ответа - разница между двумя значениями плюс одно (день) и делением на 7. Если обе даты окончания являются указанным днем, добавьте одну.

Отредактировано: исправлено «по модулю 7» для «деления на 7» - спасибо. И это целочисленное деление.

1 голос
/ 23 февраля 2012

Это вернет коллекцию целых чисел, показывающую, сколько раз каждый день недели происходит в диапазоне дат

    int[] CountDays(DateTime firstDate, DateTime lastDate)
    {
        var totalDays = lastDate.Date.Subtract(firstDate.Date).TotalDays + 1;
        var weeks = (int)Math.Floor(totalDays / 7);

        var result = Enumerable.Repeat<int>(weeks, 7).ToArray();
        if (totalDays % 7 != 0)
        {
            int firstDayOfWeek = (int)firstDate.DayOfWeek;
            int lastDayOfWeek = (int)lastDate.DayOfWeek;
            if (lastDayOfWeek < firstDayOfWeek)
                lastDayOfWeek += 7;
            for (int dayOfWeek = firstDayOfWeek; dayOfWeek <= lastDayOfWeek; dayOfWeek++)
                result[dayOfWeek % 7]++;
        }
        return result;
    }

Или небольшой вариант, который позволяет вам сделать FirstDate.TotalDaysOfWeeks (SecondDate) и вернуть словарь

    public static Dictionary<DayOfWeek, int> TotalDaysOfWeeks(this DateTime firstDate, DateTime lastDate)
    {
        var totalDays = lastDate.Date.Subtract(firstDate.Date).TotalDays + 1;
        var weeks = (int)Math.Floor(totalDays / 7);

        var resultArray = Enumerable.Repeat<int>(weeks, 7).ToArray();
        if (totalDays % 7 != 0)
        {
            int firstDayOfWeek = (int)firstDate.DayOfWeek;
            int lastDayOfWeek = (int)lastDate.DayOfWeek;
            if (lastDayOfWeek < firstDayOfWeek)
                lastDayOfWeek += 7;
            for (int dayOfWeek = firstDayOfWeek; dayOfWeek <= lastDayOfWeek; dayOfWeek++)
                resultArray[dayOfWeek % 7]++;
        }
        var result = new Dictionary<DayOfWeek, int>();
        for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++)
            result[(DayOfWeek)dayOfWeek] = resultArray[dayOfWeek];
        return result;
    }
1 голос
/ 06 декабря 2011
Вы можете попробовать это, если хотите получить конкретные дни недели между двумя датами
public List<DateTime> GetSelectedDaysInPeriod(DateTime startDate, DateTime endDate, List<DayOfWeek> daysToCheck)
{
    var selectedDates = new List<DateTime>();

    if (startDate >= endDate)
        return selectedDates; //No days to return

    if (daysToCheck == null || daysToCheck.Count == 0)
        return selectedDates; //No days to select

    try
    {
        //Get the total number of days between the two dates
        var totalDays = (int)endDate.Subtract(startDate).TotalDays;

        //So.. we're creating a list of all dates between the two dates:
        var allDatesQry = from d in Enumerable.Range(1, totalDays)
                             select new DateTime(
                                                  startDate.AddDays(d).Year,
                                                  startDate.AddDays(d).Month,
                                                  startDate.AddDays(d).Day);

        //And extracting those weekdays we explicitly wanted to return
        var selectedDatesQry = from d in allDatesQry
                                  where daysToCheck.Contains(d.DayOfWeek)
                                  select d;

        //Copying the IEnumerable to a List
        selectedDates = selectedDatesQry.ToList();
    }
    catch (Exception ex)
    {
        //Log error
        //...

        //And re-throw
        throw;
    }
    return selectedDates;
}
1 голос
/ 24 августа 2009

У меня была такая же потребность сегодня. Я начал с функции cjm , поскольку я не понимаю функцию JonB и поскольку функция Cyberherbalist не является линейной.

Я должен был исправить

DifferenceInDays(Start, End) / 7   // Integer division discarding remainder
+ 1 if DayOfWeek(Start) <= DayImLookingFor
+ 1 if DayOfWeek(End)   >= DayImLookingFor
- 1

до

DifferenceInDays(Start, End) / 7   // Integer division discarding remainder
+ 1 if DayImLookingFor is between Start.Day and End.Day 

С помощью функции before, которая возвращает true, если начиная с начального дня мы сначала встречаем dayImLookingFor до endDay.

Я сделал промежуточную функцию, вычислив число дней от startDay до двух других дней:

private int CountDays(DateTime start, DateTime end, DayOfWeek selectedDay)
{
    if (start.Date > end.Date)
    {
        return 0;
    }
    int totalDays = (int)end.Date.Subtract(start.Date).TotalDays;
    DayOfWeek startDay = start.DayOfWeek;
    DayOfWeek endDay = end.DayOfWeek;
    ///look if endDay appears before or after the selectedDay when we start from startDay.
    int startToEnd = (int)endDay - (int)startDay;
    if (startToEnd < 0)
    {
        startToEnd += 7;
    }
    int startToSelected = (int)selectedDay - (int)startDay;
    if (startToSelected < 0)
    {
        startToSelected += 7;
    }
    bool isSelectedBetweenStartAndEnd = startToEnd >= startToSelected;
    if (isSelectedBetweenStartAndEnd)
    {
        return totalDays / 7 + 1;
    }
    else
    {
        return totalDays / 7;
    }
}
1 голос
/ 29 октября 2008

Конвертируйте даты в Юлианский номер дня, затем сделайте немного математики. Поскольку понедельник нулевой мод 7, вы можете сделать расчет следующим образом:

JD1=JulianDayOf(the_first_date)
JD2=JulianDayOf(the_second_date)
Round JD1 up to nearest multiple of 7
Round JD2 up to nearest multiple of 7
d = JD2-JD1
nMondays = (JD2-JD1+7)/7    # integer divide
...