I have a problem, which is trying to get a next available date from within list of dates. This list of dates contains dates which are not saturday/sunday or any public holiday. My inputs will be like, what was the first available date in last 180 days, plan is to build a function like below,
d = current date - interval 180 days
d = get_next_available_date(d)
Breaking down the problem
- Get next available from any list of dates
- Create function from point(1) solution to solve problem
1. Get next available from any list of dates
Initializations
# Initializing full feb dates
test_dates = ['01-02-2020', '02-02-2020', '03-02-2020', '04-02-2020', '05-02-2020',
'06-02-2020', '07-02-2020', '08-02-2020', '09-02-2020', '10-02-2020', '11-02-2020',
'12-02-2020', '13-02-2020', '14-02-2020', '15-02-2020', '16-02-2020', '17-02-2020',
'18-02-2020', '19-02-2020', '20-02-2020', '21-02-2020', '22-02-2020', '23-02-2020',
'24-02-2020', '25-02-2020', '26-02-2020', '27-02-2020', '28-02-2020', '29-02-2020']
Remove a week from feb between 09/02/2020 - 15/02/2020
del test_dates[8:15]
After removing a week data looks like this
print(test_dates)
#Output:
['01-02-2020', '02-02-2020', '03-02-2020', '04-02-2020',
'05-02-2020', '06-02-2020', '07-02-2020', '08-02-2020', '16-02-2020', '17-02-2020',
'18-02-2020', '19-02-2020', '20-02-2020', '21-02-2020', '22-02-2020', '23-02-2020',
'24-02-2020', '25-02-2020', '26-02-2020', '27-02-2020', '28-02-2020', '29-02-2020']
Converts string to datetime format
get_datetime = lambda s: dt.datetime.strptime(s, '%d-%m-%Y')
Main logic to get the next available date
base_date = '10-02-2020'
base = get_datetime(base_date)
# Converting all the dates to datetime format
availdates = list(map(lambda d: get_datetime(d), test_dates))
print('\n Base date =',base, 'Type =',type(base))
print('\n Available dates =\n',availdates, 'Type =',type(availdates), 'Element Type =', type(availdates[0]))
# Filtering the dates greater than base date
later = filter(lambda d: d > base, availdates)
# Finding the minimum date among the dates greater than base date
closest_date = min(later)
print('\n Next available date =',closest_date, 'Type =',type(closest_date))
print('\n Next available date =',closest_date.strftime("%Y-%m-%d"), 'Type =',type(closest_date.strftime("%Y-%m-%d")))
Output:
test_dates after removing a week =
['01-02-2020', '02-02-2020', '03-02-2020', '04-02-2020', '05-02-2020', '06-02-2020', '07-02-2020', '08-02-2020', '16-02-2020', '17-02-2020', '18-02-2020', '19-02-2020', '20-02-2020', '21-02-2020', '22-02-2020', '23-02-2020', '24-02-2020', '25-02-2020', '26-02-2020', '27-02-2020', '28-02-2020', '29-02-2020']
Base date = 2020-02-10 00:00:00 Type = <class 'datetime.datetime'>
Available dates =
[datetime.datetime(2020, 2, 1, 0, 0), datetime.datetime(2020, 2, 2, 0, 0), datetime.datetime(2020, 2, 3, 0, 0), datetime.datetime(2020, 2, 4, 0, 0), datetime.datetime(2020, 2, 5, 0, 0), datetime.datetime(2020, 2, 6, 0, 0), datetime.datetime(2020, 2, 7, 0, 0), datetime.datetime(2020, 2, 8, 0, 0), datetime.datetime(2020, 2, 16, 0, 0), datetime.datetime(2020, 2, 17, 0, 0), datetime.datetime(2020, 2, 18, 0, 0), datetime.datetime(2020, 2, 19, 0, 0), datetime.datetime(2020, 2, 20, 0, 0), datetime.datetime(2020, 2, 21, 0, 0), datetime.datetime(2020, 2, 22, 0, 0), datetime.datetime(2020, 2, 23, 0, 0), datetime.datetime(2020, 2, 24, 0, 0), datetime.datetime(2020, 2, 25, 0, 0), datetime.datetime(2020, 2, 26, 0, 0), datetime.datetime(2020, 2, 27, 0, 0), datetime.datetime(2020, 2, 28, 0, 0), datetime.datetime(2020, 2, 29, 0, 0)] Type = <class 'list'> Element Type = <class 'datetime.datetime'>
Next available date = 2020-02-16 00:00:00 Type = <class 'datetime.datetime'>
Next available date = 2020-02-16 Type = <class 'str'>
2. Create function from point(1) solution to solve problem
Difference between .today() & .now()
.today()
Returns the current local datetime, without tzinfo
dt.datetime.today() # datetime.datetime(2020, 4, 19, 11, 58, 28, 547738)
print(dt.datetime.today()) # 2020-04-19 11:58:28.831696
.now()
Return the current local date and time. If optional argument tz is None or not specified, this is like today(), but, if possible, supplies more precision
dt.datetime.now() # datetime.datetime(2020, 4, 19, 11, 58, 28, 335700)
print(dt.datetime.now()) # 2020-04-19 11:58:28.830691
Making .now() timezone aware you will have to use import pytz
import pytz
d = dt.datetime.now()
timezone = pytz.timezone('Asia/Calcutta')
d_aware = timezone.localize(d)
d_aware.tzinfo
#Output:
#<DstTzInfo 'Asia/Calcutta' IST+5:30:00 STD>
pytz.all_timezones
will list all available timezones
['Africa/Abidjan',
...
'Asia/Calcutta',
'Asia/Shanghai',
'Europe/London',
'America/Los_Angeles',
'America/New_York',
...
'Zulu']
Above timezone thing, has nothing to do with this article, i just got bit side-tracked.
Below, i am subracting 180 days from current date, that will be the base date or start date.
base = dt.datetime.today() - dt.timedelta(days=180)
print('Base Date : ',base)
#Output:
#Base Date : 2019-10-22 12:29:19.946372
Now, merging point(1) and timedelta of point(2) to create a new function,
def get_next_available_date(test_dates, days):
base = dt.datetime.today() - dt.timedelta(days=days)
print(' Base date =',base, 'Type =',type(base))
availdates = list(map(lambda d: get_datetime(d), test_dates))
#print('\n Available dates =\n',availdates, 'Type =',type(availdates), 'Element Type =', type(availdates[0]))
later = filter(lambda d: d > base, availdates)
#closest_date = min(later, key = lambda d: get_datetime(d))
closest_date = min(later)
print(' Next available date =',closest_date, 'Type =',type(closest_date))
closest_date = closest_date.strftime("%Y-%m-%d")
return closest_date
Below is the call,
print('timedelta =',dt.datetime.today() - dt.timedelta(days=69))
#timedelta = 2020-02-10 12:45:21.150761
print(get_next_available_date(test_dates, 69))
# Base date = 2020-02-10 12:47:05.276859 Type = <class 'datetime.datetime'>
# Next available date = 2020-02-16 00:00:00 Type = <class 'datetime.datetime'>
#2020-02-16
Below is the logic used to generate the dates for initialization,
start = dt.datetime.strptime("01-02-2020", "%d-%m-%Y")
end = dt.datetime.strptime("29-02-2020", "%d-%m-%Y")
date_generated = [start + dt.timedelta(days=x) for x in range(0, (end-start).days+1)]
test_dates = []
for date in date_generated:
test_dates.append(date.strftime("%d-%m-%Y"))
Other concepts used
1. map
map(func, *iterables)
- Where func is the function on which each element in iterables (as many as they are) would be applied on.
Example 1
my_pets = ['alfred', 'tabitha', 'william', 'arla']
uppered_pets = list(map(str.upper, my_pets))
print(uppered_pets)
# Output : ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']
Example 2
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, range(1,7)))
print(result)
# Output : [3.6, 5.58, 4.009, 56.2424, 9.01344, 32.00013]
Example 3 ( using repeat to have same value repeated for round() )
circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
result = list(map(round, circle_areas, np.repeat(2,6)))
print(result)
# Output : [3.57, 5.58, 4.01, 56.24, 9.01, 32.0]
2. filter
filter(func, iterable)
- Filter passes each element in the iterable through func and returns only the ones that evaluate to true.
Example 1
scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]
def is_A_student(score):
return score > 75
over_75 = list(filter(is_A_student, scores))
print(over_75)
# Output : [90, 76, 88, 81]
Example 2
dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
palindromes = list(filter(lambda word: word == word[::-1], dromes))
print(palindromes)
# Output : ['madam', 'anutforajaroftuna']