Calculer les plages de dates et d’heures dans Groovy


  • FrançaisFrançais


  • De temps en temps, j’ai besoin de faire des calculs liés aux dates. Il y a quelques jours, un collègue m’a demandé de mettre en place une nouvelle définition de projet dans notre système de gestion de projet (open source, bien sûr !). Ce projet doit commencer le 1er août et se terminer le 31 décembre. Le service à fournir est budgétisé à 10 heures par semaine.

    Donc, oui, je devais déterminer combien de semaines entre le 2021-08-01 et le 2021-12-31 inclus.

    C’est le genre de problème parfait à résoudre avec un tout petit Sensationnel scénario.

    Installer Groovy sur Linux

    Groovy est basé sur Java, il nécessite donc une installation Java. Une version récente et décente de Java et de Groovy peut se trouver dans les référentiels de votre distribution Linux. Alternativement, vous pouvez installer Groovy en suivant les instructions sur le groovy-lang.org.

    Une bonne alternative pour les utilisateurs de Linux est SDKMan, qui peut être utilisé pour obtenir plusieurs versions de Java, Groovy et de nombreux autres outils connexes. Pour cet article, j’utilise la version OpenJDK11 de ma distribution et la dernière version Groovy de SDKMan.

    Résoudre le problème avec Groovy

    Depuis Java 8, les calculs d’heure et de date ont été regroupés dans un nouveau package appelé java.time, et Groovy y donne accès. Voici le script :

    import java.time.*

    import java.time.temporal.*

    def start = LocalDate.parse('2021-08-01','yyyy-MM-dd')

    def end = LocalDate.parse('2022-01-01','yyyy-MM-dd')

    println "${ChronoUnit.WEEKS.between(start,end)} weeks between $start and $end"

    Copiez ce code dans un fichier appelé wb.groovy et exécutez-le sur la ligne de commande pour voir les résultats :

    $ groovy wb.groovy
    21 weeks between 2021-08-01 and 2022-01-01

    Passons en revue ce qui se passe.

    Date et l’heure

    Les java.time.LocalDate classer fournit de nombreuses méthodes statiques utiles (comme analyser () montré ci-dessus, qui nous permet de convertir d’une chaîne en un DateLocale exemple selon un modèle, dans ce cas, ‘aaaa-MM-jj’). Les caractères de format sont expliqués dans un certain nombre d’endroits – par exemple, la documentation pour java.time.format.DateTimeFormat. Remarquerez que M représente « mois », pas m, qui représente “minute”. Ce modèle définit donc une date formatée sous la forme d’une année à quatre chiffres, suivie d’un trait d’union, suivi d’un numéro de mois à deux chiffres (1-12), suivi d’un autre trait d’union, suivi d’un numéro de jour du mois à deux chiffres (1-31).

    Notez également qu’en Java, analyser () nécessite une instance de DateHeureFormat:

    parse(CharSequence text, DateTimeFormatter formatter)

    En conséquence, l’analyse syntaxique devient une opération en deux étapes, alors que Groovy fournit une version supplémentaire de analyser () qui accepte la chaîne de format directement à la place du DateHeureFormat exemple.

    Les java.time.temporal.ChronoUnit classer, en fait un Énumération, fournit plusieurs Constantes d’énumération, Comme SEMAINES (ou JOURS, ou DES SIÈCLES…) qui à leur tour fournissent le entre() méthode qui nous permet de calculer l’intervalle de ces unités entre deux DatesLocal (ou d’autres types de données de date ou d’heure similaires). Notez que j’ai utilisé le 1er janvier 2022, comme valeur pour finir; Ceci est dû au fait entre() couvre la période de temps commençant à la première date donnée jusqu’à mais n’incluant pas la deuxième date donnée.

    Plus d’arithmétique de date

    De temps en temps, j’ai besoin de savoir combien de jours ouvrés sont dans un laps de temps spécifique (comme, disons, un mois). Ce script pratique le calculera pour moi :

    import java.time.*

    def holidaySet = [LocalDate.parse('2021-01-01'), LocalDate.parse('2021-04-02'),
        LocalDate.parse('2021-04-03'), LocalDate.parse('2021-05-01'),
        LocalDate.parse('2021-05-15'), LocalDate.parse('2021-05-16'),
        LocalDate.parse('2021-05-21'), LocalDate.parse('2021-06-13'),
        LocalDate.parse('2021-06-21'), LocalDate.parse('2021-06-28'),
        LocalDate.parse('2021-06-16'), LocalDate.parse('2021-06-18'),
        LocalDate.parse('2021-08-15'), LocalDate.parse('2021-09-17'),
        LocalDate.parse('2021-09-18'), LocalDate.parse('2021-09-19'),
        LocalDate.parse('2021-10-11'), LocalDate.parse('2021-10-31'),
        LocalDate.parse('2021-11-01'), LocalDate.parse('2021-11-21'),
        LocalDate.parse('2021-12-08'), LocalDate.parse('2021-12-19'),
        LocalDate.parse('2021-12-25')] as Set

    def weekendDaySet = [DayOfWeek.SATURDAY,DayOfWeek.SUNDAY] as Set

    int calcWorkingDays(start, end, holidaySet, weekendDaySet) {
        (start..<end).inject(0) { subtotal, d ->
            if (!(d in holidaySet || DayOfWeek.from(d) in weekendDaySet))
                subtotal + 1
            else
                subtotal
        }
    }

    def start = LocalDate.parse('2021-08-01')
    def end = LocalDate.parse('2021-09-01')

    println "${calcWorkingDays(start,end,holidaySet,weekendDaySet)} working day(s) between $start and $end"

    Copiez ce code dans un fichier appelé wdb.groovy et exécutez-le à partir de la ligne de commande pour voir les résultats :

    $ groovy wdb.groovy
    22 working day(s) between 2021-08-01 and 2021-09-01

    Passons en revue ceci.

    Tout d’abord, je crée un ensemble de dates de vacances (ce sont les « jours feriados » du Chili pour 2021, au cas où vous vous le demanderiez) appelé holidaySet. Notez que le modèle par défaut pour LocalDate.parse() est ‘aaaa-MM-jj‘, j’ai donc laissé le modèle ici. Notez également que j’utilise le raccourci Groovy [a,b,c] créer un Lister puis le contraindre à un Régler.

    Ensuite, je veux sauter les samedis et dimanches, donc je crée un autre ensemble incorporant deux énumérer valeurs de java.time.DayOfWeekSAMEDI et DIMANCHE.

    Puis je définis une méthode calcWorkingDays() qui prend comme arguments la date de début, la date de fin (qui suivant l’exemple précédent de entre() est la première valeur en dehors de la plage que je souhaite prendre en compte), l’ensemble de vacances et l’ensemble de jours de week-end. Ligne par ligne, cette méthode :

    • Définit une plage entre début et finir, ouvert sur le finir, (c’est ce que signifie) et exécute l’argument de clôture transmis au injecter() méthode (injecter() met en œuvre l’opération « réduire » sur Lister dans Groovy) sur les éléments successifs dans le périmètre:
      • Tant que n’est ni dans le vacancesSet ni dans le week-endJourEnsemble, incrémente le total par 1
    • Renvoie la valeur du résultat renvoyé par injecter()

    Ensuite, je définis le début et finir dates entre lesquelles je veux calculer les jours ouvrés.

    Enfin, j’appelle imprimer en utilisant un Groovy GString pour évaluer la calcWorkingDays() méthode et afficher le résultat.

    Notez que j’aurais pu utiliser le chaque fermeture au lieu de injecter, voire un pour boucle. J’aurais également pu utiliser des flux Java plutôt que des plages, des listes et des fermetures Groovy. Beaucoup d’options.

    Mais pourquoi ne pas utiliser groovy.Date ?

    Certains d’entre vous, anciens utilisateurs de Groovy, se demandent peut-être pourquoi je n’utilise pas le bon vieux groovy.Date. La réponse est, je pourrais l’utiliser. Mais Groovy Date est basé sur Java Date, et il y a de bonnes raisons de passer à java.time, même si Groovy Date a ajouté pas mal de choses intéressantes à Java Date.

    Pour moi, la raison principale est qu’il y a des décisions de conception pas si bonnes enfouies dans l’implémentation de Java Date, le pire étant qu’il est inutilement modifiable. J’ai passé un certain temps à traquer un bug étrange qui est né de ma mauvaise compréhension du clearTime() méthode à la date Groovy. J’ai appris qu’il efface en fait le champ d’heure de l’instance de date, plutôt que de renvoyer la valeur de date avec la partie heure définie sur ” 00:00:00 “.

    Les instances de date ne sont pas non plus thread-safe, ce qui peut être un peu difficile pour les applications multithread.

    Enfin, avoir à la fois la date et l’heure dans un seul champ n’est pas toujours pratique et peut conduire à d’étranges contorsions de modélisation de données. Pensez, par exemple, à un jour où plusieurs événements se produisent : Idéalement, le Date champ serait le jour, et le temps le champ serait sur chaque événement; mais ce n’est pas facile à faire avec Groovy Date.

    Groovy est groovy

    Groovy est un projet Apache, et il fournit une syntaxe simplifiée pour Java afin que vous puissiez l’utiliser pour des scripts rapides et simples en plus des applications complexes. Vous conservez la puissance de Java, mais vous y accédez avec un ensemble d’outils efficace. Essayez-le bientôt, et voyez si vous trouvez votre groove avec Groovy.

    Source

    La Rédaction

    L'équipe rédactionnnelle du site

    Pour contacter personnellement le taulier :

    Laisser un commentaire

    Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

    Copy code