Generate a random date between two other dates

Viewed 217092

How would I generate a random date that has to be between two other given dates?

The function's signature should be something like this:

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

and would return a date such as: 2/4/2008 7:20 PM

31 Answers

Convert both strings to timestamps (in your chosen resolution, e.g. milliseconds, seconds, hours, days, whatever), subtract the earlier from the later, multiply your random number (assuming it is distributed in the range [0, 1]) with that difference, and add again to the earlier one. Convert the timestamp back to date string and you have a random time in that range.

Python example (output is almost in the format you specified, other than 0 padding - blame the American time format conventions):

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

    start and end should be strings specifying times formatted 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, time_format))
    etime = time.mktime(time.strptime(end, time_format))

    ptime = stime + prop * (etime - stime)

    return time.strftime(time_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()))
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)

The precision is seconds. You can increase precision up to microseconds, or decrease to, say, half-hours, if you want. For that just change the last line's calculation.

example run:

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

output:

2008-12-04 01:50:17

Convert your dates into timestamps and call random.randint with the timestamps, then convert the randomly generated timestamp back into a 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)

Then you can use it like this

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

If you care about timezones you should just use date_time_between_dates from the Faker library, where I stole this code from, as a different answer already suggests.

# 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")))
  1. Convert your input dates to numbers (int, float, whatever is best for your usage)
  2. Choose a number between your two date numbers.
  3. Convert this number back to a date.

Many algorithms for converting date to and from numbers are already available in many operating systems.

What do you need the random number for? Usually (depending on the language) you can get the number of seconds/milliseconds from the Epoch from a date. So for a randomd date between startDate and endDate you could do:

  1. compute the time in ms between startDate and endDate (endDate.toMilliseconds() - startDate.toMilliseconds())
  2. generate a number between 0 and the number you obtained in 1
  3. generate a new Date with time offset = startDate.toMilliseconds() + number obtained in 2

The easiest way of doing this is to convert both numbers to timestamps, then set these as the minimum and maximum bounds on a random number generator.

A quick PHP example would be:

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

This function makes use of strtotime() to convert a datetime description into a Unix timestamp, and date() to make a valid date out of the random timestamp which has been generated.

Alternative way to create random dates between two dates using np.random.randint(), pd.Timestamp().value and pd.to_datetime() with 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)

Output

['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']

Get random date between start_date and end_date. If any of them is None, then get random date between today and past 100 years.

class GetRandomDateMixin:
    def get_random_date(self, start_date=None, end_date=None):
        """
        get random date between start_date and end_date.
        If any of them is None, then get random date between
        today and past 100 years.
        :param start_date: datetime obj.
            eg: datetime.datetime(1940, 1, 1).date()
        :param end_date: datetime obj
        :return: random date
        """
        if start_date is None or end_date is None:

            end_date = datetime.datetime.today().date()
            start_date = end_date - datetime.timedelta(
                days=(100 * 365)
            )

        delta = end_date - start_date
        random_days = random.randint(1, delta.days)
        new_date = start_date + datetime.timedelta(
            days=random_days
        )

        return new_date

Use my randomtimestamp module. It has 3 functions, randomtimestamp, random_time, and random_date.

Below is the signature of randomtimestamp function. It can generate a random timestamp between two years, or two datetime objects (if you like precision).

There's option to get the timestamp as a datetime object or string. Custom patterns are also supported (like strftime)

randomtimestamp(
    start_year: int = 1950,
    end_year: int = None,
    text: bool = False,
    start: datetime.datetime = None,
    end: datetime.datetime = None,
    pattern: str = "%d-%m-%Y %H:%M:%S"
  ) -> Union[datetime, str]:

Example:

>>> randomtimestamp(start_year=2020, end_year=2021)
datetime.datetime(2021, 1, 10, 5, 6, 19)

>>> start = datetime.datetime(2020, 1, 1, 0, 0, 0)
>>> end = datetime.datetime(2021, 12, 31, 0, 0, 0)

>>> randomtimestamp(start=start, end=end)
datetime.datetime(2020, 7, 14, 14, 12, 32)

Why not faker?

Because randomtimestamp is lightweight and fast. As long as random timestamps are the only thing you need, faker is an overkill and also heavy (being feature rich).

Conceptually it's quite simple. Depending on which language you're using you will be able to convert those dates into some reference 32 or 64 bit integer, typically representing seconds since epoch (1 January 1970) otherwise known as "Unix time" or milliseconds since some other arbitrary date. Simply generate a random 32 or 64 bit integer between those two values. This should be a one liner in any language.

On some platforms you can generate a time as a double (date is the integer part, time is the fractional part is one implementation). The same principle applies except you're dealing with single or double precision floating point numbers ("floats" or "doubles" in C, Java and other languages). Subtract the difference, multiply by random number (0 <= r <= 1), add to start time and done.

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

refer

Building off of @Pieter Bos 's answer:

import random
import datetime

start = datetime.date(1980, 1, 1)
end = datetime.date(2000, 1, 1)

random_date = start + (end - start) * random.random()
random_date = datetime.datetime.combine(random_date, datetime.datetime.min.time())

What about

import datetime
import random


def random_date(begin: datetime.datetime, end: datetime.datetime):
    epoch = datetime.datetime(1970, 1, 1)
    begin_seconds = int((begin - epoch).total_seconds())
    end_seconds = int((end - epoch).total_seconds())
    dt_seconds = random.randint(begin_seconds, end_seconds)

    return datetime.datetime.fromtimestamp(dt_seconds)

Haven't tried it with "epoch" years different than 1970 but it does the job

Generates random dates between last 50 yrs to last 30 years. And generates date only.

import random
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta

start_date = date.today() - relativedelta(years=50)
end_date = date.today() - relativedelta(years=20)
delta = end_date - start_date
print(delta.days)

random_number = random.randint(1, delta.days)

new_date = start_date + timedelta(days=random_number)
print (new_date)
Related