Trying to create a list of random times within specific date/time conditions

Question:

I am trying to modify this GitHub code for my own purposes in the title:

import random
from datetime import datetime, timedelta

min_year=1900
max_year=datetime.now().year

start = datetime(min_year, 1, 1, 00, 00, 00)
years = max_year - min_year+1
end = start + timedelta(days=365 * years)

for i in range(10):
    random_date = start + (end - start) * random.random()
    print(random_date)

My desired outcome specifically is, for all weekdays (Mon. – Fri.) April 1st, 2023 until July 31st, 2023 print two times (hh:mm:ss) which meet the following conditions:

  • Are an hour or more apart

  • Are within the hours of 8 AM – 6 PM (0800 – 1800)

I came up with this before remembering I need to add the two per weekday and hour-apart constraint in somewhere:

import random
from datetime import datetime, timedelta

start = datetime(2023, 4, 1, 00, 00, 00)
end = start + timedelta(days=160)

for i in range(10):
    random_date = start + (end - start) * random.random()
    no = random_date.weekday()
    if no < 5:
      print(random_date)

I’ll be continuing to work on it, but if anyone has any advice I’d greatly appreciate it! Am fairly new to programming

Asked By: mrparga

||

Answers:

You could try the following:

from random import uniform
from datetime import datetime, timedelta

start = datetime(2023, 4, 1)  # April 1st, 2023
end = datetime(2023, 7, 31)   # July 31st, 2023 
one_day = timedelta(days=1)
while start <= end:
    if start.weekday() < 5:
        t1, t2 = uniform(8, 18), uniform(8, 18)
        while abs(t1 - t2) < 1:
            t2 = uniform(8, 18)
        t1, t2 = (t1, t2) if t1 < t2 else (t2, t1)
        t1 = start + one_day * (t1 / 24)
        t2 = start + one_day * (t2 / 24)
        print(t1, t2)
    start += one_day
  • Loop over the date range of interest (from start to end) with a while-loop: at the end of each loop add a day to start, break out of the loop once end is processed.
  • If the day is a weekday, draw 2 random numbers uniformly distributed between 8 and 18 until they are at least 1 apart. Then divide them by 24 and add the corresponding part of a day to start.

If you are only interested in a seconds-resolution (which seems to be the case, but I have missed at first) then you can do that solely integer-based:

from random import randint
from datetime import datetime, timedelta

start = datetime(2023, 4, 1)  # April 1st, 2023
end = datetime(2023, 7, 31)   # July 31st, 2023
start_s, end_s = 3_600 * 8, 3_600 * 18
one_day = timedelta(days=1)
while start <= end:
    if start.weekday() < 5:
        t1, t2 = randint(start_s, end_s), randint(start_s, end_s)
        while abs(t1 - t2) < 3_600:
            t2 = randint(start_s, end_s)
        t1, t2 = (t1, t2) if t1 < t2 else (t2, t1)
        t1 = start + timedelta(seconds=t1)
        t2 = start + timedelta(seconds=t2)
        print(t1, t2)
    start += one_day

Result looks like:

2023-04-03 12:10:39 2023-04-03 17:13:36
2023-04-04 09:11:52 2023-04-04 13:57:01
2023-04-05 09:57:52 2023-04-05 16:37:29
2023-04-06 09:36:55 2023-04-06 10:53:52
2023-04-07 13:57:20 2023-04-07 16:22:57
2023-04-10 10:01:00 2023-04-10 13:52:53
2023-04-11 14:15:35 2023-04-11 15:40:56
...
Answered By: Timus

You can create the time ranges dynamically (guarantees a O(N) runtime where N is the number of days).

Based on your example, the start time must be from [8 AM, 5 PM]; the end time must be [start + 1h, 6 PM].

from collections import namedtuple
from datetime import date, timedelta, datetime
from random import randint

# Python built-in times are easy to mess up; work with "second tuples"
SimpleTime = namedtuple("SimpleTime", "hh mm ss")

def timetuple_to_seconds(time_tuple: SimpleTime) -> int:
  return (time_tuple[0] * 60 + time_tuple[1]) * 60 + time_tuple[2]

def combine_timestamp(base_date: date, seconds_since_midnight: int) -> datetime:
  return (
    datetime.fromordinal(base_date.toordinal()) +
    timedelta(seconds=seconds_since_midnight)
  )

one_day = timedelta(days=1)
current_date = date(2023, 4, 1) - one_day               # start on Apr 1
end_date = date(2023, 7, 31)                            # end on Jul 31
start_time = timetuple_to_seconds(SimpleTime(8, 0, 0))  # 8 AM
end_time = timetuple_to_seconds(SimpleTime(18, 0, 0))   # 6 PM
min_gap = timetuple_to_seconds(SimpleTime(1, 0, 0))     # 1h

while current_date < end_date:
    current_date += one_day
    if current_date.weekday() > 4: # weekend
        continue
    time_1 = randint(start_time, end_time - min_gap)    # from start to end - gap
    time_2 = randint(time_1 + min_gap, end_time)        # from time_1 to end
    print(
      combine_timestamp(current_date, time_1),
      'to',
      combine_timestamp(current_date, time_2)
    )

which should return something like

2023-04-03 08:14:14 to 2023-04-03 15:47:28
2023-04-04 08:23:13 to 2023-04-04 16:11:04
2023-04-05 16:08:53 to 2023-04-05 17:19:22
2023-04-06 09:47:17 to 2023-04-06 12:07:44
2023-04-07 11:27:45 to 2023-04-07 17:36:54
2023-04-10 08:43:15 to 2023-04-10 13:56:21
2023-04-11 09:06:26 to 2023-04-11 14:16:13
2023-04-12 13:44:43 to 2023-04-12 17:10:56
...
Answered By: Jedi