hey guys i was trying to create a code that generate dates in given range but i faced some problems
the code is as follows:
import datetime
import random
import os
from random import randrange
year_b = 2019
month_b = 1
day_b = 1
year_e = 2020
month_e = 1
day_e = 1
def date_range(start, end, step: datetime.timedelta):
while start < end:
yield start
start += step
rand_list=[5,8,6,9,10]
for d in date_range(
start=datetime.datetime(year_b, month_b, day_b),
end=datetime.datetime(year_e, month_e, day_e),
step=datetime.timedelta(days=rand.choice(rand_list)),
):
print(d)
os.system("pause")
output:
2019-01-01 00:00:00
2019-01-09 00:00:00
2019-01-17 00:00:00
2019-01-25 00:00:00
2019-02-02 00:00:00
Press any key to continue . . .
first problem that the code only select one random value from the list and add it to the date but i need it to select random value for each date generated
second problem is that code the time it not been generated randomly
any ideas to solve those problems???!!
"Random" is often poorly defined. Why not select a specific number of samples uniformly from the distribution?
from datetime import datetime, timedelta
def items(start, end, samples):
total_sec = int((end - start).total_seconds())
deltas = random.sample(range(total_sec), samples) # xrange if py2k!
return (start + timedelta(seconds=delta) for delta in sorted(deltas))
Then you have
samples = 10
start = datetime(2019, 1, 1)
end = datetime(2020, 1, 1)
print(list(items(start, end, samples)))
giving, e.g.:
[datetime.datetime(2019, 1, 12, 16, 40, 53),
datetime.datetime(2019, 2, 1, 1, 41, 45),
datetime.datetime(2019, 2, 25, 10, 29, 51),
datetime.datetime(2019, 3, 10, 10, 24, 48),
datetime.datetime(2019, 4, 3, 12, 46, 14),
datetime.datetime(2019, 8, 12, 18, 30, 57),
datetime.datetime(2019, 9, 11, 3, 59, 6),
datetime.datetime(2019, 9, 27, 3, 9, 36),
datetime.datetime(2019, 10, 13, 14, 23, 37),
datetime.datetime(2019, 12, 14, 12, 23, 5)]
From this base you can easily modify to various other distributions, or use days or microseconds, or allow duplicates.
After reading your question more closely, I'm not entirely convinced this answer is what you're looking for, but I'll leave it up in case it is helpful.
The problem is because randrange(10) evaluates as soon as it is called. You can instead make date_range take a callable that returns a "random" time delta (or a variable delta, or whatever you'd like).
def adjustable_date_range(start, end, random_delta_maker):
while start < end:
yield start
start += random_delta_maker
And use it like so in your example by passing a callable that makes a random time delta on [0-10) days:
for d in adjustable_date_range(
start=datetime.datetime(year_b, month_b, day_b),
end=datetime.datetime(year_e, month_e, day_e),
step=lambda: datetime.timedelta(days=randrange(10)),
):
print(d)
now not all the months are showing:
from datetime import datetime, timedelta
import random
samples = 10
start = datetime(2018, 1, 1)
end = datetime(2020, 1, 1)
def items(start, end, samples):
total_sec = int((end - start).total_seconds())
deltas = random.sample(range(total_sec), samples) # xrange if py2k!
return (start + timedelta(seconds=delta) for delta in sorted(deltas))
for _ in list(items(start, end, samples)):
print(_)
output:
2018-02-01 18:25:48
2018-02-20 20:24:23
2018-06-07 22:03:48
2018-07-20 07:15:37
2018-08-22 07:04:06
2018-08-28 18:02:07
2018-10-09 03:40:58
2019-01-04 15:11:40
2019-03-22 12:16:58
2019-07-22 14:44:00
Related
I want total number of hours between mention dates excluding weekend(Saturday, Sunday).
```
start_time = datetime.datetime(2021, 7, 1, 22, 45, 25)
end_time = datetime.datetime(2021, 7, 15, 10, 00, 00)
```
from BusinessHours import BusinessHours
import datetime
start_time = datetime.datetime(2021, 7, 1, 22, 45, 25)
end_time = datetime.datetime(2021, 7, 15, 10, 00, 00)
hours = BusinessHours(start_time, end_time, worktiming=[9, 18], weekends=[6, 7], holidayfile=None)
print(hours.gethours())
This could help you, for more information please refer BusinessHours module in python!
An additional library is needed for my answer (NumPy)
Key elements:
numpy.busday_count to count the number of weekdays
datetime.timedelta(1) to omit starting day (to be checked through if-else to count for minutes)
isoweekday() returns value between 1 and 7, 1 being Monday and 7 Sunday
Steps:
Skip the starting and ending days and find valid in-between days. Multiply by 24 (total hours per day)
Check if starting day is a weekday and calculate the total hours remaining to end that date
Check if the ending day is a weekday and calculate the total hours passed in that day
import datetime
import numpy as np
start_time = datetime.datetime(2021, 7, 1, 22, 45, 25)
end_time = datetime.datetime(2021, 7, 15, 10, 00, 00)
#Step 1.
total_hours=np.busday_count(start_time.date()+datetime.timedelta(1),end_time.date())*24 #already not counting last date
#Step2.
if start_time.isoweekday() in range(1, 6):
total_hours=total_hours+24-start_time.hour-start_time.minute/60-start_time.second/3600
#Step 3.
if end_time.isoweekday() in range(1, 6):
total_hours=total_hours+end_time.hour+end_time.minute/60+end_time.second/3600
print(total_hours)
output: 227.24305555555554
This will do it:
busdays = np.busday_count(start_time.date(), end_time.date())
I have two UNIX timestamps that have distance in seconds D, where D = end - start. I want to find how many of these D seconds belong to an another time window [start2, end2], i.e. how many seconds intersect. Obviously it is not time2 - time1.
For example
start timestamp 1621707530 is Saturday, May 22, 2021 6:18:50 PM (GMT)
end timestamp 1621736330 is Sunday, May 23, 2021 2:18:50 AM (GMT)
How to find (preferably in Python) how many seconds between 01:00 AM and 02:00 AM belong to my [start, end] window?
For example interval [09:00 AM, 10:00 AM] does not intersect with [01:00 AM,02:00 AM], however interval [00:55 AM,01:03 AM] intersects for 3 minutes, or 180 seconds.
My code so far:
from datetime import datetime
from collections import namedtuple
Range = namedtuple('Range', ['start', 'end'])
r1 = Range(start=datetime(2021, 5, 22, 22, 23, 00),
end=datetime(2021, 5, 23, 2, 1, 2))
r2 = Range(start=datetime(2021, 5, 23, 1, 0, 00),
end=datetime(2021, 5, 23, 2, 0, 00))
latest_start = max(r1.start, r2.start)
earliest_end = min(r1.end, r2.end)
delta = (earliest_end - latest_start).seconds
overlap = max(0, delta)
print(overlap)
Can it be done with a simpler method?
Based on the answer: https://stackoverflow.com/a/46992092/14265469
The condition if (r1.start <= r2.end) and (r2.start <= r1.end): works surprisingly well:
from datetime import datetime
from collections import namedtuple
Range = namedtuple('Range', ['start', 'end'])
r1 = Range(start=datetime(2021, 5, 22, 22, 23, 20),
end=datetime(2021, 5, 22, 22, 23, 55))
r2 = Range(start=datetime(2021, 5, 22, 22, 23, 0),
end=datetime(2021, 5, 22, 22, 23, 25))
delta = "No overlapping"
if (r1.start <= r2.end) and (r2.start <= r1.end):
latest_start = max(r1.start, r2.start)
earliest_end = min(r1.end, r2.end)
delta = (earliest_end - latest_start).seconds
print(delta) # 5
I want to generate a list of length n with random datetime strings in Python within a range.
import datetime
start = datetime.datetime(2019,1,1,0,0,0).astimezone().replace(microsecond=0).isoformat()
end = datetime.datetime(2019,12,31,23,59,59).astimezone().replace(microsecond=0).isoformat()
I had asked a similar question before, but I did not use the correct format for datetime.What is the best way to achieve this?
Modifying the answer here for your format: https://stackoverflow.com/a/553448/7942856
from random import randrange
from datetime import timedelta, datetime
start = datetime(2019,1,1,0,0,0).astimezone().replace(microsecond=0)
end = datetime(2019,12,31,23,59,59).astimezone().replace(microsecond=0)
n = 5
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)
random_dates = [random_date(start, end) for i in range(n)]
Outputs:
[datetime.datetime(2019, 8, 6, 2, 25, 32, tzinfo=datetime.timezone(datetime.timedelta(seconds=39600), 'AUS Eastern Summer Time')), datetime.datetime(2019, 8, 5, 13, 2, 15, tzinfo=datetime.timezone(datetime.timedelta(seconds=39600), 'AUS Eastern Summer Time')), datetime.datetime(2019, 6, 24, 15, 57, 19, tzinfo=datetime.timezone(datetime.timedelta(seconds=39600), 'AUS Eastern Summer Time')), datetime.datetime(2019, 5, 3, 9, 35, 8, tzinfo=datetime.timezone(datetime.timedelta(seconds=39600), 'AUS Eastern Summer Time')), datetime.datetime(2019, 2, 12, 14, 33, 35, tzinfo=datetime.timezone(datetime.timedelta(seconds=39600), 'AUS Eastern Summer Time'))]
Then if you want the text ISO format, simply call .isoformat() on your list elements.
[random_date(start, end).isoformat() for i in range(n)]
#['2019-05-03T22:06:56+11:00', '2019-12-31T06:56:09+11:00', '2019-03-09T05:07:59+11:00', '2019-03-24T09:24:42+11:00', '2019-01-08T03:23:31+11:00']
I have some trouble to understand the behavior of dateutil.relativedelta.
I understand that relativedelta could return past dates if I use negative arguments as specified in relativedelta doc.
However, when I provide positive parameters, I expect that it always return a date in the future... that seems legit right?
My use case is the following : we are Tuesday, it is 8:35. I want to get the date of the closest Monday and Tuesday at 6:00.
Here what I did. The first result seems correct to me, while the second one is wrong.
>>> import datetime
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2016, 11, 29, 8, 35, 23, 786349)
>>> from dateutil import relativedelta
>>> now.weekday()
1
>>> now + relativedelta.relativedelta(weekday=0, hour=6, minute=0) # should give a time in the future
datetime.datetime(2016, 12, 5, 6, 0, 23, 786349) # here this is correct, in the future
>>> now + relativedelta.relativedelta(weekday=1, hour=6, minute=0) # should give a time in the future
datetime.datetime(2016, 11, 29, 6, 0, 23, 786349) # but this is in the past / I would expect result (2016, 12, 6, 6, 0, 23, 786349)
So , am I doing something wrong here ?
So according to your initial date, you're actually at 8AM, but you're targeting 6AM by using the hour param, if you're trying to increment one hour, you should use hours and minutes respectively
>>> now
datetime.datetime(2016, 11, 29, 3, 5, 41, 763818)
>>> now.weekday()
1
>>> now + relativedelta.relativedelta(weekday=1, hour=1)
datetime.datetime(2016, 11, 29, 1, 5, 41, 763818) # Notice how it's in the past
>>> now + relativedelta.relativedelta(weekday=1, hours=1)
datetime.datetime(2016, 11, 29, 4, 5, 41, 763818) # Notice how it's one hour in the future
>>> n + relativedelta.relativedelta(weekday=1, hour=6, minute=0, weeks=1)
datetime.datetime(2016, 12, 6, 6, 0, 41, 763818)
I think it's in the doc:
Starting with, about weekday:
These instances may receive a parameter N, specifying the Nth weekday, which could be positive or negative (like MO(+1) or MO(-2). Not specifying it is the same as specifying +1.
So by passing 1, it's like you're passing (1, 1)
Then, continuing on the doc, on the 7th dot of behavior of operations with relativedelta:
Notice that if the calculated date is already Monday, for example, using (0, 1) or (0, -1) won’t change the day.
So the 29th of November is already a Tuesday, and you're asking for a Tuesday.
So nothing changes.
Let's imagine this datetime
>>> import datetime
>>> dt = datetime.datetime(2012, 10, 25, 17, 32, 16)
I'd like to ceil it to the next quarter of hour, in order to get
datetime.datetime(2012, 10, 25, 17, 45)
I imagine something like
>>> quarter = datetime.timedelta(minutes=15)
>>> import math
>>> ceiled_dt = math.ceil(dt / quarter) * quarter
But of course, this does not work
This one takes microseconds into account!
import math
def ceil_dt(dt):
# how many secs have passed this hour
nsecs = dt.minute*60 + dt.second + dt.microsecond*1e-6
# number of seconds to next quarter hour mark
# Non-analytic (brute force is fun) way:
# delta = next(x for x in xrange(0,3601,900) if x>=nsecs) - nsecs
# analytic way:
delta = math.ceil(nsecs / 900) * 900 - nsecs
#time + number of seconds to quarter hour mark.
return dt + datetime.timedelta(seconds=delta)
t1 = datetime.datetime(2017, 3, 6, 7, 0)
assert ceil_dt(t1) == t1
t2 = datetime.datetime(2017, 3, 6, 7, 1)
assert ceil_dt(t2) == datetime.datetime(2017, 3, 6, 7, 15)
t3 = datetime.datetime(2017, 3, 6, 7, 15)
assert ceil_dt(t3) == t3
t4 = datetime.datetime(2017, 3, 6, 7, 16)
assert ceil_dt(t4) == datetime.datetime(2017, 3, 6, 7, 30)
t5 = datetime.datetime(2017, 3, 6, 7, 30)
assert ceil_dt(t5) == t5
t6 = datetime.datetime(2017, 3, 6, 7, 31)
assert ceil_dt(t6) == datetime.datetime(2017, 3, 6, 7, 45)
t7 = datetime.datetime(2017, 3, 6, 7, 45)
assert ceil_dt(t7) == t7
t8 = datetime.datetime(2017, 3, 6, 7, 46)
assert ceil_dt(t8) == datetime.datetime(2017, 3, 6, 8, 0)
Explanation of delta:
900 seconds is 15 minutes (a quarter of an hour sans leap seconds which I don't think datetime handles...)
nsecs / 900 is the number of quarter hour chunks that have transpired. Taking the ceil of this rounds up the number of quarter hour chunks.
Multiply the number of quarter hour chunks by 900 to figure out how many seconds have transpired in since the start of the hour after "rounding".
#Mark Dickinson suggested the best formula so far:
def ceil_dt(dt, delta):
return dt + (datetime.min - dt) % delta
In Python 3, for an arbitrary time delta (not just 15 minutes):
#!/usr/bin/env python3
import math
from datetime import datetime, timedelta
def ceil_dt(dt, delta):
return datetime.min + math.ceil((dt - datetime.min) / delta) * delta
print(ceil_dt(datetime(2012, 10, 25, 17, 32, 16), timedelta(minutes=15)))
# -> 2012-10-25 17:45:00
To avoid intermediate floats, divmod() could be used:
def ceil_dt(dt, delta):
q, r = divmod(dt - datetime.min, delta)
return (datetime.min + (q + 1)*delta) if r else dt
Example:
>>> ceil_dt(datetime(2012, 10, 25, 17, 32, 16), timedelta(minutes=15))
datetime.datetime(2012, 10, 25, 17, 45)
>>> ceil_dt(datetime.min, datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0)
>>> ceil_dt(datetime.min, 2*datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0)
>>> ceil_dt(datetime.max, datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
>>> ceil_dt(datetime.max, 2*datetime.resolution)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ceil_dt
OverflowError: date value out of range
>>> ceil_dt(datetime.min+datetime.resolution, datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0, 0, 1)
>>> ceil_dt(datetime.min+datetime.resolution, 2*datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0, 0, 2)
>>> ceil_dt(datetime.max-datetime.resolution, datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998)
>>> ceil_dt(datetime.max-datetime.resolution, 2*datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998)
>>> ceil_dt(datetime.max-2*datetime.resolution, datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999997)
>>> ceil_dt(datetime.max-2*datetime.resolution, 2*datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998)
>>> ceil_dt(datetime.max-timedelta(1), datetime.resolution)
datetime.datetime(9999, 12, 30, 23, 59, 59, 999999)
>>> ceil_dt(datetime.max-timedelta(1), 2*datetime.resolution)
datetime.datetime(9999, 12, 31, 0, 0)
>>> ceil_dt(datetime.min, datetime.max-datetime.min)
datetime.datetime(1, 1, 1, 0, 0)
>>> ceil_dt(datetime.max, datetime.max-datetime.min)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
def ceil(dt):
if dt.minute % 15 or dt.second:
return dt + datetime.timedelta(minutes = 15 - dt.minute % 15,
seconds = -(dt.second % 60))
else:
return dt
This gives you:
>>> ceil(datetime.datetime(2012,10,25, 17,45))
datetime.datetime(2012, 10, 25, 17, 45)
>>> ceil(datetime.datetime(2012,10,25, 17,45,1))
datetime.datetime(2012, 10, 25, 18, 0)
>>> ceil(datetime.datetime(2012,12,31,23,59,0))
datetime.datetime(2013,1,1,0,0)
You just need to calculate correct minutes and add them in datetime object after setting minutes, seconds to zero
import datetime
def quarter_datetime(dt):
minute = (dt.minute//15+1)*15
return dt.replace(minute=0, second=0)+datetime.timedelta(minutes=minute)
for minute in [12, 22, 35, 52]:
print quarter_datetime(datetime.datetime(2012, 10, 25, 17, minute, 16))
It works for all cases:
2012-10-25 17:15:00
2012-10-25 17:30:00
2012-10-25 17:45:00
2012-10-25 18:00:00
The formula proposed here by #Mark Dickinson worked beautifully, but I needed a solution that also handled timezones and Daylight Savings Time (DST).
Using pytz, I arrived at:
import pytz
from datetime import datetime, timedelta
def datetime_ceiling(dt, delta):
# Preserve original timezone info
original_tz = dt.tzinfo
if original_tz:
# If the original was timezone aware, translate to UTC.
# This is necessary because datetime math does not take
# DST into account, so first we normalize the datetime...
dt = dt.astimezone(pytz.UTC)
# ... and then make it timezone naive
dt = dt.replace(tzinfo=None)
# We only do math on a timezone naive object, which allows
# us to pass naive objects directly to the function
dt = dt + ((datetime.min - dt) % delta)
if original_tz:
# If the original was tz aware, we make the result aware...
dt = pytz.UTC.localize(dt)
# ... then translate it from UTC back its original tz.
# This translation applies appropriate DST status.
dt = dt.astimezone(original_tz)
return dt
A nearly identical floor function can be made by changing one line of code:
def datetime_floor(dt, delta):
...
dt = dt - ((datetime.min - dt) % delta)
...
The following datetime is three minutes before the transition from DST back to Standard Time (STD):
datetime.datetime(2020, 11, 1, 1, 57, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)
Assuming the above as dt, we can round down to the nearest five minute increment using our floor function:
>>> datetime_floor(dt, timedelta(minutes=5))
datetime.datetime(2020, 11, 1, 1, 55, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)
The timezone and relationship to DST is preserved. (The same would be true for the ceiling function.)
On this date DST will end at 2 am, at which point the time will "roll back" to 1am STD. If we use our ceiling function to round up from 1:57am DST, we should not end up at 2am DST, but rather at 1:00am STD, which is the result we get:
>>> datetime_ceiling(dt, timedelta(minutes=5))
datetime.datetime(2020, 11, 1, 1, 0, tzinfo=<DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>)
Here is my code working with any periods:
def floorDT(dt, secperiod):
tstmp = dt.timestamp()
return datetime.datetime.fromtimestamp(
math.floor(tstmp/secperiod)*secperiod).astimezone().astimezone(datetime.timezone.utc)
def ceilDT(dt, secperiod):
tstmp = dt.timestamp()
return datetime.datetime.fromtimestamp(
math.ceil(tstmp/secperiod)*secperiod).astimezone().astimezone(datetime.timezone.utc)
Note: we must use astimezone().astimezone() trick else it uses local timezone during converting from timestamp
Based on the example by Mark Dickinson's, here is an enhanced datetime round up function that also preserves the timezone information of the original timestamp (both naive and timezone aware):
def round_datetime_up(
ts: datetime.datetime,
delta: datetime.timedelta,
offset: datetime.timedelta = datetime.timedelta(minutes=0)) -> datetime.datetime:
"""Snap to next available timedelta.
Preserve any timezone info on `ts`.
If we are at the the given exact delta, then do not round, only add offset.
:param ts: Timestamp we want to round
:param delta: Our snap grid
:param offset: Add a fixed time offset at the top of rounding
:return: Rounded up datetime
"""
rounded = ts + (datetime.datetime.min.replace(tzinfo=ts.tzinfo) - ts) % delta
return rounded + offset
See full code and tests.