How to calculate the difference between 2 dates, split them dynamically? Python - python

I have to write a function which will count a difference in days between 2 dates. If the number of days exceeds 30, then split it in n date ranges and save in list or dict. I have started with the function but cannot finish it. The function has to calculate dynamic values.
For example
start_date = '2020-07-01'
end_date = '2020-09-15'
difference = (end_date - start_date).days
dateranges = []
dateranges.append(start_date)
if difference > 30:
end_date = start_date + dt.timedelta(days=30)
dateranges.append(end_date)
But I do not get how to make it cyclic, when it take each time a new start_date and end_date and calculates the difference between them. For example I always add 30 dys here but it can be that a less number of days has to be added.

If you want list of date ranges, between 2 dates, with maximum difference of 30 days, you can use timedelta to iterate over the range and split accordingly.
from datetime import datetime
from datetime import timedelta
def get_range(start_date, end_date, date_diff):
start_date = datetime.strptime(start_date, "%Y-%m-%d")
end_date = datetime.strptime(end_date, "%Y-%m-%d")
if abs((end_date - start_date).days) <= date_diff:
return [datetime.strftime(start_date,"%Y-%m-%d"),datetime.strftime(end_date,"%Y-%m-%d")]
else:
result=[]
while 1:
d3=start_date+timedelta(days=date_diff)
if d3>=end_date:
result.append([datetime.strftime(start_date,"%Y-%m-%d"),datetime.strftime(end_date,"%Y-%m-%d")])
break
else:
result.append([datetime.strftime(start_date,"%Y-%m-%d"),datetime.strftime(d3,"%Y-%m-%d")])
start_date=d3+timedelta(days=1)
return result
print(get_range('2020-07-01', '2020-09-15',30))
Output
[['2020-07-01', '2020-07-31'], ['2020-08-01', '2020-08-31'], ['2020-09-01', '2020-09-15']]
Here I assume, by date range, you mean start and end date like [start,end].

For example I always add 30 dys here but it can be that a less number
of days has to be added.
datetime library and timedelta function takes care of it and increase month if need.
from datetime import datetime
from datetime import timedelta
start_date = '2020-07-01'
end_date = '2020-09-15'
def date_difference(d1, d2):
d1 = datetime.strptime(d1, "%Y-%m-%d")
d2 = datetime.strptime(d2, "%Y-%m-%d")
if abs((d2 - d1).days) > 30:
dates = []
# if you don't want to include start_date, use range(1, 30) instead.
for i in range(0, 30):
dates.append(d1 + timedelta(days=i))
return dates
return []
print(date_difference(start_date, end_date))
Output:
[datetime.datetime(2020, 7, 1, 0, 0), datetime.datetime(2020, 7, 2, 0, 0),
datetime.datetime(2020, 7, 3, 0, 0), datetime.datetime(2020, 7, 4, 0, 0),
datetime.datetime(2020, 7, 5, 0, 0), datetime.datetime(2020, 7, 6, 0, 0),
datetime.datetime(2020, 7, 7, 0, 0), datetime.datetime(2020, 7, 8, 0, 0),
datetime.datetime(2020, 7, 9, 0, 0), datetime.datetime(2020, 7, 10, 0, 0),
...
datetime.datetime(2020, 7, 30, 0, 0)]

from datetime import datetime,timedelta
def split_dates(prev_date,next_date,interval):
date_ranges = []
if next_date > prev_date+timedelta(days=30):
while prev_date <= next_date:
prev_date += timedelta(days=interval)
date_ranges.append(prev_date)
return date_ranges

Related

How to get a list of some specific weekdays within a date range in python

I need to get a list of Mondays within a date range.
For example:
# selected date range
date_range = ['2019-02-25', '2019-03-25']
# desired output:
mondays = ['2019-02-25', '2019-03-04', '2019-03-11', '2019-03-18', '2019-03-25']
# selected date range
date_range = ['2019-02-25', '2019-03-20']
# desired output
mondays = ['2019-02-25', '2019-03-04', '2019-03-11', '2019-03-18']
The starting date is always a Monday.
Does anyone know how I can generate the list of Mondays using python datetime?
This is one approach using weekday().
Ex:
import datetime
def get_mondays(date_start, date_end):
date_start = datetime.datetime.strptime(date_start, "%Y-%m-%d")
date_end = datetime.datetime.strptime(date_end, "%Y-%m-%d")
result = []
while date_start <= date_end:
if date_start.weekday() == 0: #0 == Monday
result.append(date_start.strftime("%Y-%m-%d"))
date_start += datetime.timedelta(days=1)
return result
print(get_mondays('2019-02-25', '2019-03-25'))
print(get_mondays('2019-02-25', '2019-03-20'))
Output:
['2019-02-25', '2019-03-04', '2019-03-11', '2019-03-18', '2019-03-25']
['2019-02-25', '2019-03-04', '2019-03-11', '2019-03-18']
In case you can use 3rd-party module dateutil:
from dateutil import rrule, parser
def case_of_the_mondays(start,end):
mondays = list(rrule.rrule(rrule.WEEKLY,
dtstart=parser.parse(start),
until=parser.parse(end)))
return mondays
Result:
In [22]: case_of_the_mondays('2019-02-25','2019-03-25')
Out[22]:
[datetime.datetime(2019, 2, 25, 0, 0),
datetime.datetime(2019, 3, 4, 0, 0),
datetime.datetime(2019, 3, 11, 0, 0),
datetime.datetime(2019, 3, 18, 0, 0),
datetime.datetime(2019, 3, 25, 0, 0)]

How to retrieve first and last date of previous 3 months?

Given today's date, what is the efficient way to retrieve the first and last date for previous 3 months (i.e. 3/1/2020' and '3/31/2020'; '2/1/2020' and '2/29/2020'; '1/1/2020' and '1/31/2020')?
EDIT
For previous month's first and last, the following code is working as expected. But I am not sure how to retrieve the previous 2nd and 3rd month's first and last date.
from datetime import date, timedelta
last_day_of_prev_month = date.today().replace(day=1) - timedelta(days=1)
start_day_of_prev_month = (date.today().replace(day=1)
- timedelta(days=last_day_of_prev_month.day))
# For printing results
print("First day of prev month:", start_day_of_prev_month)
print("Last day of prev month:", last_day_of_prev_month)
You may
get the 3 previous month
create the date with day 1, and last day by going to the next and remove 1 day
def before_month(month):
v = [9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
return v[month:month + 3]
dd = datetime(2020, 4, 7)
dates = [[dd.replace(month=month, day=1), dd.replace(month=month, day=monthrange(dd.year, month)[1])]
for month in before_month(dd.month)]
print(dates)
# [[datetime.datetime(2020, 1, 1, 0, 0), datetime.datetime(2020, 1, 31, 0, 0)],
# [datetime.datetime(2020, 2, 1, 0, 0), datetime.datetime(2020, 2, 29, 0, 0)],
# [datetime.datetime(2020, 3, 1, 0, 0), datetime.datetime(2020, 3, 31, 0, 0)]]
I did not found another nice way to get the 3 previous month, but sometimes the easiest way it the one to use
You can loop over the 3 previous month, just update the date to the first day of the actual month at the end of every iteration:
from datetime import date, timedelta
d = date.today()
date_array = []
date_string_array = []
for month in range(1, 4):
first_day_of_month = d.replace(day=1)
last_day_of_previous_month = first_day_of_month - timedelta(days=1)
first_day_of_previous_month = last_day_of_previous_month.replace(day=1)
date_array.append((first_day_of_previous_month, last_day_of_previous_month))
date_string_array.append((first_day_of_previos_month.strftime("%m/%d/%Y"), last_day_of_previos_month.strftime("%m/%d/%Y")))
d = first_day_of_previos_month
print(date_array)
print(date_string_array)
Results:
[(datetime.date(2020, 3, 1), datetime.date(2020, 3, 31)), (datetime.date(2020, 2, 1), datetime.date(2020, 2, 29)), (datetime.date(2020, 2, 1), datetime.date(2020, 2, 29))]
[('03/01/2020', '03/31/2020'), ('03/01/2020', '03/31/2020'), ('03/01/2020', '03/31/2020')]

Is there a way to set a number to equal a date in python?

I have a few files that have a randomly generated number that corresponds with a date:
736815 = 01/05/2018
I need to create a function or process that applies logic to sequential numbers so that the next number equals the next calendar date.
Ideally i would need it in a key:pair format, so that when i convert the file to a new format i can apply the date in place of the auto file name.
Hopefully this makes more sense, it is to be used to name a converted file.
I think origin parameter is possible use here, also add unit='D' to to_datetime:
df = pd.DataFrame({'col':[5678, 5679, 5680]})
df['date'] = pd.to_datetime(df['col'] - 5678, unit='D', origin=pd.Timestamp('2020-01-01'))
print (df)
col date
0 5678 2020-01-01
1 5679 2020-01-02
2 5680 2020-01-03
Non pandas solution, only pure python with same idea:
from datetime import datetime, timedelta
L = [5678, 5679, 5680]
a = [timedelta(x-5678) + datetime(2020,1,1) for x in L]
print (a)
[datetime.datetime(2020, 1, 1, 0, 0),
datetime.datetime(2020, 1, 2, 0, 0),
datetime.datetime(2020, 1, 3, 0, 0)]
The number doesn't need to translate into the date directly in any way. You just need to pick a start date and a number, and add another number either via simple addition or via a timedelta:
from datetime import date, timedelta
from random import randint
start_date = date.today()
start_int = randint(1000, 10000)
for i in range(10):
print(start_int + i, start_date + timedelta(days=i))
6964 2020-01-29
6965 2020-01-30
6966 2020-01-31
6967 2020-02-01
6968 2020-02-02
6969 2020-02-03
6970 2020-02-04
6971 2020-02-05
6972 2020-02-06
6973 2020-02-07
If you're getting your list of numbers from somewhere else, add/subtract appropriately from a start int/date for the same effect.
Another solution is to create an object, which encapsulates the date and the base number to count from. Each call to this object (implemented using the __call__ special method) will create a new date object using the time delta between the base number and the supplied number.
import datetime
class RelativeDate:
def __init__(self, date, base):
self.date = date
self.base = base
def __call__(self, number):
delta = datetime.timedelta(days=number - self.base)
return self.date + delta
def create_base_date(number, date):
return RelativeDate(
date=datetime.datetime.strptime(date, '%d/%m/%Y'),
base=number,
)
base_date = create_base_date(1, '03/01/2020')
base_date(3)
datetime.datetime(2020, 1, 5, 0, 0)
Example snippet:
base_date = create_base_date(1, '03/01/2020')
{i: base_date(i) for i in range(1, 10)}
Output:
{1: datetime.datetime(2020, 1, 3, 0, 0),
2: datetime.datetime(2020, 1, 4, 0, 0),
3: datetime.datetime(2020, 1, 5, 0, 0),
4: datetime.datetime(2020, 1, 6, 0, 0),
5: datetime.datetime(2020, 1, 7, 0, 0),
6: datetime.datetime(2020, 1, 8, 0, 0),
7: datetime.datetime(2020, 1, 9, 0, 0),
8: datetime.datetime(2020, 1, 10, 0, 0),
9: datetime.datetime(2020, 1, 11, 0, 0)}

Reference each occurence of a day of the month given start datetime and an end datetime

As the title says, I'm trying to generate a list of datetimes corresponding to the occurrences of a specific day of the month between two dates.
So given a start date, an end date, and a day of the month, I want to see every occurrence of that day of the month:
from datetime import datetime
end_date = datetime(2012, 9, 15, 0, 0)
start_date = datetime(2012, 6, 1, 0, 0)
day_of_month = 16
dates = "magic code goes here"
dates would then hold an array as such:
dates == [
datetime(2012, 6, 16, 0, 0),
datetime(2012, 7, 16, 0, 0),
datetime(2012, 8, 16, 0, 0)
]
The issue I'm running into is the number of checks I have to perform. First I have to check if it's the start year, if so, then I have to start at the beginning month, but if the day of the month is before the start date, then I have to skip that month. This same thing applies for the end of the period. Not to mention I have to check if the period starts and ends in the same year. All in all it's turning into quite a mess of nested if and for statements.
Here is my solution:
import numpy as np
for year in np.arange(start_date.year, end_date.year + 1):
for month in np.arange(1, 13):
date = datetime(year, month, day_of_month, 0, 0)
if start_date < date < end_date:
dates.append(date)
Is there a more Pythonic way to accomplish this?
Here's a quick and dirty (but reasonably efficient) solution:
import datetime
d = start_date
days = []
while d <= end_date: # Change to < if you do not want the end_date
if d.day == day_of_month:
days.append(d)
d += datetime.timedelta(1)
days
# [datetime.datetime(2012, 6, 16, 0, 0),
# datetime.datetime(2012, 7, 16, 0, 0),
# datetime.datetime(2012, 8, 16, 0, 0)]
Ideally, you want to use pandas for this.
This is a succinct, but not efficient, way using pandas.date_range.
from datetime import datetime
import pandas as pd
end_date = datetime(2012, 9, 15, 0, 0)
start_date = datetime(2012, 6, 1, 0, 0)
day_of_month = 16
rng = [i.to_pydatetime() for i in pd.date_range(start_date, end_date, freq='1D') if i.day == day_of_month]
# [datetime.datetime(2012, 6, 16, 0, 0),
# datetime.datetime(2012, 7, 16, 0, 0),
# datetime.datetime(2012, 8, 16, 0, 0)]
Here is a more efficient method using a generator for the date range, which does not rely on pandas:
def daterange(start_date, end_date):
for n in range(int ((end_date - start_date).days)):
yield start_date + timedelta(n)
rng = [i for i in daterange(start_date, end_date) if i.day == day_of_month]
# [datetime.datetime(2012, 6, 16, 0, 0),
# datetime.datetime(2012, 7, 16, 0, 0),
# datetime.datetime(2012, 8, 16, 0, 0)]

A more suitable way to rewrite this?

I have the method:
def checkAgainstDate():
currentDate = date.today()
currentMonth = date.today().month
if currentMonth == 1
year = currentDate.year-1
return date(year, 11, 01)
elif currentMonth == 2:
year = currentDate.year-1
return date(year, 12, 01)
else
return date(currentDate.year, currentMonth-2, 01)
This just returns the first of the month 2 months ago, which is what I want is there a better approach I could have used using timedeltas? I choose my way because weeks in a month are not always constant.
dateutil is an amazing thing. It really should become stdlib someday.
>>> from dateutil.relativedelta import relativedelta
>>> from datetime import datetime
>>> (datetime.now() - relativedelta(months=2)).replace(day=1)
datetime.datetime(2010, 6, 1, 13, 16, 29, 643077)
>>> (datetime(2010, 4, 30) - relativedelta(months=2)).replace(day=1)
datetime.datetime(2010, 2, 1, 0, 0)
>>> (datetime(2010, 2, 28) - relativedelta(months=2)).replace(day=1)
datetime.datetime(2009, 12, 1, 0, 0)
Convert to an "absolute month number", subtract 2, convert back to year & month:
currentdate = date.today()
monthindex = 12*currentdate.year + (currentdate.month-1) -2
return datetime( monthindex // 12, monthindex % 12 + 1, 1)

Categories