View Javadoc
1   package org.nuiton.util;
2   
3   /*-
4    * #%L
5    * Nuiton Utils
6    * %%
7    * Copyright (C) 2004 - 2020 CodeLutin
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * #L%
23   */
24  
25  import java.time.DayOfWeek;
26  import java.time.LocalDate;
27  import java.time.Year;
28  import java.time.temporal.ChronoUnit;
29  import java.util.HashSet;
30  import java.util.LinkedList;
31  import java.util.List;
32  import java.util.Set;
33  import java.util.function.Function;
34  
35  /**
36   * Permet de calculer le nombre de jour ouvré entre deux dates.
37   *
38   * du 3 au 3 donne 0.
39   * du 3 au 4 donne 1 (si 3 n'est pas un jour fermé)
40   */
41  public class WorkdayUtil {
42  
43      public static long computeWorday(LocalDate start, LocalDate end, Set<DayOfWeek> weekWorkday, Function<Year, List<LocalDate>> publicHoliday) {
44  
45          // recherche tous les jours fériés des années entre les deux dates
46          Set<LocalDate> publicHolidays = new HashSet<>();
47          for (int year = start.getYear(), lastYear = end.getYear(); year <= lastYear; year++) {
48              publicHolidays.addAll(publicHoliday.apply(Year.of(year)));
49          }
50  
51          // calcul le nombre de jour férié dans l'interval
52          long publicHolidayNumber = publicHolidays.stream()
53                  // On garde que des jours fériés qui tombe un jour ouvré
54                  .filter(d -> weekWorkday.contains(d.getDayOfWeek()))
55                  // On garde que des jours fériés qui sont dans l'interval de date
56                  .filter(d -> start.compareTo(d) * d.compareTo(end) >= 0)
57                  .count();
58  
59          // calcul le nombre de jour ouvré en semaine pleine
60          long days = ChronoUnit.DAYS.between(start, end);
61          long week = days / DayOfWeek.values().length;
62  
63          long result = week * weekWorkday.size();
64  
65          // on calcul le nombre de jour ouvré de la dernière semaine non pleine
66          LocalDate lastDays = end.minusDays(days % DayOfWeek.values().length);
67  
68          while (lastDays.isBefore(end)) {
69              if (weekWorkday.contains(lastDays.getDayOfWeek())) {
70                  result++;
71              }
72              lastDays = lastDays.plusDays(1);
73          }
74  
75          // on retranche les jours fériés
76          result -= publicHolidayNumber;
77  
78          return result;
79      }
80  
81      public static class FrenchPublicHoliday implements Function<Year, List<LocalDate>> {
82  
83          @Override
84          public List<LocalDate> apply(Year year) {
85              LocalDate easter = computeEaster(year);
86  
87              List<LocalDate> result = new LinkedList<>();
88  
89              result.add(computeEasterMonday(easter));
90              result.add(computeAscensionDay(easter));
91              result.add(computeWhitMonday(easter));
92              result.add(LocalDate.of(year.getValue(), 1, 1));
93              result.add(LocalDate.of(year.getValue(), 5, 1));
94              result.add(LocalDate.of(year.getValue(), 5, 8));
95              result.add(LocalDate.of(year.getValue(), 7, 14));
96              result.add(LocalDate.of(year.getValue(), 8, 15));
97              result.add(LocalDate.of(year.getValue(), 11, 1));
98              result.add(LocalDate.of(year.getValue(), 11, 11));
99              result.add(LocalDate.of(year.getValue(), 12, 25));
100 
101             return result;
102         }
103     }
104 
105     /**
106      * Calcul fait par la méthode de Butcher-Meeus (valide si année ≥ 1583)
107      *
108      * @param year l'année pour lequel on souhaite calculer le dimanche de paques
109      * @return la date du dimanche paques
110      */
111     public static LocalDate computeEaster(Year year) {
112         int y = year.getValue();
113         int n = y % 19;
114 
115         int c = y / 100;
116         int u = y % 100;
117 
118         int s = c / 4;
119         int t = c % 4;
120 
121         int p = (c + 8) / 25;
122         int q = (c - p + 1) / 3;
123 
124         int e = (19 * n + c - s - q + 15) % 30;
125 
126         int b = u / 4;
127         int d = u % 4;
128 
129         int L = (32 + 2 * t + 2 * b - e - d) % 7;
130         int h = (n + 11 * e + 22 * L) / 451;
131 
132         int m = (e + L - 7 * h + 114) / 31;
133         int j = (e + L - 7 * h + 114) % 31;
134 
135         LocalDate easterMonday = LocalDate.of(y, m, j + 1);
136 
137         return easterMonday;
138     }
139 
140     public static LocalDate computeEasterMonday(LocalDate easter) {
141         LocalDate easterMonday = easter.plusDays(1);
142         return easterMonday;
143     }
144 
145     public static LocalDate computeAscensionDay(LocalDate easter) {
146         LocalDate ascensionDay = easter.plusDays(39);
147         return ascensionDay;
148     }
149 
150     public static LocalDate computeWhitMonday(LocalDate easter) {
151         LocalDate whitMonday = easter.plusDays(50);
152         return whitMonday;
153     }
154 }