Générer une date aléatoire entre deux autres dates


138

Comment générer une date aléatoire qui doit être entre deux autres dates données?

La signature de la fonction devrait ressembler à ceci:

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

et renverrait une date telle que: 2/4/2008 7:20 PM


La façon dont la question est présentée pour le moment ne permet pas de savoir si vous voulez ou non que la date ou l'heure soit aléatoire. Votre exemple suggère que vous recherchez un temps. S'il doit être entre les deux dates, vous pouvez modifier les réponses données jusqu'à présent en fonction de vos besoins et exclure l'heure de fin et de début. Enfin, dans la plupart des réponses, telles que celle acceptée, le code génère une date / heure excluant l'heure de fin en raison de la troncature en int. Pour générer une heure qui peut inclure la fin dans la réponse, changez le code enptime = stime + prop * (etime - stime) + 0.5
tortal

Réponses:


149

Convertissez les deux chaînes en horodatages (dans la résolution choisie, par exemple millisecondes, secondes, heures, jours, peu importe), soustrayez la première de la plus tardive, multipliez votre nombre aléatoire (en supposant qu'il est distribué dans le range [0, 1]) avec cette différence, et ajoutez à nouveau à le plus tôt. Convertissez l'horodatage en chaîne de date et vous avez une heure aléatoire dans cette plage.

Exemple Python (la sortie est presque au format que vous avez spécifié, autre que le 0remplissage - blâmez les conventions de format d'heure américaines):

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

Cette approche ne prend pas en charge les dates commençant avant 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 précision est en secondes. Vous pouvez augmenter la précision jusqu'à quelques microsecondes, ou diminuer jusqu'à, disons, une demi-heure, si vous le souhaitez. Pour cela, changez simplement le calcul de la dernière ligne.

exemple d'exécution:

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

production:

2008-12-04 01:50:17

3
L'utilisation de la startvariable dans ce cas est parfaitement correcte. Le seul problème que je vois dans le code est l'utilisation de l' secondsattribut du résultat delta. Cela ne renverrait pas le nombre total de secondes dans tout l'intervalle; à la place, c'est juste le nombre de secondes du composant 'time' (quelque chose entre 0 et 60); un timedeltaobjet a une total_secondsméthode, qui doit être utilisée à la place.
emyller

7
@emyller: Non, j'utilise (delta.days * 24 * 60 * 60) + delta.secondsce qui donne le nombre total de secondes. La total_seconds()méthode est nouvelle dans python 2.7 et n'existait pas en 2009 lorsque j'ai répondu à la question. Si vous avez python 2.7, vous devriez l'utiliser à la place, mais le code fonctionne bien tel quel.
nosklo

Je n'étais pas au courant de l'inexistence de cette méthode en 2.7-. Je viens de vérifier qu'un objet timedelta est essentiellement composé de nombres de jours et de secondes, donc vous avez raison. :-)
emyller

@emyller: Par souci d'exhaustivité, l'objet timedelta est composé de jours, secondes et microsecondes . La précision du code de génération de date aléatoire ci-dessus est de quelques secondes, mais elle pourrait être modifiée, comme je l'ai mentionné dans la réponse.
nosklo

83

Une version minuscule.

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

Notez que les arguments startet enddoivent tous deux être des datetimeobjets. Si vous avez des chaînes à la place, c'est assez facile à convertir. Les autres réponses indiquent des moyens de le faire.


54

Réponse mise à jour

C'est encore plus simple avec Faker .

Installation

pip install faker

Usage:

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

Ancienne réponse

C'est très simple d'utiliser le radar

Installation

pip install radar

Usage

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 pour avoir suggéré le module faker .. J'utilisais pour générer le profil mais je n'ai pas utilisé l'utilitaire de date faker est un très bon module lors des tests.
Gahan

J'obtiens la sortie dans ce format datetime.date(2039, 3, 16)mais je veux une sortie comme celle-ci 2039-03-16. Comment faire ça?
Ayush Kumar

Voulez-vous dire, vous voulez une chaîne? Très facile (format Élargi en conséquence): fake.date_between(start_date='today', end_date='+30y').strftime('%Y-%m-%d').
Artur Barseghyan

1
Votez pour l'utilisation d'une bibliothèque incroyable, même si vous devez l'installer. Cela réduit la complexité de la mise en œuvre à essentiellement 4 lignes.
Blairg23

1
@ KubiK888: Bien sûr, voir ma réponse sur les mises à jour. Vous devez simplement fournir explicitement la date de début.
Artur Barseghyan

24

C'est une approche différente - ce genre de travail.

from random import randint
import datetime

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

UNE MEILLEURE APPROCHE

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

1
La première approche ne choisira jamais une date se terminant le 29, 30 ou 31 et votre deuxième approche ne tient pas compte des années bissextiles, lorsque l'année est de 366 jours, c'est-à-dire si startdate+ 1 an passe jusqu'au 31 décembre d'une année bissextile, ce code ne choisira jamais la même date exactement un an plus tard. Les deux approches vous permettent uniquement de spécifier une date de début et combien d'années dans le futur, alors que la question portait sur la spécification de deux dates, et à mon avis, c'est une API plus utile.
Boris

15

Puisque Python 3 timedeltaprend en charge la multiplication avec des flottants, vous pouvez maintenant faire:

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

étant donné que startet endsont du type datetime.datetime. Par exemple, pour générer une date / heure aléatoire le jour suivant:

import random
from datetime import datetime, timedelta

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

6

Pour intégrer une solution basée sur les pandas, j'utilise:

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

Je l'aime bien, à cause des pd.Timestampfonctionnalités intéressantes qui me permettent de lancer différents trucs et formats. Considérez les quelques exemples suivants ...

Votre signature.

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

Position aléatoire.

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

Format différent.

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

Passer directement des pandas / objets datetime.

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

Et comment créeriez-vous une série datetime aléatoire avec élégance (c'est-à-dire sans itérer votre fonction pour chaque élément)?
dmvianna

Eh bien, il est peut-être possible de modifier la fonction pour générer un tableau de deltavaleurs et de les mapper toutes en même temps sur des horodatages. Personnellement, cependant, je préférerais simplement faire quelque chose comme pd.Series([5] * 10, [random_date('2014-01-01', '2014-01-30') for i in range(10)]).
metakermit

3

Voici une réponse à la signification littérale du titre plutôt qu'au corps de cette question:

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)

Ce code est basé vaguement sur la réponse acceptée.


vous pouvez modifier l'avant-dernière ligne pour ptime = random.randint(stime, etime)qu'elle soit légèrement plus correcte car elle randintproduit une plage inclusive.
Boris

3

Vous pouvez utiliser Mixer,

pip install mixer

et,

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 syntaxe a un peu changé, je ne sais pas comment faire ce qui précède, mais un objet django aura une date aléatoire remplie comme ceci:client = mixer.blend(Client, date=mixer.RANDOM)
tutuDajuju

@tutuDajuju: Que représente le client?
Nima Soroush

Selon leur documentation , il peut s'agir d'une classe de modèle Django, SQLAlchemy ou 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

Convertissez vos dates en horodatages et appelez random.randintavec les horodatages, puis reconvertissez l'horodatage généré aléatoirement en une date:

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)

Ensuite, vous pouvez l'utiliser comme ça

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 vous vous souciez des fuseaux horaires, vous devriez simplement utiliser date_time_between_datesde la Fakerbibliothèque, où j'ai volé ce code , comme une réponse différente le suggère déjà.


1
  1. Convertissez vos dates d'entrée en nombres (int, float, ce qui convient le mieux à votre utilisation)
  2. Choisissez un nombre entre vos deux numéros de date.
  3. Convertissez ce nombre en une date.

De nombreux algorithmes de conversion de date vers et à partir de nombres sont déjà disponibles dans de nombreux systèmes d'exploitation.


1

Pourquoi avez-vous besoin du nombre aléatoire? Habituellement (selon la langue), vous pouvez obtenir le nombre de secondes / millisecondes à partir de l'époque à partir d'une date. Donc, pour une date aléatoire entre startDate et endDate, vous pouvez faire:

  1. calculer le temps en ms entre startDate et endDate (endDate.toMilliseconds () - startDate.toMilliseconds ())
  2. générer un nombre entre 0 et le nombre que vous avez obtenu en 1
  3. générer une nouvelle date avec décalage horaire = startDate.toMilliseconds () + nombre obtenu en 2

1

Le moyen le plus simple de le faire est de convertir les deux nombres en horodatages, puis de les définir comme limites minimale et maximale sur un générateur de nombres aléatoires.

Un exemple PHP rapide serait:

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

Cette fonction permet de strtotime()convertir une description datetime en un horodatage Unix, et date()de créer une date valide à partir de l'horodatage aléatoire qui a été généré.


Si quelqu'un peut écrire cela en python, ce serait utile.
quilby le

1

Juste pour en ajouter un autre:

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

Le traitement de la journée nécessite quelques considérations. Avec 28 vous êtes sur le site sécurisé.


1

Voici une solution modifiée de l'approche d'emyller qui renvoie un tableau de dates aléatoires à n'importe quelle résolution

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

Une partie de ce qui est bien avec cette approche est qu'elle np.datetime64est vraiment efficace pour contraindre les choses à des dates, vous pouvez donc spécifier vos dates de début / fin sous forme de chaînes, d'heures de données, d'horodatages pandas ... à peu près tout fonctionnera.


0

Conceptuellement, c'est assez simple. En fonction de la langue que vous utilisez, vous pourrez convertir ces dates en un entier de référence de 32 ou 64 bits, représentant généralement des secondes depuis l'époque (1er janvier 1970) autrement connue sous le nom de «heure Unix» ou des millisecondes depuis une autre date arbitraire. Générez simplement un entier aléatoire de 32 ou 64 bits entre ces deux valeurs. Cela devrait être une seule ligne dans n'importe quelle langue.

Sur certaines plates-formes, vous pouvez générer une heure sous forme de double (la date est la partie entière, l'heure est la partie fractionnaire est une implémentation). Le même principe s'applique sauf que vous avez affaire à des nombres à virgule flottante simple ou double précision ("floats" ou "doubles" en C, Java et d'autres langages). Soustrayez la différence, multipliez par un nombre aléatoire (0 <= r <= 1), ajoutez à l'heure de début et c'est fait.


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)

(besoin d'une dateutilbibliothèque python - pip install python-dateutil)


0

Utilisez ApacheCommonUtils pour générer un long aléatoire dans une plage donnée, puis créez une date à partir de ce long.

Exemple:

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 question est étiquetée "python"
David Marx

0

J'ai fait cela pour un autre projet en utilisant le hasard et le temps. J'ai utilisé un format général à partir du moment où vous pouvez consulter la documentation ici pour le premier argument dans strftime (). La deuxième partie est une fonction random.randrange. Il renvoie un entier entre les arguments. Remplacez-le par les plages qui correspondent aux chaînes que vous souhaitez. Vous devez avoir de beaux arguments dans le tuple du deuxième argument.

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

Solution Pandas + 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 est la différence entre les horodatages en secondes (float). Il est ensuite utilisé pour créer un delta temporel pandas entre 0 et dts, qui est ajouté à l'horodatage de début.


0

Sur la base de la réponse de mouviciel, voici une solution vectorisée utilisant numpy. Convertissez les dates de début et de fin en entiers, générez un tableau de nombres aléatoires entre eux et reconvertissez le tableau entier en dates.

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

C'est la méthode modifiée de @ (Tom Alsberg). Je l'ai modifié pour obtenir la date en millisecondes.

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)

Exemple:

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

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

référer


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

Une manière alternative pour créer des dates aléatoires entre deux dates à l' aide np.random.randint(), pd.Timestamp().valueet pd.to_datetime()avec 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)

Production

['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']
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.