Journal Convertir des dates avec month_nb

Posté par  (site Web personnel) . Licence CC By‑SA.
12
14
jan.
2020

month_nb est un petit utilitaire permettant de traduire le nom d'un mois en son nombre, quelle que soit sa langue (fonctionne pour 69 langues actuellement).

>> month_nb('août');
8

J'ai raconté la génèse du projet sur le blog de Meta-Press.es.

La principale motivation vient du fait que rien n'est prévu pour parser des dates internationales en JavaScript (une fois sorti du format ISO et des formats anglais on est livré à soi-même).

En m'inspirant de la traduction en français du livre web Eloquant JavaScript de Marijn Haverbeke, j'ai fini par aboutir à une séparation propre des données et du code :

Sous la surface de la machine, le programme évolue. Sans effort, il prend de l’ampleur et se contracte. Avec beaucoup d’harmonie, les électrons se dispersent et se regroupent. Les formes sur le moniteur ne sont que l’écume de la vague.

Quand les créateurs ont construit la machine, ils y ont mis un processeur et de la mémoire. À partir de là surgissent les deux points de vue sur le programme.

Du côté du processeur, l’élément actif est appelé Contrôle. Du côté de la mémoire, l’élément passif est appelé Données.

-- Les deux points de vue, Le livre de la programmation, EloquentJavascript.net, Marijn Haverbeke

Aujourd'hui le projet est articulé autour d'un arbre d'expression rationnelles qu'il suffit de parcourir en profondeur d'abord pour trouver le numéro d'un nom de mois, quelle que soit sa langue, et sans avoir à connaître cette dernière.

La structure de donnée est réutilisable pour d'autres langages de programmation. J'ai commencé avec une version Python qui a fonctionné directement pour 49 langues (je n'ai pas encore compris ce qui coince pour les autres).

Le programme mériterait probablement aussi d'être empaqueté pour NodeJS, mais en attendant, il est utilisable pour ajouter un nouveau journal à l'index de Meta-Press.es !

Ayant suivi de près le feuilleton Tap-Tempo, je me disais que les 30 lignes de code utiles pour décliner month_nb dans d'autres langages démangeraient peut être certains d'entre vous :-)

  • # Quid des années bissextiles ?

    Posté par  (site Web personnel) . Évalué à -3.

    month_nb('février') == 28.25 ?

    • [^] # Re: Quid des années bissextiles ?

      Posté par  . Évalué à 8.

      Non c'est le numéro de mois (et pas le nombre de jours), donc février c'est 2.

      En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

      • [^] # GNU Date ?

        Posté par  (site Web personnel) . Évalué à 4. Dernière modification le 14/01/20 à 09:38.

        Certes…

        Je me serais attendu à ce que ce qui suit (qui n'aidera pas en javascript) marche par défaut :

        $ LC_ALL=en_GB date +"%m" -d "01 January 1970 00:00:00"
        01
        $ LC_ALL=fr_FR date +"%m" -d "01 décembre 1970 00:00:00"
        date: invalid date '01 d\303\251cembre 1970 00:00:00'
        $ LC_ALL=de_DE date +"%m" -d "01 Dezember 1970 00:00:00"
        date: invalid date '01 Dezember 1970 00:00:00'

        (mais non cf https://www.gnu.org/software/coreutils/manual/coreutils.html#Calendar-date-items )

  • # Perl

    Posté par  . Évalué à 4.

    En perl il y a une bibliothèque très sympa Regex::Assemble qui permet de grouper des expression régulière. Il offre de très bonnes performances. C'était une occasion sympa de m'en servir :)

    J'utilise un json différent du tiens:

    {
    "1":["january", "ажьырныҳәа", "āž̍ərnəh°̍ā", "…"],
    "2": ["february", "жәабран", "ž°ābrān", "…"],
    "3": ["march", "хәажәкыра", "x°āž°ḳərā", "…"],
    "4": ["april", "мшаҧы", "mšāpə", "…"],
    "5": ["may", "лаҵара", "lāc̣ārā", "…"],
    "6": ["june", "рашәара", "rāš°ārā", "…"],
    "7": ["july", "ҧхынгәы", "pxəng°ə", "…"],
    "8": ["august", "нанҳәа", "nānh°̍ā", "…"],
    "9": ["september", "цәыббра", "c°əbbrā", "…"],
    "10": ["october", "жьҭаара", "ž̍tāārā", "…"],
    "11": ["november", "абҵара", "ābc̣ārā", "…"],
    "12": ["december", "ҧхынҷкәын", "pxənč̣̍ḳ°ən", "…"]
    }
    #!/usr/bin/perl
    
    use 5.10.0;
    use JSON;
    use Regexp::Assemble;
    
    my $filename = 'definitions.json';
    my $data;
    
    # lecture du fichier json
    if (open (my $json_str, $filename)) {
        local $/ = undef;
        my $json = JSON->new;
        $data = $json->decode(<$json_str>);
        close($json_stream);
    }
    
    # on construit un tableau d'expressions régulières
    my @months_hash;
    foreach my $month_number (1..12) {
        my $ra = Regexp::Assemble->new;
        foreach my $month (@{$data->{$month_number}}) {
            $ra->add('^'.$month.'$');
        }
        push(@months_hash, $ra);
    }
    
    my $input = 'february';
    
    # on test mois après mois
    foreach my $month_number (0..11) {
        if ($input =~ /($months_hash[$month_number])/i) {
            say 1+$month_number;
            exit;
        }
    }
    • [^] # Re: Perl

      Posté par  . Évalué à 1.

      Bof… c'est un peu overkill non le Regexp::Assemble; pour un truc si simple alors qu'un simple

      $re = '^(?:'.join('|', @{$data->{$month_number}}).')$';
      suffit non ?

      • [^] # Re: Perl

        Posté par  . Évalué à 1.

        Avec 69 langages dont certains ont plusieurs écritures (traditionnels et latines), ça commence à faire. Je trouve pas suffisamment complexe Regexp::Assemble pour ne pas l'utiliser (comme je l'ai dit c'est en plus pour moi une occasion de m'en servir).

        En relisant la doc, j'ai même vu qu'on peut faire encore mieux : il peut t'indiquer la regex qui a matchée. Donc tu peux :

        • assembler par mois pour construire une regex
        • assembler chaque de ses regex
        • faire un match de cette dernière et il t'indiquera la quelle des 12 a matchée

        Ça complexifie encore la construction, mais la correspondance devient complètement triviale et particulièrement efficace.

  • # Toujours du NIH

    Posté par  . Évalué à 4.

    Sans déconner, réinventer le parsing de date ? Je croyais qu'on arrêtait après l'université…

    La structure de donnée est réutilisable pour d'autres langages de programmation. J'ai commencé avec une version Python qui a fonctionné directement pour 49 langues (je n'ai pas encore compris ce qui coince pour les autres).

    Il se trouve qu'il existe déjà un ensemble de traductions, qui est justement utilisé pour faire la traduction dans le sens index -> représentation linguistique. Ça s'appelle des locales, c'est cent fois plus standard que ta structure, et il existe même une fonction POSIX pour ça : strptime(3).

    Ah bien sûr, tu as commencé par du Python sans lire la doc, puis sur ce langage moisi qu'est Javascript, et tu as ensuite amené ta perversion vers le reste de nos beaux systèmes… NIH, quand tu nous tiens.

    Pour l'exemple, en Python :

    import locale
    import datetime
    
    locale.setlocale(locale.LC_ALL,('fr_FR','UTF-8'))
    datetime.datetime.strptime("août","%B").month
    

    Et je n'ai pas compté le nombre de langues dans lequel c'est traduit pour enlarger ma réputation, mais je m'en fous, puisque je n'aime pas passer mon temps à merger des pulls request de choses qui ont déjà été bien mieux faites par d'autres avant moi.

    Au lieu de fumer des trucs pas nets qui te font aimer des discours tarés de gourous du JS, commence par RTFM !

    • [^] # Re: Toujours du NIH

      Posté par  (site Web personnel) . Évalué à 9.

      Pourquoi ce ton agressif et condescendant ?

      Tu te trompes dans ton analyse, j'ai commencé par le JavaScript comme je l'indique explicitement dans mon journal. Tu manque donc d'humilité et insulte à la légère.

      C'est en JavaScript que j'ai un cas concret d'utilisation : Meta-Press.es.

      De plus, la solution que je présente pour ce problème s'affranchit du besoin de connaître la langue du mois à convertir. Ça me semble inédit et digne d'intérêt.

      Est-il mauvais d'explorer ?

      La liberté ne s'use, que si on ne s'en sert pas.

      • [^] # Re: Toujours du NIH

        Posté par  . Évalué à 5.

        la solution que je présente pour ce problème s'affranchit du besoin de connaître la langue du mois à convertir

        Cela implique qu'il n'y a aucun ambiguïté, par exemple si « avril » existe dans plusieurs langues, cela désigne toujours le même mois.
        À vue de nez j'aurais tendance à dire que c'est le cas. Si tu as réussi à faire 69 langues, je suppose que tu confirmes pour ces 69.

        • [^] # Re: Toujours du NIH

          Posté par  (site Web personnel) . Évalué à 2.

          Il y a effectivement des ambiguïtés possibles, je détaille les deux rencontrées pour l'instant ici : https://framagit.org/Siltaar/month_nb#user-content-supported-languages

          • Croatian "listopad" fails to return 10 as it is 11 in Czech and Polish
          • Slovenian "prosinec" fails to return 1, as it is 12 in Croatian and Czech

          Ça fonctionne donc parfaitement pour 67 langues, c'est plus honnêtes de l'indiquer comme ça, merci. (c'était plus amusant avec 69, mais bon…)

          La liberté ne s'use, que si on ne s'en sert pas.

          • [^] # Re: Toujours du NIH

            Posté par  (site Web personnel) . Évalué à 2.

            Mais du coup, comme le but est justement de fonctionner sans avoir à préciser la langue en entrée, est-ce qu'on peut dire que ta lib fonctionne ?

            Est-ce que tu gères les calendriers autres que grégorien ? (par exemple, رمضان devrait renvoyer 9).

            Enfin, tu fonctionnes à coup de regex. Est-ce que ça n'aurait pas été plus simple de fouiller directement dans un arbre ? (tu cherches les lettres en entrée directement les unes après les autres dans un arbre).

            La connaissance libre : https://zestedesavoir.com

            • [^] # Re: Toujours du NIH

              Posté par  . Évalué à 3.

              Enfin, tu fonctionnes à coup de regex. Est-ce que ça n'aurait pas été plus simple de fouiller directement dans un arbre ? (tu cherches les lettres en entrée directement les unes après les autres dans un arbre).

              Un arbre n-aire n étant le nombre de caractères dans UTF8 ? Rien que la gueule de la structure me paraît énorme. Ma version avec un tableau de 12 mois et une expression régulière me paraît nettement plus efficace (l'article que j'ai mis en lien montre les performances en augmentant le nombre de langages pris en compte). Pour la gestion des calendriers autres que gregorien ma solution fonctionne très bien, c'est dans le jeu de données initial que tu choisi que رمضان vaut 9, 12 ou 121. La seule contrainte en l'état c'est que les mois doivent être des entiers positifs et continuent (tu ne peux pas avoir de mois 15 sans avoir un 14ème mois).

              Par contre je n'ai pas fais les tests complets pour voir qu'il y avait des collisions et encore moins de comment les gérer.

              • [^] # Re: Toujours du NIH

                Posté par  . Évalué à 2.

                Un arbre n-aire n étant le nombre de caractères dans UTF8 ? Rien que la gueule de la structure me paraît énorme.

                Je ne sais pas si c'est ce qu'il voulait dire à la base, mais dans ce genre de cas c'est un automate à états finis qu'il convient utiliser.
                C'est par exemple ce que te génère flex et qui (à priori) est utilisé dans les compilateurs pour transformer le texte source en tokens (quelqu'un a un terme français qui va bien là ?) avant de le soumettre à l'analyseur grammatical.

                C'est une des structure/méthode les plus performantes et compacte en mémoire pour ce genre de travail, par contre, c'est très chiant à maintenir, faire évoluer et il est très facile de se planter en y ajoutant de nouveaux mots, c'est pour cela que des outils automatiques (flex, entre autres) existent pour générer du code à partir d'un fichier descriptif plus simple à maintenir pour un humain.

                • [^] # Re: Toujours du NIH

                  Posté par  . Évalué à 1.

                  Et c'est comme ça que sont compilée les expressions régulières. Mais ce qu'il proposait me semblait être un arbre n-aire avec un caractère par niveau ce qui est loin des automates en terme d'optimisation.

                  • [^] # Re: Toujours du NIH

                    Posté par  . Évalué à 2.

                    théoriquement, ça me semble être la même chose.
                    Les deux différences que je vois sont :
                    1) les outils qui génèrent des automates sont capable de les minimiser. Minimisation_d'un_automate_fini_déterministe
                    2) quand on entend le mot "arbre", on (en tout cas moi…) pense à priori à une structure de type chainage de noeuds, alors que ça n'est qu'une façon de l'implémenter, on peut très bien le faire avec une table. C'est d'ailleurs ce que fais flex, il construit la table associée à l'automate (qui est souvent représenté comme un graphe sur papier) qui reconnait les mots requis.

                    à partir de là, si tu implémentes ton arbre n-aire dans une table, tu as le même résultat, sauf que tu le fais à la main au lieu de t'appuyer sur des outils automatiques.

                    • [^] # Re: Toujours du NIH

                      Posté par  . Évalué à 1.

                      1) les outils qui génèrent des automates sont capable de les minimiser.

                      C'est surtout à ça que je pensais. Les compilateurs d'expressions régulière font ça très bien.

            • [^] # Re: Toujours du NIH

              Posté par  (site Web personnel) . Évalué à 4.

              Pour l'instant on peut dire qu'elle fonctionne pour 100% des sources connues de Meta-Press.es :-) et j'ai hâte d'avoir à complexifier le code d'un côté ou de l'autre parce que le besoin sera apparu ! (ça voudra dire que Meta-Press.es rencontre le succès que je lui souhaite)

              Je ne gère qu'une portion de ce qui est listé ici : https://en.wiktionary.org/wiki/Appendix:Months_of_the_year à défaut d'une meilleure base de référence. Mon but premier est de pouvoir appeler new Date() en JavaScript et donc tendre vers le format ISO 8601.

              Y'a sûrement moyen de faire quelque chose de plus rapide en restant sur une comparaison de caractères à la place des regex. Dans l'immédiat, je vois des cas comme celui-là :
              "^.(.[rρր]|[පპ])": 4,

              Que j'étais bien contant de réaliser en regex. Ça fait une structure plus compacte de pouvoir s'étaler aussi en largeur. Mais dans l'absolu on doit toujours pouvoir décomposer les regex.

              J'en ai aussi quelques unes fourre-tout un peu honteuse (pour les langues écrites en lettres latines qui ne présente pas l'arbre habituel) :
              "^(pro|gru|jo|ar|rh|ui)": 12,

              J'ai aussi une première barrière qui a cette tête là :
              "^([\u0590-\u05FF]|[\u0600-\u06FF]|[\u0900-\u097F])"

              Là en s'appuyant sur les regex, le code est simple, et ce niveau ne fait pas de différence par rapport aux autres. C'est là :
              https://framagit.org/Siltaar/month_nb/blob/master/month_nb.json

              Du coup, la partie algorithme est très simple et peut facilement (même si l'utilité n'est pas évidente au premier coup d'œil) se décliner dans d'autres langages de programmation, moi ça m'amuse.

              La liberté ne s'use, que si on ne s'en sert pas.

          • [^] # Re: Toujours du NIH

            Posté par  (site Web personnel) . Évalué à 7.

            Ça fonctionnerait pour 69 langues si tu renvois une liste des couples (mois, langue) qui correspondent. Libre à l'utilisateur de gérer ou non la désambiguïsation à partir du contexte.

            Adhérer à l'April, ça vous tente ?

      • [^] # Re: Toujours du NIH

        Posté par  . Évalué à 2.

        Pourquoi ce ton agressif et condescendant ?

        J'avoue, c'est un peu perso. Voir toutes les habituelles tares du JS plus ton nom, je me suis lancé. C'est con je sais.

        Tu te trompes dans ton analyse, j'ai commencé par le JavaScript comme je l'indique explicitement dans mon journal. Tu manque donc d'humilité et insulte à la légère.

        Effectivement, j'avais pourtant compris ça en premier, puis en rédigeant un peu vite et en mode « clash », j'ai inversé le sens.

        De plus, la solution que je présente pour ce problème s'affranchit du besoin de connaître la langue du mois à convertir. Ça me semble inédit et digne d'intérêt.

        Effectivement, je n'avais pas vu ça. Même si c'est intéressant de poster également les limites après comme tu l'as fait (langues différentes ayant les même mots pour des mois différents).

    • [^] # Re: Toujours du NIH

      Posté par  (site Web personnel) . Évalué à -5. Dernière modification le 14/01/20 à 15:59.

      Tes contributions à LinuxFr sont chouettes, fournies et abondantes. Je n'imaginais pas que tu te permettais d'agir en vrai connard comme ça à 15:34, empressé d'exposer de la mauvaiseté sans prendre le temps de vraiment comprendre. C'est une petite déception et je préférerai que tu t'occupes des hors sujets que tu cultives si bien sinon.

      La liberté ne s'use, que si on ne s'en sert pas.

    • [^] # Re: Toujours du NIH

      Posté par  (site Web personnel) . Évalué à 0.

      Et un tel message violent et erroné prends +6… Personne ne lit vraiment en fait ?

      Même Benoit S. a réagit trop vite :-P

      La liberté ne s'use, que si on ne s'en sert pas.

    • [^] # Re: Toujours du NIH

      Posté par  . Évalué à 10. Dernière modification le 15/01/20 à 07:57.

      Bonjour,

      Benoar, inutile d'être aussi agressif… Tu aurais pu exprimer ce point de vue de manière un peu plus diplomate. C'est toujours très agréable de présenter un petit projet et de se faire baffer sur DLFP. Après on s'étonne qu'il n'y ait pas plus de personnes créant du contenu…

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.