给定日期范围内星期一的数量

25

给定一个日期范围,我需要知道在该范围内有多少个星期一(或星期二、星期三等)。

我目前在使用C#进行工作。

14个回答

56

试试这个:

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

谢谢,Jon B...这就是我在寻找的答案。我接近了,我遗漏的部分是如何处理余数。 - ThePeeje
你有没有其他的方法可以简化这段代码? - user1647667

22

如果你正在使用C#,且版本为C#3.0,则可以使用LINQ。

假设你有一个包含日期类型DateTime的数组/列表/IQueryable等:

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

不知道,但我投了一票来补偿。我喜欢你的LINQ示例。 - Cyberherbalist
同意,这些样例非常棒。 - GnomeCubed
9
我认为这个问题被否决的原因有两个: 1:问题是关于日期范围而不是日期列表,所以你的例子不适用。 2:如果你需要在一个大范围和大次数内进行循环,那么遍历日期范围会花费很长时间。 - Peter Morris

21

研究不同算法来计算星期几是非常有趣的,@Gabe Hollombe提供的关于这个主题的WP链接是一个好主意(我还记得大约二十年前在COBOL中实现过Zeller's Congruence),但这更像是在给人一个时钟的设计图而不是回答他们所问的时间。

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)

2
我真的很喜欢你在迭代器中使用DateTime的方式。 - Sandor Davidhazi
1
我使用这个方法在一段时间范围内获取星期一(星期二,星期三等)的日期列表,谢谢。 - Sergio Ramirez
接受的答案在性能上超过了这个答案,但在我看来,这个答案更易读和理解。谢谢@Cyberherbalist。 - Jacob Adams

5

以下是一些伪代码:

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

有特定的语言和日期格式吗?

如果日期表示为天数计数,则两个值之间的差加一(天),再除以7,就是大部分答案。如果两个结束日期都是所询问的那一天,则再加一。

编辑:更正“模7”为“除以7” - 谢谢。这是整数除法。


除非我误解了你提出的算法,否则任何日期范围的最大结果都将是6,因为这是任何数字的模7的最大结果。我错过了什么? - EBGreen
是的,应该除以7而不是模7。 - Moe
对不起,我是指(整数)除法! - Jonathan Leffler
@Simucal:最初提问时只有3个标签,没有C#。 - Jonathan Leffler

2
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
今天我也有同样的需求。由于我不理解JonB函数,而且Cyberherbalist函数不是线性的,所以我从cjm函数开始尝试。
我必须进行更正。
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 

使用between函数,如果从开始日期开始,我们在结束日期之前首先遇到了dayImLookingFor,则返回true。

我通过计算从startDay到另外两个日期的天数来完成between函数:

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

这将返回一个整数集合,显示日期范围内每个星期几出现的次数

    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

加上最小的数使第一天成为星期一。减去最小的数使最后一天成为星期一。计算天数差并除以7。


1

将日期转换为儒略日数,然后进行一些数学计算。由于星期一是模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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接