#c# #.net-2.0
#c# #.net-2.0
Вопрос:
Предположим, у меня есть пользовательский интерфейс, в котором пользователь может выбирать дни. Есть ли способ проверить, являются ли выбранные дни последовательными, например:
4/4, 4/5, 4/6, 4/7, 4/8, 4/9, 4/10 или
4/29, 4/30, 5/1, 5/2, 5/3
Я знаю, что, вероятно, могу перебрать диапазон дат и проверить, но мне было более любопытно, существует ли уже встроенный метод для проверки этого.
Что касается вышеупомянутых сценариев, они в порядке, и их можно перенести на следующий месяц.
Я использую .NET Framework 2.0 и не могу использовать LINQ.
Что касается ответа Тома:
DateTime dtStart = new DateTime(2011,5,4);
DateTime dtEnd = new DateTime(2011,5,11);
int numberOfDaysSelected = 7; //Assume 7 days were selected.
TimeSpan ts = dtEnd - dtStart;
if(ts.Days == numberOfDaysSelected - 1)
{
Console.WriteLine("Sequential");
}
else
{
Console.WriteLine("Non-Sequential");
}
Комментарии:
1. должно быть
if(ts.Days == numberOfDaysSelected-1)
, поскольку для двух последовательных дней у вас есть промежуток времени в один день, а не два.2. Если начальная дата — 5/4/2011, а конечная — 5/11/2011, то фактически было выбрано всего 8 дней (5/4, 5/5, 5/6, 5/7, 5/8, 5/9, 5/10, и 5/11). Вам необходимо принять во внимание исправление BrokenGlass, упомянутое в комментарии выше. Я отредактирую свой ответ, чтобы отразить требуемое значение «-1».
Ответ №1:
Я не верю, что существует встроенный метод для достижения желаемых результатов, но если вы можете легко определить самые ранние и самые поздние даты, вы могли бы создать новый временной интервал, вычтя самую раннюю дату из самой последней даты, а затем проверив, что количество дней временного интервала соответствует количеству выбранных дат — 1.
Комментарии:
1. 1 если я чего-то не упускаю, это хорошее решение проблемы
2. 1, потому что я считаю, что это самое удобное решение. Обратите внимание, однако, что для этого требуется, чтобы в вашем списке дат не было дубликатов.
3. Это отличное решение, и их дублирование невозможно.
4. -1 потому что это предполагает, что список дат является уникальным и упорядоченным.
5. @Nicholas, но получение упорядоченного уникального списка — это тривиальная операция.
var shinyList = origList.OrderBy(d => d.Day).Distinct();
Ответ №2:
Вы не сказали нам, упорядочены ли дни.
Вы не сказали нам, могут ли они превышать границу месяца, как в
30, 31, 1.
Я предположу, что они упорядочены, и я предполагаю, что они не превысят границу месяца (потому что ваш пример упорядочен, и он не превышает границу месяца).
Тогда вы можете сказать
public bool IsSequential(this IEnumerable<DateTime> sequence) {
Contract.Requires(sequence != null);
var e = sequence.GetEnumerator();
if(!e.MoveNext()) {
// empty sequence is sequential
return true;
}
int previous = e.Current.Date;
while(e.MoveNext()) {
if(e.Current.Date != previous.AddDays(1)) {
return false;
}
previous = e.Current.Date;
}
return true;
}
Обратите внимание, что для этого решения требуется только один раз пройти последовательность. Если у вас нет упорядоченной последовательности или вы допускаете превышение месячной границы, решение более сложное.
Комментарии:
1. Обновленный пост. Они в порядке, и их можно переносить. Я обновил сообщение двумя сценариями.
2. @Xaisoft: Вводятся ли они как экземпляры
DateTime
?3. -1, потому что реализация этого с помощью
int
, а неDateTime
злоупотребляет фреймворком и накладывает ненужные ограничения. Я думаю, что у Тома есть лучший ответ.4. @Jason, да, это экземпляры DateTime. Что такое контракт в вашем коде. Требуется?
5. @JSBangs, имеет ли это значение в данном случае, найдете ли вы когда-нибудь день, который не является целым числом.
Ответ №3:
Ничего встроенного, но вы можете легко создать его с помощью Linq:
List<DateTime> timeList = new List<DateTime>();
//populate list..
bool isSequential = timeList.Zip(timeList.Skip(1),
(a, b) => b.Date == a.Date.AddDays(1))
.All(x => x);
Отредактированный — неправильно понятый вопрос, сначала означающий возрастание во времени, а не последовательный — исправлено.
Комментарии:
1. Одна проблема: я все еще использую .NET 2.0 и не могу использовать LINQ.
2. @Xaisoft: Тогда игнорируйте это решение, оно применимо только к .NET 4 — подход @Tom’s должен сработать.
Ответ №4:
Метод расширения с использованием Linq:
public static bool IsContiguous(this IEnumerable<DateTime> dates)
{
var startDate = dates.FirstOrDefault();
if (startDate == null)
return true;
//.All() doesn't provide an indexed overload :(
return dates
.Select((d, i) => new { Date = d, Index = i })
.All(d => (d.Date - startDate).Days == d.Index);
}
Тестирование:
List<DateTime> contiguousDates = new List<DateTime>
{
new DateTime(2011, 05, 05),
new DateTime(2011, 05, 06),
new DateTime(2011, 05, 07),
};
List<DateTime> randomDates = new List<DateTime>
{
new DateTime(2011, 05, 05),
new DateTime(2011, 05, 07),
new DateTime(2011, 05, 08),
};
Console.WriteLine(contiguousDates.IsContiguous());
Console.WriteLine(randomDates.IsContiguous());
ВОЗВРАТ
True
False
Редактировать:
.NET 2-подобный ответ:
public static bool CheckContiguousDates(DateTime[] dates)
{
//assuming not null and count > 0
var startDate = dates[0];
for (int i = 0; i < dates.Length; i )
{
if ((dates[i] - startDate).Days != i)
return false;
}
return true;
}
Комментарии:
1. Все еще на .NET 2 и не могу использовать LINQ.
Ответ №5:
Вы можете использовать TimeGapCalculator из библиотеки временных периодов для .NET, чтобы находить промежутки между несколькими периодами времени (независимо от порядка, количества и перекрытия):
// ----------------------------------------------------------------------
public void SequentialPeriodsDemo()
{
// sequential
ITimePeriodCollection periods = new TimePeriodCollection();
periods.Add( new Days( new DateTime( 2011, 5, 4 ), 2 ) );
periods.Add( new Days( new DateTime( 2011, 5, 6 ), 3 ) );
Console.WriteLine( "Sequential: " IsSequential( periods ) );
periods.Add( new Days( new DateTime( 2011, 5, 10 ), 1 ) );
Console.WriteLine( "Sequential: " IsSequential( periods ) );
} // SequentialPeriodsDemo
// --------------------------------------------------------------------
public bool IsSequential( ITimePeriodCollection periods, ITimePeriod limits = null )
{
return new TimeGapCalculator<TimeRange>(
new TimeCalendar() ).GetGaps( periods, limits ).Count == 0;
} // IsSequential