Как мне перебрать диапазон дат? - PullRequest
171 голосов
/ 04 декабря 2009

Я даже не уверен, как это сделать, не используя какое-то ужасное решение для типа петля / счетчик. Вот проблема:

Мне дают две даты, дату начала и дату окончания, и в указанный интервал мне нужно предпринять некоторые действия. Например: для каждой даты с 3 октября 2009 года по третий день до 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;
     }

}

Но похоже, что может быть лучший способ?

Ответы [ 15 ]

421 голосов
/ 04 декабря 2009

Ну, вам нужно перебрать их так или иначе. Я предпочитаю определять такой метод:

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).

28 голосов
/ 04 декабря 2009

У меня есть Range класс в MiscUtil , который вы можете найти полезным. В сочетании с различными методами расширения вы можете сделать:

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

(Вы можете или не хотите исключать конец - я просто подумал, что приведу его в качестве примера.)

Это в основном готовая (и более универсальная) форма решения mquander.

19 голосов
/ 04 декабря 2009

Для вашего примера вы можете попробовать

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);
}
12 голосов
/ 26 сентября 2014

Код от @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);
}
7 голосов
/ 04 декабря 2009
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 голосов
/ 04 октября 2016

1 год спустя, может это кому-нибудь помочь,

Эта версия включает в себя предикат , чтобы быть более гибким.

Использование

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, но я предпочитаю возвращать пустой диапазон вместо []

3 голосов
/ 04 декабря 2009
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
}
2 голосов
/ 09 апреля 2015

В зависимости от проблемы вы можете попробовать это ...

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

    startDate = startDate.AddDays(1);
}

спасибо ......

1 голос
/ 12 февраля 2018
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 голос
/ 04 декабря 2009

Вместо этого вы можете написать итератор, который позволит вам использовать обычный синтаксис цикла for, такой как ++. Я искал и нашел похожий вопрос ответил здесь, в StackOverflow, который дает указатели на то, чтобы сделать DateTime повторяемым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...