Wygeneruj losową datę między dwoma innymi datami


138

Jak wygenerować losową datę, która musi znajdować się między dwoma innymi podanymi datami?

Podpis funkcji powinien wyglądać mniej więcej tak:

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

i zwróci datę taką jak: 2/4/2008 7:20 PM


Ze sposobu, w jaki pytanie jest teraz przedstawiane, nie jest jasne, czy chcesz, aby data lub godzina były tylko przypadkowe. Twój przykład sugeruje, że szukasz czasu. Jeśli ma to nastąpić pomiędzy dwoma datami, możesz zmodyfikować udzielone dotychczas odpowiedzi, tak aby odpowiadały Twoim potrzebom, z wyłączeniem czasu zakończenia i rozpoczęcia. Wreszcie, w większości odpowiedzi, na przykład zaakceptowanej, kod wyprowadza datę i godzinę z wyłączeniem czasu końcowego z powodu obcięcia do int. Aby wygenerować czas, który może zawierać koniec odpowiedzi, zmień kod naptime = stime + prop * (etime - stime) + 0.5
tortal

Odpowiedzi:


149

Zamień oba ciągi na znaczniki czasu (w wybranej rozdzielczości, np. Milisekundy, sekundy, godziny, dni, cokolwiek), odejmij wcześniejszą od późniejszej, pomnóż liczbę losową (zakładając, że jest rozłożona w range [0, 1]) z tą różnicą i dodaj ponownie do wcześniejszy. Przekształć znacznik czasu z powrotem w ciąg daty, a otrzymasz losowy czas w tym zakresie.

Przykład Pythona (dane wyjściowe są prawie w określonym formacie, innym niż 0wypełnienie - obwiniaj amerykańskie konwencje formatu czasu):

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

To podejście nie obsługuje dat rozpoczynających się przed rokiem 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)

Precyzja to sekundy. Jeśli chcesz, możesz zwiększyć precyzję do mikrosekund lub zmniejszyć do, powiedzmy, pół godziny. W tym celu po prostu zmień obliczenia w ostatniej linii.

przykładowy bieg:

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

wynik:

2008-12-04 01:50:17

3
Użycie startzmiennej w tym przypadku jest całkowicie poprawne. Jedynym problemem, jaki widzę w kodzie, jest użycie secondsatrybutu z wyniku delta. To nie zwróci całkowitej liczby sekund w całym interwale; zamiast tego jest to tylko liczba sekund od składnika „czas” (od 0 do 60); timedeltaobiekt ma total_secondssposobu, który ma zostać użyty w zamian.
emyller

7
@emyller: Nie, używam, (delta.days * 24 * 60 * 60) + delta.secondsco daje całkowitą liczbę sekund. Ta total_seconds()metoda jest nowa w Pythonie 2.7 i nie istniała w 2009 roku, kiedy odpowiedziałem na pytanie. Jeśli masz Pythona 2.7, powinieneś go zamiast tego użyć, ale kod działa dobrze tak, jak jest.
nosklo

Nie byłem świadomy braku tej metody w wersji 2.7-. Właśnie sprawdziłem, że obiekt timedelta składa się w zasadzie z liczby dni i sekund, więc masz rację. :-)
emyller 23.11.11

@emyller: Aby uzyskać kompletność, obiekt timedelta składa się z dni, sekund i mikrosekund . Dokładność powyższego kodu generującego losową datę wynosi do kilku sekund, ale można to zmienić, jak wspomniałem w odpowiedzi.
nosklo

83

Malutka wersja.

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

Zauważ, że oba argumenty starti endargumenty powinny być datetimeobiektami. Jeśli zamiast tego masz ciągi, konwersja jest dość łatwa. Inne odpowiedzi wskazują na kilka sposobów, aby to zrobić.


54

Zaktualizowana odpowiedź

Korzystanie z Fakera jest jeszcze prostsze .

Instalacja

pip install faker

Stosowanie:

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

Stara odpowiedź

Korzystanie z radaru jest bardzo proste

Instalacja

pip install radar

Stosowanie

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 za zasugerowanie modułu faker .. Używałem do generowania profilu, ale nie korzystałem z narzędzia Data Faker jest bardzo dobrym modułem podczas testów.
Gahan,

Otrzymuję dane wyjściowe w tym formacie, datetime.date(2039, 3, 16)ale chcę uzyskać takie wyniki 2039-03-16. Jak to zrobić?
Ayush Kumar

Masz na myśli, chcesz sznurek? Bardzo łatwe (tylko odpowiednio go sformatować) fake.date_between(start_date='today', end_date='+30y').strftime('%Y-%m-%d').
Artur Barseghyan

1
Głosuj za korzystaniem z niesamowitej biblioteki, nawet jeśli musisz ją zainstalować. Zmniejsza to złożoność implementacji zasadniczo do 4 linii.
Blairg23,

1
@ KubiK888: Jasne, zobacz odpowiedź na moje aktualizacje. Powinieneś po prostu jawnie podać datę_początkową.
Artur Barseghyan

24

To jest inne podejście - taki rodzaj działa.

from random import randint
import datetime

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

LEPSZE PODEJŚCIE

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

1
Pierwsze podejście nigdy nie wybierze daty kończącej się 29, 30 lub 31 grudnia, a drugie podejście nie uwzględnia lat przestępnych, gdy rok wynosi 366 dni, tj. Jeśli startdate+ 1 rok upłynie do 31 grudnia w roku przestępnym, to Kod nigdy nie wybierze tej samej daty dokładnie rok później. Oba podejścia pozwalają tylko określić datę początkową i ile lat w przyszłości, podczas gdy pytanie dotyczyło określenia dwóch dat i moim zdaniem jest to bardziej przydatne API.
Boris

15

Ponieważ Python 3 timedeltaobsługuje mnożenie przez liczby zmiennoprzecinkowe, teraz możesz zrobić:

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

biorąc pod uwagę to starti endsą tego typu datetime.datetime. Na przykład, aby wygenerować losową datę i godzinę w ciągu następnego dnia:

import random
from datetime import datetime, timedelta

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

6

Aby dodać rozwiązanie oparte na pandach, używam:

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

Podoba mi się to ze względu na fajne pd.Timestampfunkcje, które pozwalają mi wrzucić do niego różne rzeczy i formaty. Rozważ kilka poniższych przykładów ...

Twój podpis.

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

Losowa pozycja.

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

Inny format.

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

Bezpośrednie przekazywanie obiektów pandy / datetime.

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

A jak można elegancko utworzyć losową serię datetime (tj. Bez iteracji funkcji dla każdego elementu)?
dmvianna

Cóż, być może możliwe jest zmodyfikowanie funkcji w celu wygenerowania tablicy deltawartości i zmapowania ich wszystkich jednocześnie na znaczniki czasu. Osobiście jednak wolałbym po prostu zrobić coś takiego pd.Series([5] * 10, [random_date('2014-01-01', '2014-01-30') for i in range(10)]).
metakermit

3

Oto odpowiedź na dosłowne znaczenie tytułu, a nie treść tego pytania:

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)

Ten kod jest luźno oparty na zaakceptowanej odpowiedzi.


możesz zmienić przedostatnią linię ptime = random.randint(stime, etime)na nieznacznie bardziej poprawną, ponieważ randinttworzy zakres obejmujący.
Boris

3

Możesz użyć Mixer,

pip install mixer

i,

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
składnia zmieniła się nieco, nie jestem pewien, jak to zrobić, ale obiekt django będzie miał losową datę wypełnioną w następujący sposób:client = mixer.blend(Client, date=mixer.RANDOM)
tutuDajuju

@tutuDajuju: Za czym stoi klient?
Nima Soroush

Według ich dokumentacji może to być klasa modelu Django, SQLAlchemy lub 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

Zamień swoje daty na znaczniki czasu i zadzwoń random.randintz sygnaturami czasowymi, a następnie przekonwertuj losowo wygenerowany znacznik czasu z powrotem na datę:

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)

Następnie możesz go używać w ten sposób

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

Jeśli zależy Ci na strefach czasowych, powinieneś po prostu skorzystać date_time_between_datesz Fakerbiblioteki, z której ukradłem ten kod , jak już sugeruje inna odpowiedź.


1
  1. Konwertuj wprowadzone daty na liczby (int, float, cokolwiek jest najlepsze dla twojego użytku)
  2. Wybierz liczbę spośród dwóch numerów dat.
  3. Zamień tę liczbę z powrotem na datę.

W wielu systemach operacyjnych jest już dostępnych wiele algorytmów konwertowania daty na liczby i odwrotnie.


1

Do czego potrzebujesz liczby losowej? Zwykle (w zależności od języka) można uzyskać liczbę sekund / milisekund z epoki z daty. Więc dla losowej daty między startDate a endDate możesz zrobić:

  1. oblicz czas w ms między startDate a endDate (endDate.toMilliseconds () - startDate.toMilliseconds ())
  2. wygeneruj liczbę od 0 do liczby uzyskanej w 1
  3. wygeneruj nową datę z przesunięciem czasu = startDate.toMilliseconds () + liczba uzyskana w 2

1

Najłatwiejszym sposobem jest przekonwertowanie obu liczb na znaczniki czasu, a następnie ustawienie ich jako granic minimalnej i maksymalnej w generatorze liczb losowych.

Szybki przykład 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);
}

Ta funkcja wykorzystuje strtotime()do konwersji opisu date()daty i godziny na znacznik czasu uniksowego oraz do ustalenia poprawnej daty z losowego znacznika czasu, który został wygenerowany.


Jeśli ktoś może napisać to w Pythonie, byłoby to pomocne.
quilby

1

Wystarczy dodać kolejny:

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

Codzienna obsługa wymaga pewnych rozważań. Mając 28, jesteś na bezpiecznej stronie.


1

Oto rozwiązanie zmodyfikowane z podejścia emyllera, które zwraca tablicę losowych dat w dowolnej rozdzielczości

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

Jedną z fajnych rzeczy w tym podejściu jest to, że np.datetime64jest naprawdę dobre w wymuszaniu dat, więc możesz określić daty rozpoczęcia / zakończenia jako ciągi znaków, czasy dat, znaczniki czasu pand ... prawie wszystko będzie działać.


0

Koncepcyjnie jest to dość proste. W zależności od tego, jakiego języka używasz, będziesz w stanie przekonwertować te daty na jakieś 32- lub 64-bitowe liczby całkowite odniesienia, zazwyczaj reprezentujące sekundy od epoki (1 stycznia 1970), zwane inaczej „czasem uniksowym” lub milisekundami od innej dowolnej daty. Wystarczy wygenerować losową 32- lub 64-bitową liczbę całkowitą między tymi dwoma wartościami. Powinien to być jeden liniowiec w dowolnym języku.

Na niektórych platformach można generować czas jako podwójny (data to część całkowita, czas to część ułamkowa to jedna implementacja). Ta sama zasada ma zastosowanie, chyba że masz do czynienia z liczbami zmiennoprzecinkowymi o pojedynczej lub podwójnej precyzji („zmiennoprzecinkowe” lub „podwójne” w językach C, Javie i innych). Odejmij różnicę, pomnóż przez liczbę losową (0 <= r <= 1), dodaj do czasu rozpoczęcia i gotowe.


0

W Pythonie:

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

(potrzebujesz dateutilbiblioteki Python - pip install python-dateutil)


0

Użyj ApacheCommonUtils, aby wygenerować losową długość w danym zakresie, a następnie utwórz datę z tego długości.

Przykład:

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
pytanie jest oznaczone jako „python”
David Marx

0

Zrobiłem to dla innego projektu, używając losowości i czasu. Użyłem ogólnego formatu od czasu, kiedy możesz przeglądać dokumentację tutaj dla pierwszego argumentu w strftime (). Druga część to funkcja random.randrange. Zwraca liczbę całkowitą między argumentami. Zmień go na zakresy, które pasują do ciągów, które chcesz. Musisz mieć fajne argumenty w krotce drugiej argumentacji.

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

Pandy + numpy rozwiązanie

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 to różnica między znacznikami czasu w sekundach (liczba zmiennoprzecinkowa). Następnie jest używany do tworzenia pandy timedelta między 0 a dts, która jest dodawana do znacznika czasu rozpoczęcia.


0

Na podstawie odpowiedzi udzielonej przez mouviciel, oto rozwiązanie zwektoryzowane przy użyciu numpy. Konwertuj daty początkowe i końcowe na liczby całkowite, generuj tablicę liczb losowych między nimi i konwertuj całą tablicę z powrotem na daty.

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

To zmodyfikowana metoda @ (Tom Alsberg). Zmodyfikowałem go, aby uzyskać datę w milisekundach.

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)

Przykład:

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

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

odnosić się


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

Alternatywny sposób tworzyć losowe daty pomiędzy dwiema datami użyciu np.random.randint(), pd.Timestamp().valuea pd.to_datetime()przy 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)

Wynik

['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']
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.