Aujourd.py module de manipulation des dates et des semaines

Tags: #<Tag:0x00007f50a26e7900>

J’ai toujours été frustré par la gestion des dates du module datetime.
Par exemple, la nécessité de toujours préciser l’année, le mois et le jour dans le mois pour obtenir une pauvre date.
Par exemple, si on veut connaître le jour de la Saint Valentin

>>> from datetime import date
>>> st_valentin = date(2019, 2, 14)
>>> st_valentin
datetime.date(2019, 2, 14)
>>> print(st_valentin)
2019-02-14
>>> print(st_valentin.month)
2
>>> 

Avec ce module

>>> import aujourd
>>> from aujourd import *
>>> st_valentin = lejour(14, 2)
>>> st_valentin
date_fr(2019, 2, 14)
>>> print(st_valentin, st_valentin.mois)
jeudi 14 février 2019 2
>>> 

Pour obtenir une sortie str(d) qui ressemble à quelque chose avec datetime.date il faut se plonger dans la documentation de d.strftime et ce n’est pas du gâteau !
Comme la classe date_fr est dérivée de datetime.date j’ai été obligé de conserver l’ordre annee, mois, jour dans le constructeur.
Il y a trois dates prédéfinies :

>>> print("Hier...", hier, aujourd.hui, demain)
Hier... mercredi  2 octobre 2019 jeudi  3 octobre 2019 vendredi  4 octobre 2019
>>> 

des fonctions laveille ou veille et lendemain

>>> premier_mars = lejour(1, 3)
>>> print(veille(premier_mars))
jeudi 28 février 2019

Au lieu de lendemain(d) on peut écrire 'd + unjour' et le surlendemain est 'd + 2*unjour'

>>> print(premier_mars - 15*unjour)
jeudi 14 février 2019
>>> le15 = lejour(15)
>>> print(le15)
mardi 15 octobre 2019

Il y a une classe Semaine avec un constructeur (jour [,mois[, annee]])

>>> semaine_du_14juillet = Semaine(14, 7)
>>> print(semaine_du_14juillet)
lundi  8 juillet 2019 ... dimanche 14 juillet 2019
>>> ssuivante = semaine_du_14juillet + 1
>>> print(ssuivante.vendredi)
vendredi 19 juillet 2019

Pratique pour se promener dans le calendier.
semaine en 8, et semaine en 15

>>> sc = semaine_courante()
>>> sp = sc + 1
>>> sem_en8 = sp + 1
>>> sem_en15 = sp + 2
>>> print(sp, '\n', sem_en8, '\n', sem_en15)
lundi  7 octobre 2019 ... dimanche 13 octobre 2019 
 lundi 14 octobre 2019 ... dimanche 20 octobre 2019 
 lundi 21 octobre 2019 ... dimanche 27 octobre 2019
>>> 
>>> samedi_en8 = sem_en8.samedi
>>> print(samedi_en8)
samedi 19 octobre 2019

On peut iéter sur les jours de la semaine.

Le code

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

r"""
aujourd.py module de manipulation des dates et des semaines.

import aujourd
from aujourd import *
    print("Hier...", hier, aujourd.hui, demain)
    print("avant hier, c'était le", laveille(hier))

    surlendemain = lendemain(demain)
    print("surlendemain", surlendemain)
    print("dans 3 jours", aujourd.hui + 3 * unjour)

    print("le 15 aout", lejour(15, 8))
    
    st_valentin2020 = lejour(14, 2, 2020)
    print("Saint Valentin", st_valentin2020)

    d = sainte_catherine = lejour(25, 11)
    print(d.jour, d.mois)

    sc = semaine_courante()
    print("la semaine courante", "numéro", sc.numero)
    print(sc.tableau())

    sp = sc + 1
    print("vendredi de la semaine prochaine", sp.vendredi, '\n')

    print("\net la nuit du 4 août, c'était quel jour ?")
    s4aout = Semaine(4, 8, 1789)
    print(s4aout.tableau(), '\n')

    noel = lejour(25, 12)
    semaine_de_noel = Semaine.fromdate(noel)
    print("noel", noel, "semaine", semaine_de_noel.numero)
    print(semaine_de_noel)

"""

from __future__ import print_function

__all__ = ( 'date_fr', 'Semaine', 'hui', 'hier', 'demain', 'date_enclair',
        'unjour', 'lejour', 'laveille', 'veille', 'lendemain',
         'semaine_courante', 'semaine_suivante', 'timedelta', 'Aujour' )


import datetime
from datetime import date, timedelta
import locale

locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())

class date_fr(date):
    """une classe date qui tient compte de la locale
    """
    def __str__(self):
        s = date_enclair(self)
        return s

    def __repr__(self):
        """
        à implémenter pour ne pas tomber sur date.__str__ par défaut
        """
        s = super().__repr__()
        return s

    @property
    def jour(self):
        return self.day

    @property
    def mois(self):
        return self.month

    @property
    def annee(self):
        return self.year

    def __add__(self, other):
        """ date + delta """
        d = super().__add__(other)
        d = self.__class__(d.year, d.month, d.day)
        return d

    def __radd__(self, other):
        """  delta + date -> date """
        d = super().__radd__(other)
        d = self.__class__(d.year, d.month, d.day)
        return d

    def __sub__(self, other):
        """
        date2 - date1  -> deelta
        date - delta -> date
        """
        d = super().__sub__(other)
        if not isinstance(d, timedelta):
            d = self.__class__(d.year, d.month, d.day)
        return d



hui = date_fr.today()

unjour = date.resolution # timedelta(1)  ou date_fr.resolution

def veille(quand=None):
    """retourne la date de la veille
    """
    if quand is None: quand = hui
    return quand - unjour

laveille = veille

def lendemain(quand=None):
    """retourne la date du lendemain
    """
    if quand is None: quand = hui
    return quand + unjour

hier = laveille()
demain = hui + unjour

def date_enclair(d):
    """retourne la date d en clair, en tenat compte de la locale
    """
    fmt = "%A %e %B %Y"
    s = d.strftime(fmt)
    return s

def lejour(jour, mois=None, annee=None):
    """retourne la date demandée, mois courant et année courante par défaut
    le14juillet = lejour(14, 7)
    sainte_catherine = lejour(25, 11)
    saint_sylvestre = lejour(31, 12)
    """
    if mois is None: mois = hui.month
    if annee is None: annee = hui.year
    t_ymd = annee, mois, jour
    return date_fr(*t_ymd)

def Aujour(quand):
    """Seule l'expression "Aujour( aujourd.hui )" est possible
    """
    if quand is not hui:
        raise SyntaxError("the only valid expression is 'Aujour(aujourd.hui)'")
    return quand

class Semaine(object):
    """
    Semaine(jour, mois, annee) -> la semaine qui contient `jour` dans le mois
    s14juillet = Semaine(14, 7) pour l'année en cours
    bastille = Semaine(14, 7, 1789)
    print(bastille.tableau())
    for jour in bastille:
        print(jour)
    """
    def __init__(self, jour=None, mois=None, annee=None):
        """Semaine( 8 ) -> semaine qui contient le 8 du mois courant
        bastille = Semaine(14, 7, 1789)
        """
        if jour is None: jour = hui.day
        d = lejour(jour, mois, annee)
        idx = d.isoweekday()
        if idx == 1:
            self.lundi = d
        else:
            d = d - timedelta(idx - 1)
            self.lundi = date_fr(d.year, d.month, d.day)

    @classmethod
    def fromdate(cls, d):
        """semaine qui contient la date d"""
        return cls(d.day, d.month, d.year)

    @property
    def mardi(self):
        return self.lundi + unjour

    @property
    def mercredi(self):
        return self.lundi + timedelta(2)

    @property
    def jeudi(self):
        return self.lundi + timedelta(3)

    @property
    def vendredi(self):
        return self.lundi + timedelta(4)

    @property
    def samedi(self):
        return self.lundi + timedelta(5)

    @property
    def dimanche(self):
        """dernier jour de la semaine"""
        return self.lundi + timedelta(6)

    @property
    def numero(self):
        """numéro de la semaine entre 1 et 52"""
        return self.lundi.isocalendar()[1]

    @property
    def du_au(self):
        """les dates (lundi, dimanche)
        """
        return (self.lundi, self.dimanche)

    def __str__(self):
        s = " ... ".join( map(str, self.du_au))
        return s

    def __repr__(self):
        """  Semaine(jour, mois, annee) """
        d = self.lundi
        s = "%s(%s, %s, %s)" % (self.__class__.__name__, d.day, d.month, d.year)
        return s

#  __iter__  __next__ ?  automatique avec un générateur
    def __iter__(self):
        """iteration protocol support
        a generator function 
        """
        yield self.lundi
        for i in range(1, 7):
            yield self.lundi + timedelta(i)

    def tableau(self):
        """tableau une ligne pour chaque jourde la semaine  
        """
        s = '\n'.join(map(str, iter(self)))
        return s

    def __eq__(self, other):
        return self.lundi == other.lundi

    def __ne__(self, other):
        return self.lundi != other.lundi

    def __lt__(self, other):
        return self.lundi < other.lundi

    def __te__(self, other):
        return self.lundi <= other.lundi

    def __gt__(self, other):
        return self.lundi > other.lundi

    def __ge__(self, other):
        return self.lundi >= other.lundi

    def __add__(self, n):
        """avancer de n semaines"""
        d = self.lundi + timedelta(7*n)
        return Semaine.fromdate(d)

    def __sub__(self, n):
        """reculer de n semaines"""
        d = self.lundi - timedelta(7*n)
        return Semaine.fromdate(d)

def semaine_courante():
    """la semine courante"""
    return Semaine.fromdate(hui)
    # return Semaine(hui.day)

def semaine_suivante(sc=None):
    """La semaine qui suit sc, semaine_courante par défaut
    on peut aussi écrire semaine + 1
    """
    if sc is None: sc = semaine_courante()
    d = sc.lundi + timedelta(7)
    return Semaine.fromdate(d)


if __name__ == '__main__':
    print(__doc__)
    print("Hier...", hier, hui, demain)
    print("avant hier, c'était le", laveille(hier), '\n')

    surlendemain = lendemain(demain)
    print("surlendemain", surlendemain)
    print("dans 3 jours", hui + 3 * unjour, '\n')

    print("le 15 aout", lejour(15, 8))

    st_valentin2020 = lejour(14, 2, 2020)
    print("Saint Valentin", st_valentin2020)

    d = sainte_catherine = lejour(25, 11)
    print(d.jour, d.mois)

    sc = semaine_courante()
    print("la semaine courante", "numéro", sc.numero)
    print(sc.tableau(),'\n')

    sp = sc + 1
    print("vendredi de la semaine prochaine", sp.vendredi, '\n')

    print("\net la nuit du 4 août, c'était quel jour ?")
    s4aout = Semaine(4, 8, 1789)
    print(s4aout.tableau(), '\n')

    noel = lejour(25, 12)
    semaine_de_noel = Semaine.fromdate(noel)
    print("noel", noel, "semaine", semaine_de_noel.numero)
    print(semaine_de_noel)

    st_sylvestre = lejour(31, 12)
    semx = Semaine.fromdate(st_sylvestre)
    print("semaine de la Saint Sylvestre")
    print(semx, "numero", semx.numero)



# vim: set shiftwidth=4 softtabstop=4 expandtab tabstop=4:

En espérant ne pas avoir fait d’erreur de copier/coller.

Cordialement,
Regards,
Mit freundlichen Grüßen,
مع تحياتي الخالصة


F. Petitjean
Ingénieur civil du Génie Maritime.

« On ne perd pas son temps en aiguisant ses outils. »
Proverbe français

« Moi, lorsque je n’ai rien à dire, je veux qu’on le sache. » (R. Devos)

1 J'aime

Salut

Un truc me turlupine:

Ton #! en en-tête indique que ton script est en python3.
Du coup, je me demande l’utilité d’importer print_function de future, étant donné qu’en python3 print est déjà la fonction renvoyée par print_function.
A priori, ça ne serait impactant que pour lancer ton script en python2. Donc je suppose que c’est juste là pour une portabilité python2 (qui va devoir disposer de la lib future). Me trompe-je ?

Pas vraiment.
Vous remarquerez plusieurs choses à propos de cette instruction mystérieuse :

  • Ele doit être au début du module, mais après le “docstring” __doc__
  • l’objet print_function est de type Feature dans un module __future__
`>>> type(print_function)
<class '__future__._Feature'>
>>> help(print_function)

>>> print_function.getOptionalRelease()
(2, 6, 0, 'alpha', 2)
>>> print_function.getMandatoryRelease()
(3, 0, 0, 'alpha', 0)
>>> 

Donc, vous avez bien raison dans le cadre d’un module Python3, ce n’est pas nécessaire, d’autant plus que si on tente un

python2 aujourd.py 

on lève une belle exception

TypeError: super() takes at least 1 argument (0 given)

Pourquoi donc, ai-je mis cette instruction sibylline ?

  • parce qu’elle est jolie et excite la curiosité des lecteurs
  • parce qu’elle introduit un chemin de compatibilité entre python2 et python3
  • parce que j’ai appris les règles de placement dans le code

Et pourquoi donc cela ne fonctionne pas avec python2 ?
Parce que j’ai utilisé une syntaxe pour super() qui est propre à python3.

Vous comprendrez aisément que je me suis basé sur la documentation de Python3 et que pour ces histoires d’héritage et de super sans plomb, j’étais bien content d’avoir un truc qui marche.
Relisez le Zen de Python

import this

et on va dire que j’ai suivi plutôt ces aphorismes là

In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

yours
litthejon75-qui-fait-son-Michel-Chevalet-import-future-comment-ça-marche’ly


F. Petitjean
Ingénieur civil du Génie Maritime.

« Moi, lorsque je n’ai rien à dire, je veux qu’on le sache. » (R. Devos)

« Celui qui, parti de rien, n’est arrivé nulle part n’a de merci à dire à personne !! »
Pierre Dac