计算给定日期范围内的星期一数

Count number of Mondays in a given date range

提问人:ThePeeje 提问时间:10/30/2008 更新时间:1/26/2021 访问量:35430

问:

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

我目前在 C# 中工作。

C# 时间 日期 差异

评论


答:

3赞 Jonathan Leffler 10/30/2008 #1

任何特定的语言和日期格式?

如果日期表示为天数,则两个值加 1(天)并除以 7 之间的差值是大部分答案。如果两个结束日期都是相关日期,请添加一个。

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

评论

0赞 EBGreen 10/30/2008
除非我误解了您提出的算法,否则任何日期范围的最大结果都是 6,因为这是任何数字的模 7 的最大结果。我错过了什么?
0赞 Moe 10/30/2008
是的,它应该除以 7,而不是 mod 7
0赞 Jonathan Leffler 10/30/2008
Mea culpa - 我的意思是(整数)除法!
0赞 Jonathan Leffler 10/30/2008
@Simucal:最初被问到时,只有 3 个标签 - 也不是 C#。
1赞 Mark Ransom 10/30/2008 #2

添加尽可能小的数字,使第一天成为星期一。减去尽可能小的数字,使最后一天成为星期一。计算天数差并除以 7。

22赞 Codewerks 10/30/2008 #3

由于使用的是 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());
}

评论

0赞 Cyberherbalist 10/30/2008
不知道;但我投了一票来补偿。我喜欢你的 LINQ 示例。
0赞 GnomeCubed 1/15/2009
同意,样品很棒。
9赞 Peter Morris 2/24/2012
我认为它被否决有两个原因:1:问题是关于日期范围,而不是日期列表;所以你的例子不起作用。2:如果您必须进行大范围和大量次的循环访问,则需要很长时间。
5赞 cjm 10/30/2008 #4

下面是一些伪代码:

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

其中以天为单位返回,并以整数形式返回星期几。映射使用什么并不重要,只要它正在增加并与 .DifferenceInDaysEnd - StartDayOfWeekDayOfWeekDayImLookingFor

请注意,此算法假定日期范围是非独占的。如果不应该是范围的一部分,则必须稍微调整算法。End

转换为 C# 留给读者作为练习。

21赞 Cyberherbalist 10/30/2008 #5

查看用于计算星期几的不同算法很有趣,@Gabe Hollombe 在这个问题上指向 WP 是一个好主意(我记得大约 20 年前在 COBOL 中实现了 Zeller 的 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;
    }

当然,这不会计算“Mondayness”的结束日期,因此如果需要,请使 for 循环计算

dt < endDate.AddDays(1.0)

评论

2赞 Sandor Davidhazi 5/5/2009
我真的很喜欢你如何为迭代器使用 DateTime。
1赞 Sergio Ramirez 5/21/2016
我用它来获取日期时间范围内的星期一(星期二、星期三等)日期列表,谢谢
0赞 Jacob Adams 1/17/2019
公认的答案在性能方面胜过它,但在我看来,这个答案更具可读性和可理解性。谢谢@Cyberherbalist。
1赞 DarenW 10/30/2008 #6

将日期转换为儒略日数,然后进行一些数学运算。由于星期一是零模组 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
0赞 Will Rickards 10/30/2008 #7

我在一份报告中遇到了类似的问题。我需要两个日期之间的工作日数。 我本可以循环浏览日期并数数,但我离散的数学训练不允许我这样做。这是我在 VBA 中编写的一个函数,用于获取两个日期之间的工作日数。我敢肯定 .net 有类似的 WeekDay 函数。

   1  
   2  ' WorkDays
   3  ' returns the number of working days between two dates
   4  Public Function WorkDays(ByVal dtBegin As Date, ByVal dtEnd As Date) As Long
   5  
   6     Dim dtFirstSunday As Date
   7     Dim dtLastSaturday As Date
   8     Dim lngWorkDays As Long
   9  
  10     ' get first sunday in range
  11     dtFirstSunday = dtBegin + ((8 - Weekday(dtBegin)) Mod 7)
  12  
  13     ' get last saturday in range
  14     dtLastSaturday = dtEnd - (Weekday(dtEnd) Mod 7)
  15  
  16     ' get work days between first sunday and last saturday
  17     lngWorkDays = (((dtLastSaturday - dtFirstSunday) + 1) / 7) * 5
  18  
  19     ' if first sunday is not begin date
  20     If dtFirstSunday <> dtBegin Then
  21  
  22        ' assume first sunday is after begin date
  23        ' add workdays from begin date to first sunday
  24        lngWorkDays = lngWorkDays + (7 - Weekday(dtBegin))
  25  
  26     End If
  27  
  28     ' if last saturday is not end date
  29     If dtLastSaturday <> dtEnd Then
  30  
  31        ' assume last saturday is before end date
  32        ' add workdays from last saturday to end date
  33        lngWorkDays = lngWorkDays + (Weekday(dtEnd) - 1)
  34  
  35     End If
  36  
  37     ' return working days
  38     WorkDays = lngWorkDays
  39  
  40  End Function

评论

0赞 Codewerks 10/30/2008
WeekDay 是 .NET 中 DateTime 类型的属性,那么 WorkDays 不就是 Weekday 在 2、3、4、5 中的日期吗(假设 1=Sunday)?
56赞 Jon B 10/30/2008 #8

试试这个:

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

评论

0赞 ThePeeje 10/30/2008
谢谢,乔恩·这就是我一直在寻找的答案。我离得很近,我缺少的环节是如何处理剩下的。
0赞 user1647667 3/28/2014
有没有其他方法可以简化此代码?
0赞 Paul Osterhout 10/30/2008 #9
private System.Int32 CountDaysOfWeek(System.DayOfWeek dayOfWeek, System.DateTime date1, System.DateTime date2)
{
  System.DateTime EndDate;
  System.DateTime StartDate;

  if (date1 > date2)
  {
    StartDate = date2;
    EndDate = date1;
  }
  else
  {
    StartDate = date1;
    EndDate = date2;
  }

  while (StartDate.DayOfWeek != dayOfWeek)
    StartDate = StartDate.AddDays(1);

  return EndDate.Subtract(StartDate).Days / 7 + 1;
}
1赞 Olivier de Rivoyre 8/24/2009 #10

我今天也有同样的需求。我从 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 

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

我通过计算从 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;
    }
}
2赞 Terje Kvannli 12/6/2011 #11
如果您想在两个日期之间获得特定的工作日,您可以尝试此操作
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;
}
0赞 rasx 1/27/2012 #12

四年后,我想我应该做一个测试:

[TestMethod]
public void ShouldFindFridaysInTimeSpan()
{
    //reference: http://stackoverflow.com/questions/248273/count-number-of-mondays-in-a-given-date-range

    var spanOfSixtyDays = new TimeSpan(60, 0, 0, 0);
    var setOfDates = new List<DateTime>(spanOfSixtyDays.Days);
    var now = DateTime.Now;

    for(int i = 0; i < spanOfSixtyDays.Days; i++)
    {
        setOfDates.Add(now.AddDays(i));
    }

    Assert.IsTrue(setOfDates.Count == 60,
        "The expected number of days is not here.");

    var fridays = setOfDates.Where(i => i.DayOfWeek == DayOfWeek.Friday);

    Assert.IsTrue(fridays.Count() > 0,
        "The expected Friday days are not here.");
    Assert.IsTrue(fridays.First() == setOfDates.First(i => i.DayOfWeek == DayOfWeek.Friday),
        "The expected first Friday day is not here.");
    Assert.IsTrue(fridays.Last() == setOfDates.Last(i => i.DayOfWeek == DayOfWeek.Friday),
        "The expected last Friday day is not here.");
}

我的使用有点矫枉过正---实际上我想直接查询。TimeSpanTimeSpan

1赞 Peter Morris 2/24/2012 #13

这将返回一个整数集合,显示一周中每天在日期范围内发生的次数

    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赞 Monzur 7/7/2019 #14

一点修改后的代码在这里工作并由我测试

        private int CountDays(DayOfWeek day, DateTime startDate, DateTime endDate)
        {
            int dayCount = 0;

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

            return dayCount;
        }

例:

int Days = CountDays(DayOfWeek.Friday, Convert.ToDateTime("2019-07-04"), 
             Convert.ToDateTime("2019-07-27")).ToString();

评论

0赞 JustJohn 6/15/2020
谢谢。我在 VB.NET 中需要它并转换了它。