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 - 0 2 - 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 - 0 2 - 16 00 : 00 : 00 Type = <class 'datetime.datetime' >
Next available date = 2020 - 0 2 - 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']
Thanks