Skip to content
bobby_dreamer

Python playing with dates

python, pandas1 min read

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,

1d = current date - interval 180 days
2d = get_next_available_date(d)

Breaking down the problem

  1. Get next available from any list of dates
  2. Create function from point(1) solution to solve problem

1. Get next available from any list of dates

Initializations

1# Initializing full feb dates
2test_dates = ['01-02-2020', '02-02-2020', '03-02-2020', '04-02-2020', '05-02-2020',
3 '06-02-2020', '07-02-2020', '08-02-2020', '09-02-2020', '10-02-2020', '11-02-2020',
4 '12-02-2020', '13-02-2020', '14-02-2020', '15-02-2020', '16-02-2020', '17-02-2020',
5 '18-02-2020', '19-02-2020', '20-02-2020', '21-02-2020', '22-02-2020', '23-02-2020',
6 '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

1del test_dates[8:15]

After removing a week data looks like this

1print(test_dates)
2
3#Output:
4
5['01-02-2020', '02-02-2020', '03-02-2020', '04-02-2020',
6 '05-02-2020', '06-02-2020', '07-02-2020', '08-02-2020', '16-02-2020', '17-02-2020',
7 '18-02-2020', '19-02-2020', '20-02-2020', '21-02-2020', '22-02-2020', '23-02-2020',
8 '24-02-2020', '25-02-2020', '26-02-2020', '27-02-2020', '28-02-2020', '29-02-2020']

Converts string to datetime format

1get_datetime = lambda s: dt.datetime.strptime(s, '%d-%m-%Y')

Main logic to get the next available date

1base_date = '10-02-2020'
2base = get_datetime(base_date)
3
4# Converting all the dates to datetime format
5availdates = list(map(lambda d: get_datetime(d), test_dates))
6print('\n Base date =',base, 'Type =',type(base))
7print('\n Available dates =\n',availdates, 'Type =',type(availdates), 'Element Type =', type(availdates[0]))
8
9# Filtering the dates greater than base date
10later = filter(lambda d: d > base, availdates)
11
12# Finding the minimum date among the dates greater than base date
13closest_date = min(later)
14print('\n Next available date =',closest_date, 'Type =',type(closest_date))
15print('\n Next available date =',closest_date.strftime("%Y-%m-%d"), 'Type =',type(closest_date.strftime("%Y-%m-%d")))

Output:

1test_dates after removing a week =
2 ['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']
3
4 Base date = 2020-02-10 00:00:00 Type = <class 'datetime.datetime'>
5
6 Available dates =
7 [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'>
8
9 Next available date = 2020-02-16 00:00:00 Type = <class 'datetime.datetime'>
10
11 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

1dt.datetime.today() # datetime.datetime(2020, 4, 19, 11, 58, 28, 547738)
2print(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

1dt.datetime.now() # datetime.datetime(2020, 4, 19, 11, 58, 28, 335700)
2print(dt.datetime.now()) # 2020-04-19 11:58:28.830691

Making .now() timezone aware you will have to use import pytz

1import pytz
2d = dt.datetime.now()
3timezone = pytz.timezone('Asia/Calcutta')
4d_aware = timezone.localize(d)
5d_aware.tzinfo
6
7#Output:
8#<DstTzInfo 'Asia/Calcutta' IST+5:30:00 STD>

pytz.all_timezones will list all available timezones

1['Africa/Abidjan',
2...
3'Asia/Calcutta',
4'Asia/Shanghai',
5'Europe/London',
6'America/Los_Angeles',
7'America/New_York',
8...
9 '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.

1base = dt.datetime.today() - dt.timedelta(days=180)
2print('Base Date : ',base)
3#Output:
4#Base Date : 2019-10-22 12:29:19.946372

Now, merging point(1) and timedelta of point(2) to create a new function,

1def get_next_available_date(test_dates, days):
2 base = dt.datetime.today() - dt.timedelta(days=days)
3 print(' Base date =',base, 'Type =',type(base))
4 availdates = list(map(lambda d: get_datetime(d), test_dates))
5 #print('\n Available dates =\n',availdates, 'Type =',type(availdates), 'Element Type =', type(availdates[0]))
6 later = filter(lambda d: d > base, availdates)
7 #closest_date = min(later, key = lambda d: get_datetime(d))
8 closest_date = min(later)
9 print(' Next available date =',closest_date, 'Type =',type(closest_date))
10 closest_date = closest_date.strftime("%Y-%m-%d")
11 return closest_date

Below is the call,

1print('timedelta =',dt.datetime.today() - dt.timedelta(days=69))
2#timedelta = 2020-02-10 12:45:21.150761
3
4print(get_next_available_date(test_dates, 69))
5# Base date = 2020-02-10 12:47:05.276859 Type = <class 'datetime.datetime'>
6# Next available date = 2020-02-16 00:00:00 Type = <class 'datetime.datetime'>
7#2020-02-16

Below is the logic used to generate the dates for initialization,

1start = dt.datetime.strptime("01-02-2020", "%d-%m-%Y")
2end = dt.datetime.strptime("29-02-2020", "%d-%m-%Y")
3date_generated = [start + dt.timedelta(days=x) for x in range(0, (end-start).days+1)]
4
5test_dates = []
6for date in date_generated:
7 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

1my_pets = ['alfred', 'tabitha', 'william', 'arla']
2uppered_pets = list(map(str.upper, my_pets))
3print(uppered_pets)
4# Output : ['ALFRED', 'TABITHA', 'WILLIAM', 'ARLA']

Example 2

1circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
2result = list(map(round, circle_areas, range(1,7)))
3print(result)
4# Output : [3.6, 5.58, 4.009, 56.2424, 9.01344, 32.00013]

Example 3 ( using repeat to have same value repeated for round() )

1circle_areas = [3.56773, 5.57668, 4.00914, 56.24241, 9.01344, 32.00013]
2result = list(map(round, circle_areas, np.repeat(2,6)))
3print(result)
4# 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

1scores = [66, 90, 68, 59, 76, 60, 88, 74, 81, 65]
2def is_A_student(score):
3 return score > 75
4
5over_75 = list(filter(is_A_student, scores))
6print(over_75)
7# Output : [90, 76, 88, 81]

Example 2

1dromes = ("demigod", "rewire", "madam", "freer", "anutforajaroftuna", "kiosk")
2palindromes = list(filter(lambda word: word == word[::-1], dromes))
3print(palindromes)
4# Output : ['madam', 'anutforajaroftuna']

Thanks