Как мне перебрать диапазон дат?
Я даже не уверен, как это сделать, не используя какое-то ужасное решение для цикла / счетчика. Вот проблема:
Мне дают две даты, дату начала и дату окончания, и в указанный интервал мне нужно предпринять некоторые действия. Например: для каждой даты с 3/10/2009 в каждый третий день до 26.03.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;
}
}
Но похоже, что может быть лучше?
17 ответов
Ну, вам нужно перебрать их так или иначе. Я предпочитаю определять такой метод:
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)
,
У меня есть Range
класс в MiscUtil, который вы можете найти полезным. В сочетании с различными методами расширения вы можете сделать:
foreach (DateTime date in StartDate.To(EndDate).ExcludeEnd()
.Step(DayInterval.Days())
{
// Do something with the date
}
(Вы можете или не хотите исключать конец - я просто подумал, что приведу это в качестве примера.)
Это в основном готовая (и более универсальная) форма решения mquander.
Для вашего примера вы можете попробовать
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);
}
Код из @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);
}
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
, но я предпочитаю возвращать пустой диапазон []
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))
{
}
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);
}
В зависимости от проблемы вы можете попробовать это...
// looping between date range
while (startDate <= endDate)
{
//here will be your code block...
startDate = startDate.AddDays(1);
}
Спасибо......
Вот мои 2 цента в 2020 году.
Enumerable.Range(0, (endDate - startDate).Days + 1)
.ToList()
.Select(a => startDate.AddDays(a));
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
}
Вы можете использовать DateTime.AddDays()
функция, чтобы добавить свой DayInterval
к StartDate
и убедитесь, что он меньше, чем EndDate
,
Вы можете вместо этого написать итератор, который позволит вам использовать обычный синтаксис цикла for, например '++'. Я искал и нашел похожий вопрос, на который ответили здесь, в Stackru, который дает указатели на то, чтобы сделать DateTime итеративным.
Вы можете использовать это.
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
}
Если вы конвертируете свои даты в OADate, вы можете перебирать их, как если бы вы делали это с любым
double
количество.
DateTime startDate = new DateTime(2022, 1, 1);
DateTime endDate = new DateTime(2022, 12, 31);
for (double loopDate = startDate.ToOADate(); loopDate <= endDate.ToOADate(); loopDate++)
{
DateTime selectedDate;
selectedDate = DateTime.FromOADate(loopDate);
}
Повторять каждые 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);
}
@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);
}
Вы должны быть осторожны, чтобы не пропустить даты, когда в цикле будет лучшее решение.
это дает вам первую дату начальной даты и использует ее в цикле перед ее увеличением, и она будет обрабатывать все даты, включая последнюю дату конечной даты, следовательно, <= enddate.
поэтому приведенный выше ответ является правильным.
while (startdate <= enddate)
{
// do something with the startdate
startdate = startdate.adddays(interval);
}