【问题标题】:Count number of Mondays in a given date range计算给定日期范围内的星期一数
【发布时间】:2010-09-19 21:10:00
【问题描述】:

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

我目前正在使用 C#。

【问题讨论】:

    标签: c# datetime date datediff


    【解决方案1】:

    试试这个:

    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...这是我一直在寻找的答案。我很接近,我缺少的链接是如何处理剩余的。
    • 还有其他方法可以简化此代码吗?
    【解决方案2】:

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

    【讨论】:

    • 不知道;但我投了赞成票来补偿。我喜欢你的 LINQ 示例。
    • 同意,样品很棒。
    • 它被否决了我认为有两个原因 1:问题是关于日期范围,而不是日期列表;所以你的例子不起作用。 2:如果您必须在大范围内多次循环,则循环通过日期范围将需要很长时间。
    【解决方案3】:

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

    【讨论】:

    • 我真的很喜欢你使用 DateTime 作为迭代器的方式。
    • 我用它来获取日期时间范围内的星期一(星期二、星期三等)日期列表,谢谢
    • 接受的答案在性能方面胜过这个,但我认为这个答案更具可读性和可理解性。谢谢@Cyber​​herbalist。
    【解决方案4】:

    这是一些伪代码:

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

    其中DifferenceInDays 以天为单位返回End - StartDayOfWeek 以整数形式返回星期几。 DayOfWeek 使用什么映射并不重要,只要它不断增加并与 DayImLookingFor 匹配即可。

    请注意,此算法假定日期范围包含在内。如果End 不应该是范围的一部分,您将不得不稍微调整算法。

    翻译成 C# 留给读者作为练习。

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

      • 除非我误解了您提出的算法,否则任何日期范围的最大结果都是 6,因为这是任何数字的模 7 的最大结果。我错过了什么?
      • 是的,应该除以 7,而不是 mod 7
      • 过失 - 我的意思是(整数)除法!
      • @Simucal:最初被问到时,只有 3 个标签 - 也不是 C#。
      【解决方案6】:
      你可以试试这个,如果你想在两个日期之间获得特定的工作日
      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;
      }
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

          将日期转换为儒略日数,然后做一些数学运算。由于星期一是 0 mod 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
          

          【讨论】:

            【解决方案9】:

            我今天也有同样的需要。我从 cjm 函数开始,因为我不了解 JonB 函数并且因为 Cyber​​herbalist 函数不是线性的。

            我必须纠正

            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 
            

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

            我已经通过计算从 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;
                }
            }
            

            【讨论】:

              【解决方案10】:

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

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

              【讨论】:

                【解决方案11】:

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

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

                【讨论】:

                • 谢谢。我在 VB.NET 中需要它并对其进行了转换。
                【解决方案12】:

                我遇到了类似的报告问题。我需要两个日期之间的工作日数。 我本可以循环查看日期并数数,但我的离散数学训练不允许我这样做。这是我在 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
                

                【讨论】:

                • WeekDay 是 .NET 中 DateTime 类型的属性,所以 WorkDays 不只是工作日在 2、3、4、5 中的日期吗(假设 1=Sunday)?
                【解决方案13】:
                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;
                }
                

                【讨论】:

                  【解决方案14】:

                  四年后,我想我会进行一次测试:

                  [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.");
                  }
                  

                  我用TimeSpan有点矫枉过正——其实我是想直接查询TimeSpan

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-11-03
                    • 1970-01-01
                    • 2011-02-05
                    • 1970-01-01
                    相关资源
                    最近更新 更多