Generar una fecha aleatoria entre otras dos fechas.


138

¿Cómo generaría una fecha aleatoria que tiene que estar entre otras dos fechas dadas?

La firma de la función debería ser algo como esto:

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

y devolvería una fecha como: 2/4/2008 7:20 PM


La forma en que se presenta la pregunta en este momento no está claro si solo desea o no que la fecha o la hora sean aleatorias. Su ejemplo sugiere que está buscando un momento. Si tiene que estar entre las dos fechas, es posible que desee modificar las respuestas dadas hasta el momento para satisfacer sus necesidades y excluir el final y la hora de inicio. Por último, en la mayoría de las respuestas, como la aceptada, el código genera una fecha y hora exclusiva, la hora de finalización debido al truncamiento en int. Para generar un tiempo que pueda incluir el final en la respuesta, cambie el código aptime = stime + prop * (etime - stime) + 0.5
tortal

Respuestas:


149

Convierta ambas cadenas en marcas de tiempo (en la resolución elegida, por ejemplo, milisegundos, segundos, horas, días, lo que sea), reste lo anterior de lo posterior, multiplique su número aleatorio (suponiendo que se distribuya en el range [0, 1] ) con esa diferencia, y agregue nuevamente a el anterior Convierta la marca de tiempo a la cadena de fecha y tendrá un tiempo aleatorio en ese rango.

Ejemplo de Python (la salida está casi en el formato que especificó, aparte del 0relleno: culpe a las convenciones de formato de hora estadounidense):

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()))

Este enfoque no admite fechas que comiencen antes de 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)

La precisión es segundos. Puede aumentar la precisión hasta microsegundos o disminuir, digamos, media hora, si lo desea. Para eso solo cambia el cálculo de la última línea.

ejemplo ejecutado:

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))

salida:

2008-12-04 01:50:17

3
El uso de la startvariable en ese caso es perfectamente correcto. El único problema que veo en el código es el uso del secondsatributo de la resultante delta. Eso no devolvería el número total de segundos en todo el intervalo; en cambio, es solo el número de segundos desde el componente 'tiempo' (algo entre 0 y 60); un timedeltaobjeto tiene un total_secondsmétodo, que debería usarse en su lugar.
Emyller

77
@emyller: No, estoy usando los (delta.days * 24 * 60 * 60) + delta.secondsresultados en el total de segundos. El total_seconds()método es nuevo en Python 2.7 y no existía en 2009 cuando respondí la pregunta. Si tiene Python 2.7, debería usarlo en su lugar, pero el código funciona bien como está.
nosklo

No estaba al tanto de la inexistencia de este método en 2.7-. Acabo de comprobar que un objeto timedelta está compuesto básicamente por números de días y segundos, así que tienes razón. :-)
emyller

@emyller: solo para completar, el objeto timedelta se compone de días, segundos y microsegundos . La precisión del código de generación de fecha aleatoria anterior es de hasta segundos, pero podría cambiarse, como mencioné en la respuesta.
nosklo

83

Una pequeña versión

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())),
    )

Tenga en cuenta que ambos starty los endargumentos deben ser datetimeobjetos. Si tienes cadenas en su lugar, es bastante fácil de convertir. Las otras respuestas señalan algunas formas de hacerlo.


54

Respuesta actualizada

Es aún más simple usar Faker .

Instalación

pip install faker

Uso:

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')

Vieja respuesta

Es muy simple usar radar

Instalación

pip install radar

Uso

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
voto a favor por sugerir un módulo falsificador. Estaba usando para generar el perfil pero no usé date.
Gahan

Estoy obteniendo la salida en este formato datetime.date(2039, 3, 16)Pero quiero una salida como esta 2039-03-16. ¿Como hacer eso?
Ayush Kumar

¿Quieres decir que quieres una cuerda? Muy fácil (justo formatearlo en consecuencia): fake.date_between(start_date='today', end_date='+30y').strftime('%Y-%m-%d').
Artur Barseghyan

1
Vota por usar una biblioteca increíble, incluso si tienes que instalarla. Esto reduce la complejidad de la implementación a esencialmente 4 líneas.
Blairg23

1
@ KubiK888: Claro, vea la respuesta de mis actualizaciones. Simplemente debe proporcionar la fecha_inicio explícitamente.
Artur Barseghyan

24

Este es un enfoque diferente, ese tipo de trabajos ...

from random import randint
import datetime

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

MEJOR ENFOQUE

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

1
El primer enfoque nunca elegirá una fecha que finalice el 29, 30 o 31 y su segundo enfoque no tiene en cuenta los años bisiestos, cuando el año es 366 días, es decir, si startdate+ 1 año pasa hasta el 31 de diciembre en un año bisiesto, este el código nunca elegirá la misma fecha exactamente un año después. Ambos enfoques solo le permiten especificar una fecha de inicio y cuántos años en el futuro, mientras que la pregunta era sobre la especificación de dos fechas, y en mi opinión esa es una API más útil.
Boris

15

Dado que Python 3 timedeltaadmite la multiplicación con flotantes, ahora puede hacer:

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

dado eso starty endson del tipo datetime.datetime. Por ejemplo, para generar una fecha y hora aleatoria dentro del día siguiente:

import random
from datetime import datetime, timedelta

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

6

Para incorporar una solución basada en pandas, uso:

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

Me gusta por lo lindo pd.Timestamp características que me permiten lanzar diferentes cosas y formatos. Considere los siguientes ejemplos ...

Tu firma.

>>> 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)

Posición aleatoria

>>> 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)

Formato diferente

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

Pasar objetos pandas / datetime directamente.

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

¿Y cómo crearía una serie aleatoria de fecha y hora con elegancia (es decir, sin iterar su función para cada elemento)?
dmvianna

Bueno, tal vez sea posible modificar la función para generar una matriz de deltavalores y asignarlos todos a la vez a las marcas de tiempo. Personalmente, sin embargo, preferiría hacer algo así pd.Series([5] * 10, [random_date('2014-01-01', '2014-01-30') for i in range(10)]).
metakermit

3

Aquí hay una respuesta al significado literal del título en lugar del cuerpo de esta pregunta:

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)

Este código se basa libremente en la respuesta aceptada.


podría cambiar la segunda última línea para ptime = random.randint(stime, etime)que sea marginalmente más correcta porque randintproduce un rango inclusivo.
Boris

3

Puedes usar Mixer,

pip install mixer

y,

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
la sintaxis ha cambiado un poco, no estoy seguro de cómo hacer lo anterior, pero un objeto django tendrá una fecha aleatoria llena de la siguiente manera:client = mixer.blend(Client, date=mixer.RANDOM)
tutuDajuju

@tutuDajuju: ¿Qué significa el Cliente?
Nima Soroush

Según sus documentos , puede ser una clase de modelo Django, SQLAlchemy o 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

Convierta sus fechas en marcas de tiempo y llame random.randintcon las marcas de tiempo, luego convierta la marca de tiempo generada aleatoriamente en una fecha:

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)

Entonces puedes usarlo así

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

Si te interesan las zonas horarias, deberías usarlas date_time_between_datesdesde la Fakerbiblioteca, donde robé este código , como ya sugiere una respuesta diferente.


1
  1. Convierta sus fechas de entrada en números (int, float, lo que sea mejor para su uso)
  2. Elija un número entre sus dos números de fecha.
  3. Convierta este número de nuevo a una fecha.

Muchos algoritmos para convertir la fecha hacia y desde números ya están disponibles en muchos sistemas operativos.


1

¿Para qué necesitas el número aleatorio? Por lo general (dependiendo del idioma) puede obtener el número de segundos / milisegundos de la época desde una fecha. Entonces, para una fecha aleatoria entre startDate y endDate, podría hacer:

  1. calcular el tiempo en ms entre startDate y endDate (endDate.toMilliseconds () - startDate.toMilliseconds ())
  2. genera un número entre 0 y el número que obtuviste en 1
  3. generar una nueva fecha con desplazamiento de hora = startDate.toMilliseconds () + número obtenido en 2

1

La forma más fácil de hacerlo es convertir ambos números en marcas de tiempo, luego establecerlos como los límites mínimo y máximo en un generador de números aleatorios.

Un ejemplo rápido de PHP sería:

// 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);
}

Esta función se utiliza strtotime()para convertir una descripción de fecha y hora en una marca de tiempo de Unix y date()para hacer una fecha válida de la marca de tiempo aleatoria que se ha generado.


Si alguien puede escribir eso en Python, sería útil.
quilby

1

Solo para agregar otro:

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')

El manejo del día necesita algunas consideraciones. Con 28 estás en el sitio seguro.


1

Aquí hay una solución modificada del enfoque de Emyller que devuelve una serie de fechas aleatorias en cualquier resolución

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))

Parte de lo bueno de este enfoque es que np.datetime64es realmente bueno para obligar a las cosas a las fechas, por lo que puede especificar sus fechas de inicio / finalización como cadenas, fechas, marcas de tiempo de pandas ... casi cualquier cosa funcionará.


0

Conceptualmente es bastante simple. Dependiendo del idioma que esté utilizando, podrá convertir esas fechas en algún número entero de referencia de 32 o 64 bits, que generalmente representa segundos desde la época (1 de enero de 1970), también conocido como "tiempo Unix" o milisegundos desde alguna otra fecha arbitraria. Simplemente genere un entero aleatorio de 32 o 64 bits entre esos dos valores. Esto debería ser un trazador de líneas en cualquier idioma.

En algunas plataformas, puede generar un tiempo como doble (la fecha es la parte entera, el tiempo es la parte fraccional es una implementación). Se aplica el mismo principio, excepto que se trata de números de coma flotante de precisión simple o doble ("flotantes" o "dobles" en C, Java y otros lenguajes). Resta la diferencia, multiplica por un número aleatorio (0 <= r <= 1), suma al tiempo de inicio y listo.


0

En python:

>>> 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)

(necesita la dateutilbiblioteca de Python - pip install python-dateutil)


0

Use ApacheCommonUtils para generar un largo aleatorio dentro de un rango dado, y luego cree Fecha fuera de ese largo.

Ejemplo:

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

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

public Date nextDate (Date min, Date max) {

RandomData randomData = new RandomDataImpl();

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

}


1
la pregunta está etiquetada como "pitón"
David Marx

0

Hice esto para otro proyecto usando aleatorio y tiempo. Usé un formato general desde el momento en que puede ver la documentación aquí para el primer argumento en strftime (). La segunda parte es una función aleatoria. Devuelve un número entero entre los argumentos. Cámbielo a los rangos que coinciden con las cadenas que desea. Debes tener buenos argumentos en la tupla del segundo argumento.

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

Pandas + solución 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 es la diferencia entre las marcas de tiempo en segundos (flotante). Luego se usa para crear un pandas timedelta entre 0 y dts, que se agrega a la marca de tiempo de inicio.


0

Basado en la respuesta de mouviciel, aquí hay una solución vectorizada usando numpy. Convierta las fechas de inicio y finalización en ints, genere una matriz de números aleatorios entre ellas y convierta toda la matriz a fechas.

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

Es un método modificado de @ (Tom Alsberg). Lo modifiqué para obtener la fecha con milisegundos.

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)

Ejemplo:

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())

Salida: 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)))

referir


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

Una forma alternativa de crear fechas al azar entre dos fechas utilizando np.random.randint(), pd.Timestamp().valuey pd.to_datetime()con 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)

Salida

['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']
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.