




Начните с объекта с фиксированной датой (1 января 1995 г.) и добавьте случайное количество дней с помощью AddDays (очевидно, обратите внимание, не превышайте текущую дату).
Спасибо, Фриоль. Я собирался спросить, как ограничить число, переданное в случайное. Джоэл опубликовал пример с образцом кода, поэтому я отмечу его ответ как ответ.
private Random gen = new Random();
DateTime RandomDay()
{
DateTime start = new DateTime(1995, 1, 1);
int range = (DateTime.Today - start).Days;
return start.AddDays(gen.Next(range));
}
Для повышения производительности, если это будет вызываться повторно, создайте переменные вне функции start и gen (и, возможно, даже range).
Случайное - только псевдослучайное. Если вам нужен действительно случайный, попробуйте использовать RNGCryptoServiceProvider из пространства имен System.Security.Cryptography.
Спасибо tvanfosson. Для этой задачи достаточно псевдослучайной последовательности.
На самом деле Random даже не является особенно псевдослучайным, если вы не держите экземпляр какое-то время и продолжаете получать из него значения.
+1 за комментарий Дэвида. Если вы вызовете вышеуказанный метод дважды в быстрой последовательности, он с большей вероятностью вернет одно и то же значение. Конструктор Random выше использует текущее время в качестве начального числа, которым является DateTime.Now. Это значение увеличивается каждые 15 миллисекунд. Поле было бы лучше.
Вот почему это всего лишь образец, а не производственный код.
Да, это работает для меня; мой реальный код будет иметь экземпляр Random вне самого метода.
@Ronnie, ваше редактирование вызвало ошибку компиляции (невозможно неявно преобразовать тип double в int), пожалуйста, не меняйте код других людей, и если вы это сделаете, будьте особенно осторожны и проверьте, прежде чем делать это. Спасибо.
@ShadowWizard Мое плохое. Я думал, что дни не являются самой важной частью TimeSpan, и поэтому разница между днями и полными днями будет очень заметной, как и в случае с минутами.
Включает ли результат сегодня?
@SalmanA Вы можете быстро это узнать, если в качестве эпохи использовать вчерашний день, а не 1995 год. Похоже на ответ - нет".
@Joel Просто добавьте 1 к диапазону. OP не ясно, нужно ли включать сегодня, но вы всегда можете дать четкий ответ о крайнем случае.
Это небольшой ответ на комментарий Джоэла о создании немного более оптимизированной версии. Вместо того, чтобы напрямую возвращать случайную дату, почему бы не вернуть функцию генератора, которую можно вызывать повторно для создания случайной даты.
Func<DateTime> RandomDayFunc()
{
DateTime start = new DateTime(1995, 1, 1);
Random gen = new Random();
int range = ((TimeSpan)(DateTime.Today - start)).Days;
return () => start.AddDays(gen.Next(range));
}
Вы можете объяснить, чем это выгодно? Не могли бы вместо этого быть членами класса start, gen и range?
Могли, и в данном случае они есть. Под капотом это сгенерирует лексическое замыкание, которое является классом, содержащим start, gen и range в качестве членов. Это просто более кратко.
Хорошая функция, я просто надеюсь, что никто не будет использовать ее как: for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); }
Как эта функция используется, кто-нибудь может объяснить? Я имею в виду, как я могу это назвать?
@ BurakKarakuş: Сначала вы получаете фабрику: var getRandomDate = RandomDayFunc();, затем вызываете ее, чтобы получить случайные даты: var randomDate = getRandomDate(); Имейте в виду, что вам нужно повторно использовать getRandomDate, чтобы это было более полезным, чем ответ Джоэла.
Что ж, если вы собираетесь представить альтернативную оптимизацию, мы также можем использовать итератор:
static IEnumerable<DateTime> RandomDay()
{
DateTime start = new DateTime(1995, 1, 1);
Random gen = new Random();
int range = ((TimeSpan)(DateTime.Today - start)).Days;
while (true)
yield return start.AddDays(gen.Next(range));
}
вы можете использовать это так:
int i=0;
foreach(DateTime dt in RandomDay())
{
Console.WriteLine(dt);
if (++i == 10)
break;
}
При выборе между итератором и функцией-генератором следует учитывать, что решение итератора будет выдавать значение IDisposable. Это вынуждает вызывающую программу избавиться от нее или заплатить цену за то, чтобы финализатор находился в GC. Генератор не требует утилизации
@JaredPar, это не совсем так. Тот факт, что тип реализует IDisposable, не означает, что его можно завершить.
Я взял ответ @Joel Coehoorn и внес изменения, которые он посоветовал - убрать переменную из метода и поместить все в класс. Плюс теперь время тоже случайное. Вот результат.
class RandomDateTime
{
DateTime start;
Random gen;
int range;
public RandomDateTime()
{
start = new DateTime(1995, 1, 1);
gen = new Random();
range = (DateTime.Today - start).Days;
}
public DateTime Next()
{
return start.AddDays(gen.Next(range)).AddHours(gen.Next(0,24)).AddMinutes(gen.Next(0,60)).AddSeconds(gen.Next(0,60));
}
}
И пример того, как использовать для записи 100 случайных значений DateTime на консоль:
RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
Console.WriteLine(date.Next());
}
Почему вы создаете Random () дважды? Один раз в объявлении переменной класса gen, а другой раз в c-tor?
Да, одного раза достаточно. Я починил это.
Примерно в четыре раза быстрее сгенерировать всего одно случайное число секунд и добавить его к дате начала: range = (int)(DateTime.Today - start).TotalSeconds; и return start.AddSeconds(gen.Next(range));.
Я немного опоздал в игру, но вот одно решение, которое отлично работает:
void Main()
{
var dateResult = GetRandomDates(new DateTime(1995, 1, 1), DateTime.UtcNow, 100);
foreach (var r in dateResult)
Console.WriteLine(r);
}
public static IList<DateTime> GetRandomDates(DateTime startDate, DateTime maxDate, int range)
{
var randomResult = GetRandomNumbers(range).ToArray();
var calculationValue = maxDate.Subtract(startDate).TotalMinutes / int.MaxValue;
var dateResults = randomResult.Select(s => startDate.AddMinutes(s * calculationValue)).ToList();
return dateResults;
}
public static IEnumerable<int> GetRandomNumbers(int size)
{
var data = new byte[4];
using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(data))
{
for (int i = 0; i < size; i++)
{
rng.GetBytes(data);
var value = BitConverter.ToInt32(data, 0);
yield return value < 0 ? value * -1 : value;
}
}
}
Небольшой метод, который возвращает случайную дату в виде строки на основе некоторых простых входных параметров. Построен на основе вариантов из приведенных выше ответов:
public string RandomDate(int startYear = 1960, string outputDateFormat = "yyyy-MM-dd")
{
DateTime start = new DateTime(startYear, 1, 1);
Random gen = new Random(Guid.NewGuid().GetHashCode());
int range = (DateTime.Today - start).Days;
return start.AddDays(gen.Next(range)).ToString(outputDateFormat);
}
Полезное расширение на основе решения @Jeremy Thompson
public static class RandomExtensions
{
public static DateTime Next(this Random random, DateTime start, DateTime? end = null)
{
end ??= new DateTime();
int range = (end.Value - start).Days;
return start.AddDays(random.Next(range));
}
}
new DateTime() - это 01/01/0001 00:00:00. Вы уверены, что?
Ответ в Случайная дата и время между диапазоном - не унифицированный вывод имеет вспомогательный метод с параметрами From / To date