日期范围内的天数,不包括周末和其他日期,以C#表示

本文关键字:日期 其他 表示 不包括 范围内 周末 | 更新日期: 2023-09-27 18:00:52

我有一个C#方法,如下所示:

public static int DaysLeft(DateTime startDate, DateTime endDate, Boolean excludeWeekends, String excludeDates)
{
}

它应该计算startDate和endDate之间的天数,但也可以选择排除周末和其他日期(以逗号分隔的日期字符串形式传入(。

我完全不知道如何解决这个问题。我的直觉是从startDate到endDate循环,并进行一些字符串比较,但据我所知,C#不允许以这种方式循环日期——或者至少这不是一种非常优雅的做事方式。

日期范围内的天数,不包括周末和其他日期,以C#表示

哦,循环浏览日期真的很容易——这根本不是问题:

// I'm assuming you want <= to give an *inclusive* end date...
for (DateTime date = start; date <= end; date = date.AddDays(1))
{
     // Do stuff with date
}

您也可以很容易地编写IEnumerable<DateTime>,并使用foreach

如果可能的话,我会尽量避免在这里执行字符串操作——从根本上说,这些日期不是字符串,所以如果你能尽可能地在问题域中工作,这会让事情变得更容易。

当然,可能有比循环更有效的方法,但它们更难正确。如果循环在性能方面还可以,我会坚持下去。

作为我自己的开源项目的一个快速插件,Noda Time有一组更为多样的类型来表示日期和时间——在这种情况下,您将使用LocalDate。这样你就不必担心如果"开始"的时间晚于"结束"的时间会发生什么。另一方面,Noda time还没有真正结束。。。您需要的比特已经准备好了,应该可以正常工作,但API可能在未来仍然会改变。

编辑:如果确实需要频繁地循环日期,您可能需要类似于以下扩展方法(将其放在顶级非通用静态类中(:

public static IEnumerable<DateTime> To(this DateTime start, DateTime end)
{
    Date endDate = end.Date;
    for (DateTime date = start.Date; date <= endDate; date = date.AddDays(1))
    {
        yield return date;            
    }
}

然后:

foreach (DateTime date in start.To(end))
{
    ...
}

这里有一个例子,包括排除的日期、周末和循环。不过,我确实将excludeDates字符串更改为List。应该添加一些null检查。

    public static int DaysLeft(DateTime startDate, DateTime endDate, Boolean excludeWeekends, List<DateTime> excludeDates)
    {
        int count = 0;
        for (DateTime index = startDate; index < endDate; index = index.AddDays(1))
        {
            if (excludeWeekends && index.DayOfWeek != DayOfWeek.Sunday && index.DayOfWeek != DayOfWeek.Saturday)
            {
                bool excluded = false; ;
                for (int i = 0; i < excludeDates.Count; i++)
                {
                    if (index.Date.CompareTo(excludeDates[i].Date) == 0)
                    {
                        excluded = true;
                        break;
                    }
                }
                if (!excluded)
                {
                    count++;
                }
            }
        }
        return count;
    }

编辑:我确实想指出,如果你不必经常这样做的话,我会认为这是一种快速而肮脏的方法。如果你经常这样做,并且startDate和endDate之间的距离相当大,那么最好按照其他答案中的一个进行计算。建议这样做是为了获得迭代日期的感觉。

Subsonic糖库有很多助手来处理DateTime操作。

你可以在Subsonic网站上找到完整的列表,源代码在github中。

结合LINQ表达式,

        public int GetNoOfLeaveDays(DateTime fromDate, DateTime toDate, Boolean excludeWeekends, List<DateTime> excludeDates)
    {
        var count = 0;
        for (DateTime index = fromDate; index <= toDate; index = index.AddDays(1))
        {
            if (!excludeWeekends || index.DayOfWeek == DayOfWeek.Saturday || index.DayOfWeek == DayOfWeek.Sunday) continue;
            var excluded = excludeDates.Any(t => index.Date.CompareTo(t.Date) == 0);
            if (!excluded) count++;
        }
        return count;
    }

下面是我在SQL中使用的不同方法的伪代码:

查找两个日期之间的总天数
减去周末数
如果开始日期是星期日,则删除一天
如果开始日期是星期六,则删除一天
删除你不想要的任何其他日子(请参阅我上面的评论,了解如何在c#中做到这一点(

这可能有效并避免O(n(类型的解决方案:

public int DaysLeft(DateTime startDate, DateTime endDate, Boolean excludeWeekends, String excludeDates)
{
    //Work out days in range
    int days = (int)endDate.Subtract(startDate).TotalDays + 1;
    if (excludeWeekends)
    {
        //Remove most weekends by removing 2 in 7 days (rounded down)
        days -= ((int)Math.Floor((decimal)(days / 7)) * 2);
        if (startDate.DayOfWeek == DayOfWeek.Sunday) days -= 1;
        if (startDate.DayOfWeek == DayOfWeek.Saturday) days -= 2;
    }                  
    return days;
}

不过,它并没有经过详尽的测试。

为了处理排除日期,你可以循环浏览这些日期,并排除它们在开始和结束之间的位置(如果合适的话,也可以不是周末(。这应该是一个比从开始到结束的所有日子更短的循环。

private int workingdays(int month,int year)
{
    int daysInMonth = 0;
    int days = DateTime.DaysInMonth(year, month);
    for (int i = 1; i <= days; i++)
    {
        DateTime day = new DateTime(year, month, i);
        if (day.DayOfWeek != DayOfWeek.Sunday && day.DayOfWeek != DayOfWeek.Saturday)
        {
            daysInMonth++;
        }
    }
    return daysInMonth;
}