Python: lavorare con le date
In questo articolo vedremo un esempio pratico di operazioni sulle date in Python.
Realizzeremo una semplice applicazione da riga di comando che riceverà in input la data di nascita dell'utente e restituirà il nome del suo segno zodiacale.
I segni zodiacali sono dodici e ciascuno di loro copre un determinato arco temporale nel corso dell'anno. Per rappresentarli useremo un file JSON contenente un array di oggetti così strutturati:
[
{
"name": "Nome Segno",
"date": {
"from": [giorno, mese],
"to": [giorno, mese]
}
}
]
giorno e mese sono numeri interi. Una caratteristica del costruttore della classe date di Python è appunto quella di accettare solo numeri interi per dati come l'anno, il mese e il giorno. from rappresenta la data di inizio del periodo di un segno e to il suo termine. L'anno è ininfluente.
Dobbiamo accettare solo date in formato ISO, ossia YYYY-mm-dd e verificare che anno, mese e giorno siano validi. A tale scopo definiamo come prima cosa una funzione di validazione.
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
Il primo step è verificare il formato della data usando una semplice espressione regolare. Usiamo search() per effettuare un confronto sull'intera stringa di input. A questo punto se il formato è valido, corroboriamo la nostra routine ottenendo un oggetto di tipo date a partire dal formato ISO fornito usando il metodo fromisoformat(). Inoltre procediamo a verificare che anno, mese e giorno ricadano in un intervallo valido.
Ora possiamo definire una funzione per ottenere la lista di dizionari dal file JSON.
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
Sappiamo che i dati rilevanti sono il giorno e il mese della data di nascita dell'utente e che tali dati vanno confrontati con i valori contenuti nelle liste from e to di ciascun dizionario. Dobbiamo quindi operare un confronto tra tre oggetti date partendo dalla trasformazione della stringa nel formato YYYY-mm-dd della data di nascita in un oggetto data di Python. Quest'operazione può essere effettuata tramite il metodo datetime.strptime() che accetta come primo argomento la stringa data e come secondo argomento il formato atteso della data di input.
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
Se la data di nascita ricade nel range stabilito, ossia se è maggiore o uguale della data iniziale del periodo e se al contempo è minore o uguale della data finale del periodo, restituiamo il nome del segno zodiacale dell'utente. Ricordiamo ancora che l'anno è ininfluente ma è necessario specificarlo per creare un oggetto date di Python.
Possiamo usare il nostro codice nel seguente modo:
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()