Генерация случайной даты между двумя другими датами


138

Как бы я сгенерировал случайную дату, которая должна быть между двумя другими данными датами?

Подпись функции должна выглядеть примерно так:

random_date("1/1/2008 1:30 PM", "1/1/2009 4:50 AM", 0.34)
                   ^                       ^          ^

            date generated has  date generated has  a random number
            to be after this    to be before this

и вернет дату, такую ​​как: 2/4/2008 7:20 PM


То, как вопрос представлен в данный момент, неясно, хотите ли вы, чтобы дата или время были случайными или нет. Ваш пример показывает, что вы ищете время. Если это должно быть между двумя датами, вы можете изменить ответы, приведенные до сих пор, в соответствии с вашими потребностями и исключить время окончания и время начала. Наконец, в большинстве ответов, таких как принятый, код выводит дату и время, исключая время окончания из-за усечения до int. Чтобы сгенерировать время, которое может включать в себя конец в ответе, измените код наptime = stime + prop * (etime - stime) + 0.5
tortal

Ответы:


149

Преобразуйте обе строки в метки времени (в выбранном вами разрешении, например, в миллисекундах, секундах, часах, днях и т. Д.), Вычтите предыдущее из более позднего, умножьте ваше случайное число (если оно распределено в range [0, 1]) на эту разницу и добавьте снова к предыдущий. Преобразуйте метку времени обратно в строку даты, и у вас будет случайное время в этом диапазоне.

Пример Python (вывод почти в указанном вами формате, кроме 0заполнения - обвинять американские соглашения о формате времени):

import random
import time

def str_time_prop(start, end, format, prop):
    """Get a time at a proportion of a range of two formatted times.

    start and end should be strings specifying times formated in the
    given format (strftime-style), giving an interval [start, end].
    prop specifies how a proportion of the interval to be taken after
    start.  The returned time will be in the specified format.
    """

    stime = time.mktime(time.strptime(start, format))
    etime = time.mktime(time.strptime(end, format))

    ptime = stime + prop * (etime - stime)

    return time.strftime(format, time.localtime(ptime))


def random_date(start, end, prop):
    return str_time_prop(start, end, '%m/%d/%Y %I:%M %p', prop)

print(random_date("1/1/2008 1:30 PM", "1/1/2009 4:50 AM", random.random()))

Этот подход не поддерживает даты, начинающиеся до 1970 года.
Cmbone

114
from random import randrange
from datetime import timedelta

def random_date(start, end):
    """
    This function will return a random datetime between two datetime 
    objects.
    """
    delta = end - start
    int_delta = (delta.days * 24 * 60 * 60) + delta.seconds
    random_second = randrange(int_delta)
    return start + timedelta(seconds=random_second)

Точность составляет секунды. При желании вы можете повысить точность до микросекунд или, скажем, до получаса. Для этого просто измените расчет последней строки.

пример выполнения:

from datetime import datetime

d1 = datetime.strptime('1/1/2008 1:30 PM', '%m/%d/%Y %I:%M %p')
d2 = datetime.strptime('1/1/2009 4:50 AM', '%m/%d/%Y %I:%M %p')

print(random_date(d1, d2))

вывод:

2008-12-04 01:50:17

3
Использование startпеременной в этом случае совершенно правильно. Единственная проблема, которую я вижу в коде, это использование secondsатрибута из результирующего delta. Это не вернуло бы общее количество секунд за весь интервал; вместо этого это просто количество секунд от компонента time (что-то между 0 и 60); у timedeltaобъекта есть total_secondsметод, который следует использовать вместо него.
emyller

7
@emyller: Нет, я использую (delta.days * 24 * 60 * 60) + delta.secondsрезультаты, которые дают общее количество секунд. Этот total_seconds()метод является новым в Python 2.7 и не существовал в 2009 году, когда я ответил на вопрос. Если у вас есть Python 2.7, вы должны использовать его вместо этого, но код работает нормально, как есть.
Носкло

Я не знал о существовании этого метода еще в 2.7-. Я только что проверил, что объект timedelta в основном состоит из чисел дней и секунд, так что вы правы. :-)
emyller

@emyller: просто для полноты объект timedelta состоит из дней, секунд и микросекунд . Точность генерации кода случайной даты выше - до нескольких секунд, но ее можно изменить, как я уже упоминал в ответе.
Носкло

83

Крошечная версия.

import datetime
import random


def random_date(start, end):
    """Generate a random datetime between `start` and `end`"""
    return start + datetime.timedelta(
        # Get a random amount of seconds between `start` and `end`
        seconds=random.randint(0, int((end - start).total_seconds())),
    )

Обратите внимание, что оба startи endаргументы должны быть datetimeобъектами. Если у вас есть строки вместо этого, это довольно легко конвертировать. Другие ответы указывают на некоторые способы сделать это.


54

Обновленный ответ

Еще проще использовать Faker .

Монтаж

pip install faker

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

from faker import Faker
fake = Faker()

fake.date_between(start_date='today', end_date='+30y')
# datetime.date(2025, 3, 12)

fake.date_time_between(start_date='-30y', end_date='now')
# datetime.datetime(2007, 2, 28, 11, 28, 16)

# Or if you need a more specific date boundaries, provide the start 
# and end dates explicitly.
import datetime
start_date = datetime.date(year=2015, month=1, day=1)
fake.date_between(start_date=start_date, end_date='+30y')

Старый ответ

Это очень просто с помощью радара

Монтаж

pip install radar

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

import datetime

import radar 

# Generate random datetime (parsing dates from str values)
radar.random_datetime(start='2000-05-24', stop='2013-05-24T23:59:59')

# Generate random datetime from datetime.datetime values
radar.random_datetime(
    start = datetime.datetime(year=2000, month=5, day=24),
    stop = datetime.datetime(year=2013, month=5, day=24)
)

# Just render some random datetime. If no range is given, start defaults to 
# 1970-01-01 and stop defaults to datetime.datetime.now()
radar.random_datetime()

3
upvote для предложения модуля фейера. Я использовал для создания профиля, но не использовал утилиту даты, фейкер - очень хороший модуль во время тестирования.
Гахан

Я получаю вывод в этом формате, datetime.date(2039, 3, 16)но я хочу вывод, как это 2039-03-16. Как это сделать?
Аюш Кумар

Ты имеешь в виду, ты хочешь строку? Очень просто (просто отформатируйте его соответственно) fake.date_between(start_date='today', end_date='+30y').strftime('%Y-%m-%d').
Артур Барсегян

1
Upvote для использования невероятной библиотеки, даже если вам нужно ее установить. Это снижает сложность реализации по существу до 4 строк.
Blairg23

1
@ KubiK888: Конечно, смотрите мой ответ обновления. Вы должны просто предоставить start_date явно.
Артур Барсегян

24

Это другой подход - такого рода работы ..

from random import randint
import datetime

date=datetime.date(randint(2005,2025), randint(1,12),randint(1,28))

ЛУЧШИЙ ПОДХОД

startdate=datetime.date(YYYY,MM,DD)
date=startdate+datetime.timedelta(randint(1,365))

1
Первый подход никогда не выберет дату, заканчивающуюся 29-го, 30-го или 31-го, и ваш второй подход не учитывает високосные годы, когда год составляет 366 дней, т.е. если startdate+ 1 год проходит до 31 декабря в високосный год, это Код никогда не выберет ту же дату ровно через год. Оба подхода позволяют только указать дату начала и количество лет в будущем, тогда как вопрос задавался об указании двух дат, и, на мой взгляд, это более полезный API.
Борис

15

Поскольку Python 3 timedeltaподдерживает умножение с помощью чисел с плавающей точкой, теперь вы можете сделать:

import random
random_date = start + (end - start) * random.random()

учитывая, что startи endимеют тип datetime.datetime. Например, чтобы сгенерировать случайное время в течение следующего дня:

import random
from datetime import datetime, timedelta

start = datetime.now()
end = start + timedelta(days=1)
random_date = start + (end - start) * random.random()

6

Для чипа в решении на основе панд я использую:

import pandas as pd
import numpy as np

def random_date(start, end, position=None):
    start, end = pd.Timestamp(start), pd.Timestamp(end)
    delta = (end - start).total_seconds()
    if position is None:
        offset = np.random.uniform(0., delta)
    else:
        offset = position * delta
    offset = pd.offsets.Second(offset)
    t = start + offset
    return t

Мне это нравится, из-за приятных pd.Timestampфункций, которые позволяют мне создавать разные вещи и форматы. Рассмотрим следующие несколько примеров ...

Ваша подпись.

>>> random_date(start="1/1/2008 1:30 PM", end="1/1/2009 4:50 AM", position=0.34)
Timestamp('2008-05-04 21:06:48', tz=None)

Случайная позиция.

>>> random_date(start="1/1/2008 1:30 PM", end="1/1/2009 4:50 AM")
Timestamp('2008-10-21 05:30:10', tz=None)

Разный формат.

>>> random_date('2008-01-01 13:30', '2009-01-01 4:50')
Timestamp('2008-11-18 17:20:19', tz=None)

Передача объектов панда / дата / время напрямую.

>>> random_date(pd.datetime.now(), pd.datetime.now() + pd.offsets.Hour(3))
Timestamp('2014-03-06 14:51:16.035965', tz=None)

И как бы вы элегантно создали случайную серию дата-время (т.е. без итерации своей функции для каждого элемента)?
dmvianna

Что ж, возможно, возможно изменить функцию, чтобы сгенерировать массив deltaзначений и сопоставить их все одновременно с отметками времени. Лично я предпочел бы просто сделать что-то подобное pd.Series([5] * 10, [random_date('2014-01-01', '2014-01-30') for i in range(10)]).
metakermit

3

Вот ответ на буквальное значение названия, а не тела этого вопроса:

import time
import datetime
import random

def date_to_timestamp(d) :
  return int(time.mktime(d.timetuple()))

def randomDate(start, end):
  """Get a random date between two dates"""

  stime = date_to_timestamp(start)
  etime = date_to_timestamp(end)

  ptime = stime + random.random() * (etime - stime)

  return datetime.date.fromtimestamp(ptime)

Этот код основан на принятом ответе.


Вы могли бы изменить вторую последнюю строку на ptime = random.randint(stime, etime)чуть более правильную, потому что она randintдает широкий диапазон.
Борис

3

Вы можете использовать Mixer,

pip install mixer

и,

from mixer import generators as gen
print gen.get_datetime(min_datetime=(1900, 1, 1, 0, 0, 0), max_datetime=(2020, 12, 31, 23, 59, 59))

1
Синтаксис немного изменился, не знаю, как это сделать, но у объекта django случайная дата будет заполнена примерно так:client = mixer.blend(Client, date=mixer.RANDOM)
tutuDajuju

@tutuDajuju: Что означает Клиент?
Нима Соруш

Согласно их документам , это может быть класс моделей Django, SQLAlchemy или Mongoengine.
tutuDajuju

2
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Create random datetime object."""

from datetime import datetime
import random


def create_random_datetime(from_date, to_date, rand_type='uniform'):
    """
    Create random date within timeframe.

    Parameters
    ----------
    from_date : datetime object
    to_date : datetime object
    rand_type : {'uniform'}

    Examples
    --------
    >>> random.seed(28041990)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(1998, 12, 13, 23, 38, 0, 121628)
    >>> create_random_datetime(datetime(1990, 4, 28), datetime(2000, 12, 31))
    datetime.datetime(2000, 3, 19, 19, 24, 31, 193940)
    """
    delta = to_date - from_date
    if rand_type == 'uniform':
        rand = random.random()
    else:
        raise NotImplementedError('Unknown random mode \'{}\''
                                  .format(rand_type))
    return from_date + rand * delta


if __name__ == '__main__':
    import doctest
    doctest.testmod()

2

Преобразуйте свои даты в метки времени и позвоните random.randintс метками времени, а затем конвертируйте случайно сгенерированную метку времени обратно в дату:

from datetime import datetime
import random

def random_date(first_date, second_date):
    first_timestamp = int(first_date.timestamp())
    second_timestamp = int(second_date.timestamp())
    random_timestamp = random.randint(first_timestamp, second_timestamp)
    return datetime.fromtimestamp(random_timestamp)

Тогда вы можете использовать это так

from datetime import datetime

d1 = datetime.strptime("1/1/2018 1:30 PM", "%m/%d/%Y %I:%M %p")
d2 = datetime.strptime("1/1/2019 4:50 AM", "%m/%d/%Y %I:%M %p")

random_date(d1, d2)

random_date(d2, d1)  # ValueError because the first date comes after the second date

Если вы заботитесь о часовых поясах , вы должны использовать только date_time_between_datesиз Fakerбиблиотеки, где я украла этот код из , как другой ответ уже предлагает.


1
  1. Преобразуйте введенные даты в числа (int, float, все, что лучше для вас)
  2. Выберите число между вашими двумя числами даты.
  3. Преобразовать это число обратно в дату.

Многие алгоритмы преобразования даты в числа и из них уже доступны во многих операционных системах.


1

Зачем вам нужно случайное число? Обычно (в зависимости от языка) вы можете получить количество секунд / миллисекунд от эпохи до даты. Так что для случайной даты между startDate и endDate вы можете сделать:

  1. вычислить время в мс между startDate и endDate (endDate.toMilliseconds () - startDate.toMilliseconds ())
  2. сгенерировать число от 0 до числа, которое вы получили в 1
  3. создать новую дату со смещением времени = startDate.toMilliseconds () + число, полученное в 2

1

Самый простой способ сделать это - преобразовать оба числа в метки времени, а затем установить их как минимальные и максимальные границы в генераторе случайных чисел.

Быстрый пример PHP будет:

// Find a randomDate between $start_date and $end_date
function randomDate($start_date, $end_date)
{
    // Convert to timetamps
    $min = strtotime($start_date);
    $max = strtotime($end_date);

    // Generate random number using above bounds
    $val = rand($min, $max);

    // Convert back to desired date format
    return date('Y-m-d H:i:s', $val);
}

Эта функция использует strtotime()для преобразования описания datetime в метку времени Unix и date()для создания действительной даты из случайной метки времени, которая была сгенерирована.


Если кто-нибудь может написать это на python, это было бы полезно.
Quilby

1

Просто чтобы добавить еще один:

datestring = datetime.datetime.strftime(datetime.datetime( \
    random.randint(2000, 2015), \
    random.randint(1, 12), \
    random.randint(1, 28), \
    random.randrange(23), \
    random.randrange(59), \
    random.randrange(59), \
    random.randrange(1000000)), '%Y-%m-%d %H:%M:%S')

Обработка дня требует некоторых соображений. С 28 вы на безопасном сайте.


1

Вот решение, измененное на основе подхода Эмиллера, которое возвращает массив случайных дат при любом разрешении

import numpy as np

def random_dates(start, end, size=1, resolution='s'):
    """
    Returns an array of random dates in the interval [start, end]. Valid 
    resolution arguments are numpy date/time units, as documented at: 
        https://docs.scipy.org/doc/numpy-dev/reference/arrays.datetime.html
    """
    start, end = np.datetime64(start), np.datetime64(end)
    delta = (end-start).astype('timedelta64[{}]'.format(resolution))
    delta_mat = np.random.randint(0, delta.astype('int'), size)
    return start + delta_mat.astype('timedelta64[{}]'.format(resolution))

Частью этого подхода является то, что np.datetime64он действительно хорош для приведения вещей в соответствие с датами, поэтому вы можете указать свои даты начала / окончания в виде строк, дат времени, меток времени панд ... почти все будет работать.


0

Концептуально это довольно просто. В зависимости от того, какой язык вы используете, вы сможете преобразовать эти даты в некоторое ссылочное 32- или 64-битное целое число, обычно представляющее секунды с начала эпохи (1 января 1970 года), иначе известные как «Unix time» или миллисекунды с некоторой другой произвольной даты. Просто сгенерируйте случайное 32- или 64-битное целое число между этими двумя значениями. Это должен быть один лайнер на любом языке.

На некоторых платформах вы можете сгенерировать время как двойное число (дата - это целая часть, время - это дробная часть - одна реализация). Тот же принцип применим, за исключением того, что вы имеете дело с числами с плавающей запятой одинарной или двойной точности («плавающие» или «двойные» в C, Java и других языках). Вычтите разницу, умножьте на случайное число (0 <= r <= 1), добавьте время начала и все готово.


0

В питоне:

>>> from dateutil.rrule import rrule, DAILY
>>> import datetime, random
>>> random.choice(
                 list(
                     rrule(DAILY, 
                           dtstart=datetime.date(2009,8,21), 
                           until=datetime.date(2010,10,12))
                     )
                 )
datetime.datetime(2010, 2, 1, 0, 0)

(нужна dateutilбиблиотека Python - pip install python-dateutil)


0

Используйте ApacheCommonUtils для генерации случайного длинного в пределах заданного диапазона, а затем создайте Date из этого длинного.

Пример:

import org.apache.commons.math.random.RandomData;

import org.apache.commons.math.random.RandomDataImpl;

общедоступная дата nextDate (минимальная дата, максимальная дата) {

RandomData randomData = new RandomDataImpl();

return new Date(randomData.nextLong(min.getTime(), max.getTime()));

}


1
вопрос помечен как "питон"
Дэвид Маркс

0

Я сделал это для другого проекта, используя случайное и время. Я использовал общий формат времени, вы можете просмотреть документацию здесь для первого аргумента в strftime (). Вторая часть - это функция random.randrange. Возвращает целое число между аргументами. Измените его на диапазоны, которые соответствуют желаемым строкам. Вы должны иметь хорошие аргументы в кортеже второго аругмента.

import time
import random


def get_random_date():
    return strftime("%Y-%m-%d %H:%M:%S",(random.randrange(2000,2016),random.randrange(1,12),
    random.randrange(1,28),random.randrange(1,24),random.randrange(1,60),random.randrange(1,60),random.randrange(1,7),random.randrange(0,366),1))

0

Панды + NumPy решение

import pandas as pd
import numpy as np

def RandomTimestamp(start, end):
    dts = (end - start).total_seconds()
    return start + pd.Timedelta(np.random.uniform(0, dts), 's')

dts - это разница между временными метками в секундах (с плавающей точкой). Затем он используется для создания временной шкалы панд между 0 и dts, которая добавляется к начальной отметке времени.


0

Основываясь на ответе mouviciel, вот векторизованное решение с использованием numpy. Конвертируйте даты начала и окончания в целые, генерируйте массив случайных чисел между ними и конвертируйте весь массив обратно в даты.

import time
import datetime
import numpy as np

n_rows = 10

start_time = "01/12/2011"
end_time = "05/08/2017"

date2int = lambda s: time.mktime(datetime.datetime.strptime(s,"%d/%m/%Y").timetuple())
int2date = lambda s: datetime.datetime.fromtimestamp(s).strftime('%Y-%m-%d %H:%M:%S')

start_time = date2int(start_time)
end_time = date2int(end_time)

random_ints = np.random.randint(low=start_time, high=end_time, size=(n_rows,1))
random_dates = np.apply_along_axis(int2date, 1, random_ints).reshape(n_rows,1)

print random_dates

0

Это модифицированный метод @ (Том Алсберг). Я изменил его, чтобы получить дату с миллисекундами.

import random
import time
import datetime

def random_date(start_time_string, end_time_string, format_string, random_number):
    """
    Get a time at a proportion of a range of two formatted times.
    start and end should be strings specifying times formated in the
    given format (strftime-style), giving an interval [start, end].
    prop specifies how a proportion of the interval to be taken after
    start.  The returned time will be in the specified format.
    """
    dt_start = datetime.datetime.strptime(start_time_string, format_string)
    dt_end = datetime.datetime.strptime(end_time_string, format_string)

    start_time = time.mktime(dt_start.timetuple()) + dt_start.microsecond / 1000000.0
    end_time = time.mktime(dt_end.timetuple()) + dt_end.microsecond / 1000000.0

    random_time = start_time + random_number * (end_time - start_time)

    return datetime.datetime.fromtimestamp(random_time).strftime(format_string)

Пример:

print TestData.TestData.random_date("2000/01/01 00:00:00.000000", "2049/12/31 23:59:59.999999", '%Y/%m/%d %H:%M:%S.%f', random.random())

Вывод: 2028/07/08 12:34:49.977963


0
start_timestamp = time.mktime(time.strptime('Jun 1 2010  01:33:00', '%b %d %Y %I:%M:%S'))
end_timestamp = time.mktime(time.strptime('Jun 1 2017  12:33:00', '%b %d %Y %I:%M:%S'))
time.strftime('%b %d %Y %I:%M:%S',time.localtime(randrange(start_timestamp,end_timestamp)))

обращаться


0
    # needed to create data for 1000 fictitious employees for testing code 
    # code relating to randomly assigning forenames, surnames, and genders
    # has been removed as not germaine to the question asked above but FYI
    # genders were randomly assigned, forenames/surnames were web scrapped,
    # there is no accounting for leap years, and the data stored in mySQL

    import random 
    from datetime import datetime
    from datetime import timedelta

    for employee in range(1000):
        # assign a random date of birth (employees are aged between sixteen and sixty five)
        dlt = random.randint(365*16, 365*65)
        dob = datetime.today() - timedelta(days=dlt)
        # assign a random date of hire sometime between sixteenth birthday and yesterday
        doh = datetime.today() - timedelta(days=random.randint(1, dlt-365*16))
        print("born {} hired {}".format(dob.strftime("%d-%m-%y"), doh.strftime("%d-%m-%y")))

0

Альтернативный способ создания случайных дат между двумя датами, используя np.random.randint(), pd.Timestamp().valueи pd.to_datetime()с for loop:

# Import libraries
import pandas as pd

# Initialize
start = '2020-01-01' # Specify start date
end = '2020-03-10' # Specify end date
n = 10 # Specify number of dates needed

# Get random dates
x = np.random.randint(pd.Timestamp(start).value, pd.Timestamp(end).value,n)
random_dates = [pd.to_datetime((i/10**9)/(60*60)/24, unit='D').strftime('%Y-%m-%d')  for i in x]

print(random_dates)

Вывод

['2020-01-06',
 '2020-03-08',
 '2020-01-23',
 '2020-02-03',
 '2020-01-30',
 '2020-01-05',
 '2020-02-16',
 '2020-03-08',
 '2020-02-09',
 '2020-01-04']
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.