Как мне просмотреть диапазон дат?


198

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

Мне дают две даты, дату начала и дату окончания, и по указанному интервалу мне нужно предпринять некоторые действия. Например: для каждой даты с 3 октября 2009 года по третий день до 26 марта 2009 года мне нужно создать запись в списке. Так что мой вклад будет:

DateTime StartDate = "3/10/2009";
DateTime EndDate = "3/26/2009";
int DayInterval = 3;

и мой вывод будет список, который имеет следующие даты:

13.03.2009 16.03.2009 19.03.2009 22.03.2009 25.03.2009

Так как, черт возьми, я бы сделал что-то подобное? Я подумал об использовании цикла for, который будет перебирать каждый день в диапазоне с отдельным счетчиком следующим образом:

int count = 0;

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

}

Но кажется, что может быть лучше?


1
Я предполагаю, что C # имеет структуру данных для дат, которые вы можете использовать.
Анна

Ответы:


473

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

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

3
Действительно хорошее решение для интересной и реальной проблемы слова. Мне нравится, как это показывает довольно много полезных методов языка. И это напоминает мне, что цикл for предназначен не только для (int i = 0; ...) (-.
Audrius

9
Создание этого метода расширения для datetime может сделать его еще лучше.
MatteS

1
Посмотрите на мой ответ для продлений на дни и месяцы;) Просто для вашего удовольствия: D
Джейкоб Собус

32

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

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

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

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


2
Конечно, просто вопрос вкуса, нравится ли вам такие вещи как методы расширения или нет. ExcludeEnd()милый.
MQP

Конечно, вы можете делать все это без использования методов расширения. Просто будет намного страшнее и сложнее читать ИМО :)
Джон Скит

1
Ух ты - какой замечательный ресурс MiscUtil - спасибо за твой ответ!
onekidney

1
в случае, если кто-то, кроме меня, принял DayInterval как структуру / класс, в данном примере это целое число. это, конечно, очевидно, если вы внимательно прочитаете вопрос, а я нет.
марта

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 выше), но я не могу понять, как вы получили хороший пример кода, размещенный так быстро!
TLiebe

3
Я думаю, что нам нужно StartDate.AddDays (DayInterval); один раз в этом цикле не дважды.
Абдул Сабур

15

Код из @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

@ Alisson - это методы расширения, работающие с объектом dateFrom :). Вы можете использовать их уже на созданных объектах DateTime более свободно (используя только после экземпляра). Подробнее о методах расширения здесь: docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
Джейкоб Собус

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

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


Вау - это действительно всеобъемлющее. Спасибо Ахмад!
onekidney

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.
Джон Уошам

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, который дает указатели на то, чтобы сделать DateTime итеративным.


1

Вы можете использовать DateTime.AddDays()функцию , чтобы добавить свой DayIntervalк StartDateи проверьте , чтобы убедиться сделать это меньше EndDate.


0

Вы должны быть осторожны, чтобы не пропустить даты, когда в цикле будет лучшее решение.

это дает вам первую дату начальной даты и использует ее в цикле перед ее увеличением, и она обработает все даты, включая последнюю дату конечной даты, следовательно, <= enddate.

поэтому приведенный выше ответ является правильным.

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

Вот мои 2 цента в 2020 году.

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

Это круто. One
онкидней
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.