如何循环遍历日期范围?

260

我甚至不确定如何在不使用可怕的for循环/计数器类型解决方案的情况下完成这个问题。 这就是问题所在:

我有两个日期,一个开始日期和一个结束日期,在指定的时间间隔内需要执行某些操作。例如:对于从3/10/2009开始的每一天中的每三天,直到3/26/2009,我都需要在列表中创建一个条目。 因此,我的输入将是:

DateTime StartDate = "3/10/2009";
DateTime EndDate = "3/26/2009";
int DayInterval = 3;
而我的输出将是一个具有以下日期的列表:
3/13/2009 3/16/2009 3/19/2009 3/22/2009 3/25/2009
那么我要怎样做才能实现这个目标呢?我考虑使用一个for循环来遍历范围内的每一天,并使用一个独立的计数器,像这样:
int count = 0;

for(int i = 0; i < n; i++)
{
     count++;
     if(count >= DayInterval)
     {
          //take action
          count = 0;
     }

}

但似乎还有更好的方法?


2
我猜C#有一个日期数据结构可以使用。 - Anna
17个回答

595

好的,你需要以某种方式循环遍历它们。我更喜欢定义一个类似这样的方法:

public IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for(var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

接着你可以像这样使用它:

foreach (DateTime day in EachDay(StartDate, EndDate))
    // print it or whatever

这种方式可以让你隔一天、隔两天或仅在工作日执行。例如,要从“开始”日期开始每三天返回一次,你只需在循环中调用AddDays(3)而不是AddDays(1)


18
你可以为区间添加另一个参数。 - Justin Drury
5
一个有趣实用的世界问题得到了非常好的解决方案。我喜欢它展示了许多有用的语言技巧,也让我想起了for循环不仅仅只能写成(int i = 0; ...)的形式。 - Audrius
9
将其作为扩展方法添加到日期时间中可能会使其变得更好。 - MatteS
1
看一下我关于日期和月份的扩展的回答;)仅供您的乐趣:D - Jacob Sobus
我可以说这是我在使用.NET三年中看过的最优雅的代码吗?哦,还有一件事我不明白,为什么要用from.Date而不是只用 from - Yosef Bernal
显示剩余2条评论

33

我有一个在MiscUtil库中的Range类,可能对您有用。与各种扩展方法相结合,您可以执行以下操作:

foreach (DateTime date in StartDate.To(EndDate).ExcludeEnd()
                                   .Step(DayInterval.Days())
{
    // Do something with the date
}

(您可能希望排除结尾,也可以保留,我只是提供它作为示例。)

这基本上是 mquander 的解决方案的现成版本(且更通用)。


2
当然,你喜欢这些东西是作为扩展方法还是其他方式纯属个人口味。ExcludeEnd()很可爱。 - mqp
1
当然,您可以在不使用扩展方法的情况下完成所有这些操作。但是在我看来,这样做会更加繁琐和难以阅读 :) - Jon Skeet
1
哇,MiscUtil是一个非常棒的资源 - 感谢您的回答! - Paul Mignard
1
如果除了我之外的任何人都将DayInterval误认为是一个结构体/类,那么在这个示例中它实际上是一个整数。当然,如果你仔细阅读问题,这是显而易见的,但我没有注意到。 - marc.d

26

对于您的示例,您可以尝试

DateTime StartDate = new DateTime(2009, 3, 10);
DateTime EndDate = new DateTime(2009, 3, 26);
int DayInterval = 3;

List<DateTime> dateList = new List<DateTime>();
while (StartDate.AddDays(DayInterval) <= EndDate)
{
   StartDate = StartDate.AddDays(DayInterval);
   dateList.Add(StartDate);
}

1
这正是我在想的(虽然我也喜欢上面 mquander 的答案),但我不知道你是如何如此快速地发布漂亮的代码示例的! - TLiebe
3
我认为我们需要在此循环中只使用一次 StartDate.AddDays(DayInterval); 而不是两次。 - Abdul Saboor

20

代码来自@mquander和@Yogurt The Wise用于扩展:

public static IEnumerable<DateTime> EachDay(DateTime from, DateTime thru)
{
    for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
        yield return day;
}

public static IEnumerable<DateTime> EachMonth(DateTime from, DateTime thru)
{
    for (var month = from.Date; month.Date <= thru.Date || month.Month == thru.Month; month = month.AddMonths(1))
        yield return month;
}

public static IEnumerable<DateTime> EachDayTo(this DateTime dateFrom, DateTime dateTo)
{
    return EachDay(dateFrom, dateTo);
}

public static IEnumerable<DateTime> EachMonthTo(this DateTime dateFrom, DateTime dateTo)
{
    return EachMonth(dateFrom, dateTo);
}

“EachDayTo”和“EachMonthTo”的意义是什么?我觉得我在这里错过了什么。 - Alisson Reinaldo Silva
1
@Alisson,这些是在dateFrom对象上工作的扩展方法 :) 因此,您可以在已创建的DateTime对象上更流畅地使用它们(仅使用实例后面的“.”)。有关扩展方法的更多信息,请参见此处:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods - Jacob Sobus

14

一年后,希望它能对某些人有所帮助。

这个版本包括一个谓词,使其更加灵活。

使用方法

var today = DateTime.UtcNow;
var birthday = new DateTime(2018, 01, 01);

每日距离我的生日倒计时

var toBirthday = today.RangeTo(birthday);  

每月距我的生日,步长为2个月

var toBirthday = today.RangeTo(birthday, x => x.AddMonths(2));

每年距离我的生日还有多久

var toBirthday = today.RangeTo(birthday, x => x.AddYears(1));

使用RangeFrom替代

// same result
var fromToday = birthday.RangeFrom(today);
var toBirthday = today.RangeTo(birthday);

实现

public static class DateTimeExtensions 
{

    public static IEnumerable<DateTime> RangeTo(this DateTime from, DateTime to, Func<DateTime, DateTime> step = null)
    {
        if (step == null)
        {
            step = x => x.AddDays(1);
        }

        while (from < to)
        {
            yield return from;
            from = step(from);
        }
    }

    public static IEnumerable<DateTime> RangeFrom(this DateTime to, DateTime from, Func<DateTime, DateTime> step = null)
    {
        return from.RangeTo(to, step);
    }
}

额外内容

如果fromDate > toDate,你可以抛出一个异常,但我更喜欢返回一个空范围 []


哇,这真的非常全面。谢谢Ahmad! - Paul Mignard

9
DateTime startDate = new DateTime(2009, 3, 10);
DateTime stopDate = new DateTime(2009, 3, 26);
int interval = 3;

for (DateTime dateTime=startDate;
     dateTime < stopDate; 
     dateTime += TimeSpan.FromDays(interval))
{

}

6
DateTime begindate = Convert.ToDateTime("01/Jan/2018");
DateTime enddate = Convert.ToDateTime("12 Feb 2018");
 while (begindate < enddate)
 {
    begindate= begindate.AddDays(1);
    Console.WriteLine(begindate + "  " + enddate);
 }

4

根据问题,您可以尝试以下方法...

// looping between date range    
while (startDate <= endDate)
{
    //here will be your code block...

    startDate = startDate.AddDays(1);
}

thanks......


3
DateTime startDate = new DateTime(2009, 3, 10);
DateTime stopDate = new DateTime(2009, 3, 26);
int interval = 3;

while ((startDate = startDate.AddDays(interval)) <= stopDate)
{
    // do your thing
}

请注意,此处不包括开始日期,因为它会在第一次“while”运行时添加一天。 - John Washam

3

以下是2020年我对IT技术的看法。

Enumerable.Range(0, (endDate - startDate).Days + 1)
.ToList()
.Select(a => startDate.AddDays(a));

这太棒了。 - Paul Mignard

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