This repository has been archived on 2019-08-09. You can view files and clone it, but cannot push or open issues or pull requests.
s1-tp/S1/TP 3/date.py
2014-10-07 19:32:31 +02:00

419 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PREUD'HOMME BONTOUX Geoffrey - PeiP 12 - 2014/2015
# TP n°3 donné le 26/09/2014 - Autour du calendrier
# http://www.fil.univ-lille1.fr/~wegrzyno/portail/Info/Doc/HTML/tp_conditionnelle.html
# [Q] Programmez une fonction nommée est_divisible_par
def est_divisible_par(a, b):
"""
Indique si a est divisible par b.
CU : a entier, b entier
Exemple :
>>> est_divisible_par(2016, 4)
True
"""
# Si a est divisible par b, alors le reste de la division Euclidienne doit
# être nul
return a % b == 0
# [Tests]
# >>> est_divisible_par(1337, 7)
# True
# >>> est_divisible_par(997, 3)
# False
# [Q] Programmez une fonction nommée est_bissextile
def est_bissextile(annee):
"""
Indique si l'année a donnée est bissextile.
CU : annee entier > 1582
Exemple :
>>> est_bissextile(2016)
True
"""
# Transcription des données du sujet du TP en langage Python
# L'ordre des opérations [(A⋀B)C] ou [A⋀(BC)] est indifférent ici
return est_divisible_par(annee, 4) and \
(not est_divisible_par(annee, 100) or est_divisible_par(annee, 400))
# [Tests]
# >>> est_bissextile(2014)
# False
# >>> est_bissextile(2016)
# True
# >>> est_bissextile(2100)
# False
# >>> est_bissextile(2000)
# True
# [Q] Programmez une fonction nommée nbre_jours
def nbre_jours(m, a):
"""
Renvoie le nombre de jours contenus dans un mois m dune année a donné.
CU : m entier, a entier, 1 ≥ m ≥ 12, a > 1582
Exemple :
>>> nbre_jours(5, 1968)
31
"""
# [Q] Complétez votre fonction nbre_jours avec assert()
assert(type(m) is int and m in range(1, 13)), \
"Le mois doit être un entier compris entre 1 et 12 inclus."
assert(type(a) is int and a > 1582), \
"L'année doit être un entier supérieur à 1582."
# [/Q]
if m in [4, 6, 9, 11]: # Si le mois fait partie de ceux à 30 jours
return 30
elif m == 2: # Sinon, si le mois est février
if est_bissextile(a): # Si l'année est bissextile
return 29
else:
return 28
else: # Si les conditions ci-dessus ne sont pas vérifiées, c'est forcément
# un mois de 31 jours
return 31
# [Q] Testez votre fonction pour tous les mois de lannée 2014 et le mois
# de février 2016
test = "Test nbre_jours() : " # On crée une variable pour imprimer
# une seule ligne
for test_mois in range(1, 13): # On teste pour tous les mois de 2014
test += str(nbre_jours(test_mois, 2014)) + ", "
test += str(nbre_jours(2, 2016))
print(test) # Enfin, on affiche
# Test nbre_jours() : 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29
# [Q] Que donne votre fonction avec un mois m=-1 ? Est-ce normal ?
# >>> nbre_jours(-1, 2014)
# 31
# On constate que la fonction retourne une valeur qui aurait pu être associée
# à certains mois valides. Le fait que la fonction retourne une valeur et ne
# déclenche pas une erreur d'elle-même est normal, car à aucun moment la
# validité des données (telles que définies dans les CU) passées en paramètres
# n'est vérifiée. Il aurait d'ailleurs été tout à fait possible que la fonction
# déclenche une erreur à cause d'une sous-fonction/procédure ne supportant pas
# qu'on lui passe certaines données interdites.
# La valeur retournée est 31, car la fonction est constituée d'une série
# d'instructions conditionelles. Dès lors qu'aucune des conditions n'est
# vérifiée, (m=-1 n'appartient pas aux mois à 30 jours ni au mois de février),
# il sera executé l'instruction "par défaut" du else, c'est à dire retourner la
# valeur 31.
# [Q] Quelle condition dutilisation faut-il envisager normalement pour
# cette fonction ?
# Il faut vérifier les valeurs passées en argument, afin de produire une erreur
# si ces dernières ne sont conformes au CU.
# De cette manière, le script s'arrêtera, et affichera un texte d'erreur
# décrivant le problème. L'avantage de faire cela au lieu de ne rien
# vérifier, est que le problème sera tout de suite montré au développeur
# utilisant la fonction, de cette manière cette dernière ne continuera pas en
# renvoiant une valeur fausse, qui faussera la partie du programme se basant
# sur celle-ci. Cette pratique permet donc d'économiser du temps de débogage.
# [Q] Quelle condition dutilisation faut-il envisager normalement pour
# cette fonction ?
# Il faut vérifier que le mois et l'année soient entier, que le mois soit
# valide, c'est à dire compris entre 1 et 12 inclus, et que la date
# soit supérieure au 15 octobre 1582 (nous arrondirerons à l'année, bien qu'il
# soit possible de vérifier si la date fournie est supérieure à la date de
# début du calendrier grégorien au jours près, il n'est pas nécessaire de
# rajouter de tels calculs en supplément pour un cas de figure qui ne sera
# probablement jamais rencontré dans le cadre de ce TP).
# [Q] Tapez les instructions.
# >>> a = 1
# >>> assert(a != 0), "a doit être différent de 0"
# Aucun message ne s'est affiché, ni aucune erreur ne s'est produite.
# a est bien différent de 0 comme le veut la chaîne de caractère, et cette
# condition est vérifiée à l'interieur de l'instruciton assert()
# >>> a = 0
# >>> assert (a != 0), "a doit être différent de 0"
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AssertionError: a doit être différent de 0
# Un message d'erreur ayant pour message "AssertionError: a doit être différent
# de 0" pour est affiché. a n'est pas différent de 0, la condition dans
# l'instruction assert() n'est pas vérifiée.
# Nous pouvons en déduire que assert() vérifie que le test qui lui est
# donné est vrai (c'est donc un booléen), et sinon produit une erreur
# avec pour message le texte qui lui est apposé. Cette instruction pourra être
# utilisée pour vérifier les CU d'une fonction/procédure.
# [Q] Complétez votre fonction nbre_jours avec cette instruction
# Voir nbre_jours(), le code ajouté pour cette question a été encadré.
# Une assertion pour l'année a été ajoutée pour garder une cohérence
# avec les CU.
# [Q] Trouvez différents cas de non validité dun triplet dentiers pour
# représenter une date.
# Une date peut être invalide si son numéro de jour est inférieure strictement
# à 1 ou supérieur strictement au nombre de jours que peut comporter ce mois,
# ou bien si son numéro de mois est inférieur strictement à 1 ou supérieur
# strictement à 12. On considèrera comme invalide une date inférieure à 1582,
# car il est préférable de renvoyer une erreur indiquant que le programme ne
# gère pas les dates hors du calendrier grégorien plutôt que de renvoyer une
# valeur faussée, ce qui pourrait mener à d'éventuelles complications.
# Exemples : (-14, 7, 1789), (29, 2, 1997), (31, 6, 2014), (28, 120, 1969)
# [Q] Programmez une fonction nommée est_date_valide
def est_date_valide(j, m, a):
"""
Indique si la date donnée appartient au calendrier grégorien.
CU : -aucune-
Exemple :
>>> est_date_valide(25, 4, 1983)
True
"""
# Il n'est pas nécessaire de fournir de CU donc ne pas écrire d'instruction
# assert() car c'est le but même de la fonction.
# On vérifie les types en premier pour éviter de faire fonctionner les
# opérateurs sur d'éventuels types ne les supportant pas.
# La vérification du jour, donc l'appel de nbre_jours() est fait en dernier
# car cette dernière fonction nécessite que le mois et l'année soient
# valides.
return type(j) is int and type(m) is int and type(a) is int and a > 1582 \
and m in range(1, 13) and j in range(1, nbre_jours(m, a)+1)
# [Tests]
# >>> est_date_valide(25, 4, 1983)
# True
# >>> est_date_valide(-14, 7, 1789)
# False
# >>> est_date_valide(29, 2, 1997)
# False
# >>> est_date_valide(31, 6, 2014)
# False
# >>> est_date_valide(28, 120, 1969)
# False
# [Q] Le 15/09/2014 est un lundi. Donc, pour cette date, le numéro à
# calculer doit être 1. Vérifiez ce point.
# Un rapide et simple calcul à l'aide du prompt de Python nous permet de le
# vérifier. On est alors sûr de la syntaxe à utiliser pour num_jour().
# >>> (2014%100//4 + 2014//100//4 + 2014%100 + 2 + 15 + 2 + 5 * 2014//100) % 7
# 1
# [Q] Programmez une fonction nommée corrige_mois
def corrige_mois(m, a):
"""
Renvoie la valeur corrigée du mois selon lalgorithme de Delambre.
CU : m entier, a entier, 1 ≤ m ≤ 12, a > 1582
Exemple :
>>> corrige_mois(2, 2016)
6
"""
assert(type(a) is int and a > 1582), \
"L'année doit être un entier supérieur à 1582"
assert(type(m) is int and m in range(1, 13)), \
"Le mois doit être un entier compris entre 1 et 12 inclus"
# On crée un tableau de valeurs pour un accès facile aux données
liste_corrections_mois = [4, 0, 0, 3, 5, 1, 3, 6, 2, 4, 0, 2]
# Si l'année est bissextile, on y modifie uniquement les valeurs nécessaires
if est_bissextile(a):
liste_corrections_mois[0] = 3
liste_corrections_mois[1] = 6
# On prend dans le tableau la valeur qu'il faut
# On enlève 1 à l'index du tableau, car les mois comment à 1
# alors que le tableau commence lui à 0
return liste_corrections_mois[m-1]
# [Tests]
# >>> corrige_mois(2, 2016)
# 6
# >>> corrige_mois(9, 2014)
# 2
# [Q] Programmez une fonction nommée num_jour, paramétrée par trois
# entiers représentant une date supposée valide, qui renvoie le numéro du jour
# dans la semaine correspondant à cette date.
def num_jour(j, m, a):
"""
Renvoie le numéro du jour dans la semaine correspondant à la date donnée.
CU : Date valide (selon est_date_valide())
Exemple :
>>> num_jour(9, 11, 2004)
2
"""
# On peut utiliser le prélicat est_date_valide() précédemment écrite
# ce qui nous permet de nous libérer d'instructions assert() et de CU
assert(est_date_valide(j, m, a)), "La date n'est pas valide"
# On stocke les valeurs utilisées plusieurs fois dans des variables pour
# éviter de refaire deux fois le même calcul
ab = a//100
cd = a%100
return (cd//4 + ab//4 + cd + corrige_mois(m, a) + j + 2 + 5 * ab) % 7
# [Tests]
# >>> num_jour(9, 11, 2004)
# 2
# >>> num_jour(24, 10, 1929)
# 4
# [Q] Programmez une fonction nommée nom_jour
def nom_jour(j, m, a):
"""
Renvoie le nom du jour dans la semaine correspondant à la date donnée.
CU : Date valide (selon est_date_valide())
Exemple :
>>> nom_jour(24, 10, 1929)
"Jeudi"
"""
assert(est_date_valide(j, m, a)), "La date n'est pas valide"
num = num_jour(j, m, a)
# Au cas où la fonction num_jour() viendrait à dysfonctionner
assert(num in range(7)), "Jour de la semaine inconnu"
# On stocke les jours dans un tableau pour un accès facilité
liste_jours_semaine = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", \
"Vendredi", "Samedi"]
return liste_jours_semaine[num]
# [Tests]
# >>> nom_jour(24, 10, 1929)
# 'Jeudi'
# >>> nom_jour(9, 11, 2004)
# 'Mardi'
# [Q] Programmez une procédure imprimer_mois
def imprimer_mois(m, a):
"""
Imprime le calendrier du mois de l'année donné.
CU : m entier, a entier, 1 ≤ m ≤ 12, a > 1582
Exemple :
>>> imprimer_mois(9, 2014)
Septembre 2014
di lu ma me je ve sa
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
"""
assert(type(a) is int and a > 1582), \
"L'année doit être un entier supérieur à 1582"
assert(type(m) is int and m in range(1, 13)), \
"Le mois doit être un entier compris entre 1 et 12 inclus"
# Affichage mois et année
# On stocke les mois dans un tableau pour un accès facilité
liste_mois = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", \
"Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"]
# On enlève 1 à l'index du tableau, car les mois comment à 1
# alors que le tableau commence lui à 0
# On stocke dans une variable le titre brut (sans alignement / espacement)
titre = liste_mois[m-1] + " " + str(a)
# On affiche le titre centré en répètant sur la droite un certain nombre
# de caractères d'espacements. Ce dernier est calculé en enlevant la
# longueur du titre àl a longueur maximale de la ligne et en divisant le
# tout par deux
print(" " * ((20-len(titre))//2) + titre)
# Affichage nom des jours de la semaine
print("di lu ma me je ve sa")
# Affichage des jours
jour = 1 # Le jour à écrire (s'incrémente)
jour_semaine_1er = num_jour(jour, m, a) # Premier jour de la semaine
nbre_jours_mois = nbre_jours(m, a) # Nombre de jours du mois
# Boucle sur les lignes.
# Ne s'arrête pas tant que le jour final a été atteint
while jour <= nbre_jours_mois:
# La variable ligne stockera la ligne avant de l'imprimer
ligne = "" # On l'initialise / l'efface
# Boucle sur les jours de la semaine.
for jour_semaine in range(7):
# Si on en est au premier jour (donc sur la première ligne) mais que
# le premier jour n'a pas été dépassé, on ajoute du "vide"
if jour == 1 and jour_semaine < jour_semaine_1er:
ligne += " "
# Sinon, et si on a pas dépassé le nombre de jours
elif jour <= nbre_jours_mois:
# On ajoute un caractère d'espacement si le nombre ne possède
# qu'un chiffre pour l'aligner à droite
if jour < 10:
ligne += " "
# On ajoute le nombre du jour
ligne += str(jour)
# On incrémente jour pour passer au prochain jour
jour += 1
# Si on est entre deux jours, on ajoute un caractère d'espacement
if jour_semaine < 6 and jour <= nbre_jours_mois:
ligne += " "
print(ligne) # On affiche la ligne
# [Tests]
# >>> imprimer_mois(9, 2014)
# Septembre 2014
# di lu ma me je ve sa
# 1 2 3 4 5 6
# 7 8 9 10 11 12 13
# 14 15 16 17 18 19 20
# 21 22 23 24 25 26 27
# 28 29 30
# >>> imprimer_mois(5, 1968)
# Mai 1968
# di lu ma me je ve sa
# 1 2 3 4
# 5 6 7 8 9 10 11
# 12 13 14 15 16 17 18
# 19 20 21 22 23 24 25
# 26 27 28 29 30 31
# [Q] Écrivez une instruction utilisant la procédure imprimer_mois pour
# imprimer tous les mois de cette année
# Boucle sur les mois
for mois in range(1, 13):
imprimer_mois(mois, 2014)
# Si on est entre deux mois, on ajoute une ligne d'espacement
if mois < 12:
print("")