In this article we will see a practical example of operations on dates in Python.
We will create a simple command-line application that will take the user's date of birth as input and return the name of his zodiac sign.
There are twelve zodiac signs and each of them covers a specific period of time during the year. To represent them we will use a JSON file containing an array of objects structured as follows:
[
{
"name": "Sign Name",
"date": {
"from": [day, month],
"to": [day, month]
}
}
]
day
and month
are integers. A feature of the constructor of the Python date
class is precisely that of accepting only integers for data such as the year, month and day. from
represents the start date of the period of a sign and to
its end. The year is irrelevant.
We must only accept dates in ISO format, i.e. YYYY-mm-dd
and verify that the year, month and day are valid. To do this, we first define a validation function.
import json
import re
import sys
from datetime import date, datetime
def is_valid_date_str(date_str):
reg = re.compile(r'^\d{4}-\d{2}-\d{2}$')
match = reg.search(date_str)
if not match:
return False
try:
dt = date.fromisoformat(date_str)
year = dt.year
now = datetime.now()
if year >= now.year:
return False
month = dt.month
if month > 12 or month < 1:
return False
day = dt.day
if day > 31 or day < 1:
return False
except ValueError:
return False
return True
The first step is to check the format of the date using a simple regular expression. We use search()
to perform a match on the entire input string. At this point, if the format is valid, we corroborate our routine by obtaining an object of type date
starting from the ISO format provided using the method fromisoformat()
. We also proceed to verify that the year, month and day fall within a valid range.
Now we can define a function to get the list of dictionaries from the JSON file.
def get_signs_list(filename=None):
signs = []
if filename is None:
return signs
with open(filename, 'r') as f:
signs = json.load(f)
return signs
We know that the relevant data are the day and month of the user's date of birth and that these data must be compared with the values contained in the from
and to
lists of each dictionary . We therefore have to compare three date
objects starting from the transformation of the string in the format YYYY-mm-dd
of the date of birth into a Python date object. This operation can be performed using the datetime.strptime()
method which accepts the date string as the first argument and the expected format of the input date as the second argument.
def find_sign_by_birth_date(birthdate=None):
if birthdate is None:
return None
if not is_valid_date_str(birthdate):
return None
signs_list = get_signs_list('./zodiac-signs.json')
format_date = '%Y-%m-%d'
dt = datetime.strptime(birthdate, format_date)
now = datetime.now()
day = dt.day
month = dt.month
reference_date = date(now.year, month, day)
sign_name = ''
for sign in signs_list:
from_day, from_month = sign['date']['from']
to_day, to_month = sign['date']['to']
from_date = date(now.year, from_month, from_day)
to_date = date(now.year, to_month, to_day)
if reference_date >= from_date and reference_date <= to_date:
sign_name = sign['name']
break
return sign_name
If the date of birth falls within the established range, i.e. if it is greater than or equal to the starting date of the period and if at the same time it is less than or equal to the end date of the period, we return the name of the user's zodiac sign. We remind again that the year is irrelevant but it is necessary to specify it to create a Python date
object.
We can use our code as follows:
def main():
birth_date = input('Insert your birth date (YYYY-mm-dd)')
sign_found = find_sign_by_birth_date(birth_date)
if sign_found is None:
print('Invalid birth date')
sys.exit(1)
print(f'Your sign is: {sign_found}')
sys.exit(0)
if __name__ == '__main__':
main()