如何遍历日期范围?


196

我什至不知道如何在不使用某些可怕的循环/计数器类型解决方案的情况下做到这一点。这是问题所在:

给了我两个日期,一个开始日期和一个结束日期,在指定的时间间隔内,我需要采取一些措施。例如:对于2009年3月3日至2009年3月26日之间的每个日期,我需要在列表中创建一个条目。所以我的输入是:

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

}

但是似乎还有更好的方法?


1
我猜想C#具有可以使用的日期数据结构。
安娜

Answers:


468

好吧,您需要以一种或另一种方式遍历它们。我更喜欢定义这样的方法:

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
您甚至可以为间隔添加另一个参数。
贾斯汀·德鲁里

这将包括第一次约会。如果您不想要,只需将'var day = from.Date'更改为'var day = from.Date.AddDays(dayInterval)'
SwDevMan81 2009年

3
一个有趣而真实的单词问题的非常好的解决方案。我喜欢这显示了很多有用的语言技巧。这让我想起了那个for循环不仅是(INT I = 0; ...)( -
Audrius

9
将此方法扩展为datetime可能会使其更好。
MatteS 2011年

1
看看我为几天和几个月延长的答案;)只是为了您的乐趣:D
Jacob Sobus,2015年

30

RangeMiscUtil中有一个类,您可以找到有用的类。结合各种扩展方法,您可以执行以下操作:

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

(您可能会或可能不想排除结尾-我只是想以它为例。)

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


2
当然,无论您是否喜欢将这些东西用作扩展方法,都只是一个口味问题。 ExcludeEnd()可爱。
mqp

当然,您无需使用扩展方法即可完成所有这些操作。读IMO会更加难看和困难:)
Jon Skeet

1
哇-MiscUtil是多么有用的资源-感谢您的回答!
onekidney

1
如果除我之外的其他人将DayInterval误认为是struct / class,则在此示例中它实际上是整数。如果您仔细阅读问题,这当然是显而易见的,而我没有。
2016年

23

以您为例,您可以尝试

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的回答),但是我不知道怎么这么快就可以得到一个不错的代码示例!
蒂贝

3
我认为我们需要StartDate.AddDays(DayInterval); 在此循环中一次不会两次。
Abdul Saboor

15

@mquander和@Yogurt中的代码扩展中使用的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);
}

什么是点EachDayToEachMonthTo?我想我在这里错过了一些东西。
Alisson

@Alisson这些是对dateFrom对象工作的扩展方法:)因此,您可以在创建的DateTime对象上更流畅地使用它们(实例后仅使用。)。更多关于此扩展方法:docs.microsoft.com/en-us/dotnet/csharp/programming-guide/...
雅各布的SOBU

8
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))
{

}

8

一年后,可能对某人有帮助,

此版本包含一个谓词,以更加灵活。

用法

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

附加功能

如果可能会引发Exception fromDate > toDate,但是我宁愿返回一个空范围[]


哇-这真的很全面。谢谢艾哈迈德!
2013年

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

2

根据问题可以尝试一下...

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

    startDate = startDate.AddDays(1);
}

谢谢......


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

1

您可能会考虑编写一个迭代器,该迭代器允许您使用常规的“ for”循环语法(例如“ ++”)。我搜索,发现了一个类似的问题回答这里StackOverflow上这给制作日期时间迭代指针。


1

您可以使用DateTime.AddDays()函数将您的 代码添加DayInterval到中,StartDate然后检查以确保它小于EndDate


0

您必须在这里小心,不要错过循环中更好的解决方案的日期。

这将为您提供开始日期的第一个日期,并在递增日期之前在循环中使用它,它将处理所有日期,包括结束日期的最后日期,因此<=结束日期。

所以以上答案是正确的。

while (startdate <= enddate)
{
    // do something with the startdate
    startdate = startdate.adddays(interval);
}

0

你可以用这个

 DateTime dt0 = new DateTime(2009, 3, 10);
 DateTime dt1 = new DateTime(2009, 3, 26);

 for (; dt0.Date <= dt1.Date; dt0=dt0.AddDays(3))
 {
    //Console.WriteLine(dt0.Date.ToString("yyyy-MM-dd"));
    //take action
 }

真的很简洁。真好!
onekidney

0

每15分钟进行一次迭代

DateTime startDate = DateTime.Parse("2018-06-24 06:00");
        DateTime endDate = DateTime.Parse("2018-06-24 11:45");

        while (startDate.AddMinutes(15) <= endDate)
        {

            Console.WriteLine(startDate.ToString("yyyy-MM-dd HH:mm"));
            startDate = startDate.AddMinutes(15);
        }

0

@ jacob-sobus和@mquander和@Yogurt不太正确。如果我需要第二天,我通常要等待00:00时间

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

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

    public static IEnumerable<DateTime> EachYear(DateTime from, DateTime thru)
    {
        for (var year = from.Date; year.Date <= thru.Date || year.Year == thru.Year; year = year.NextYear())
            yield return year;
    }

    public static DateTime NextDay(this DateTime date)
    {
        return date.AddTicks(TimeSpan.TicksPerDay - date.TimeOfDay.Ticks);
    }

    public static DateTime NextMonth(this DateTime date)
    {
        return date.AddTicks(TimeSpan.TicksPerDay * DateTime.DaysInMonth(date.Year, date.Month) - (date.TimeOfDay.Ticks + TimeSpan.TicksPerDay * (date.Day - 1)));
    }

    public static DateTime NextYear(this DateTime date)
    {
        var yearTicks = (new DateTime(date.Year + 1, 1, 1) - new DateTime(date.Year, 1, 1)).Ticks;
        var ticks = (date - new DateTime(date.Year, 1, 1)).Ticks;
        return date.AddTicks(yearTicks - ticks);
    }

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

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

0

这是我在2020年的2美分。

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

这太棒了。👍
onekidney
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.