Skip to content

Commit 1e813a6

Browse files
author
Eivind Bergstøl
committed
Rewrite NorwegianDateUtil to use java.time
java.util.Date and java.util.Calendar is old now and we don't want to use that any more. Breaking changes: We use ZonedDateTime in stead of Date to represent a date to check for working days. The list of holidays are now a NavigableSet<LocalDate> and not a Date[]. Co-authored-by: tobiasgwaaler Co-authored-by: runeflobakk Co-authored-by: martin-jackson Co-authored-by: hhaga Co-authored-by: fredrbus
1 parent 0f16b47 commit 1e813a6

File tree

3 files changed

+190
-273
lines changed

3 files changed

+190
-273
lines changed

pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@
125125
<version>2.2</version>
126126
<scope>test</scope>
127127
</dependency>
128+
<dependency>
129+
<groupId>uk.co.probablyfine</groupId>
130+
<artifactId>java-8-matchers</artifactId>
131+
<version>1.9</version>
132+
</dependency>
128133
<dependency>
129134
<groupId>org.hibernate.validator</groupId>
130135
<artifactId>hibernate-validator</artifactId>

src/main/java/no/bekk/bekkopen/date/NorwegianDateUtil.java

+71-185
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,24 @@
2626
* #L%
2727
*/
2828

29-
import java.util.Arrays;
30-
import java.util.Calendar;
31-
import java.util.Date;
29+
import java.time.DayOfWeek;
30+
import java.time.LocalDate;
31+
import java.time.Month;
32+
import java.time.ZonedDateTime;
33+
import java.util.Comparator;
3234
import java.util.HashMap;
33-
import java.util.HashSet;
3435
import java.util.Map;
36+
import java.util.NavigableSet;
3537
import java.util.Set;
38+
import java.util.TreeSet;
39+
import java.util.stream.Stream;
3640

3741
/**
3842
* Utility class for Norwegian dates.
3943
*/
4044
public class NorwegianDateUtil {
41-
private static Map<Integer, Set<Date>> holidays;
45+
46+
private static Map<Integer, NavigableSet<LocalDate>> holidays;
4247

4348
/**
4449
* Adds the given number of working days to the given date. A working day is
@@ -60,19 +65,14 @@ public class NorwegianDateUtil {
6065
* The number of working days to add.
6166
* @return The new date.
6267
*/
63-
public static Date addWorkingDaysToDate(Date date, int days) {
64-
Calendar cal = dateToCalendar(date);
65-
66-
for (int i = 0; i < days; i++) {
67-
cal.add(Calendar.DATE, 1);
68-
while (!isWorkingDay(cal)) {
69-
cal.add(Calendar.DATE, 1);
70-
}
71-
}
72-
73-
return cal.getTime();
68+
public static ZonedDateTime addWorkingDaysToDate(ZonedDateTime date, int days) {
69+
return Stream.iterate(date, (d) -> d.plusDays(1))
70+
.filter(NorwegianDateUtil::isWorkingDay)
71+
.limit(days + 1)
72+
.max(Comparator.naturalOrder())
73+
.orElse(date);
7474
}
75-
75+
7676
/**
7777
* Will check if the given date is a working day. That is check if the given
7878
* date is a weekend day or a national holiday.
@@ -81,8 +81,8 @@ public static Date addWorkingDaysToDate(Date date, int days) {
8181
* The date to check.
8282
* @return true if the given date is a working day, false otherwise.
8383
*/
84-
public static boolean isWorkingDay(Date date) {
85-
return isWorkingDay(dateToCalendar(date));
84+
public static boolean isWorkingDay(ZonedDateTime date) {
85+
return date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY && !isHoliday(date);
8686
}
8787

8888
/**
@@ -92,214 +92,100 @@ public static boolean isWorkingDay(Date date) {
9292
* The Date to check.
9393
* @return true if holiday, false otherwise.
9494
*/
95-
public static boolean isHoliday(Date date) {
96-
return isHoliday(dateToCalendar(date));
95+
public static boolean isHoliday(ZonedDateTime date) {
96+
final Set<LocalDate> holidaySet = getHolidaySet(date.getYear());
97+
return holidaySet.contains(date.toLocalDate());
9798
}
98-
99+
99100
/**
100-
* Return a sorted array of holidays for a given year.
101+
* Return a sorted set of holidays for a given year.
101102
*
102103
* @param year
103104
* The year to get holidays for.
104-
* @return The array of holidays, sorted by date.
105+
* @return The set of holidays, naturally sorted by date.
105106
*/
106-
public static Date[] getHolidays(int year) {
107-
Set<Date> days = getHolidaySet(year);
108-
Date[] dates = days.toArray(new Date[days.size()]);
109-
Arrays.sort(dates);
110-
return dates;
107+
public static NavigableSet<LocalDate> getHolidays(int year) {
108+
return getHolidaySet(year);
111109
}
112110

111+
/**
112+
* Calculates easter day (sunday) by using Spencer Jones formula found here:
113+
* <a href="http://no.wikipedia.org/wiki/P%C3%A5skeformelen">Wikipedia -
114+
* Påskeformelen</a>
115+
*
116+
* @param year
117+
* The year to calculate from.
118+
* @return The LocalDate representing easter day for the given year.
119+
*/
120+
private static LocalDate getEasterDay(int year) {
121+
int a = year % 19;
122+
int b = year / 100;
123+
int c = year % 100;
124+
int d = b / 4;
125+
int e = b % 4;
126+
int f = (b + 8) / 25;
127+
int g = (b - f + 1) / 3;
128+
int h = ((19 * a) + b - d - g + 15) % 30;
129+
int i = c / 4;
130+
int k = c % 4;
131+
int l = (32 + (2 * e) + (2 * i) - h - k) % 7;
132+
int m = (a + (11 * h) + (22 * l)) / 451;
133+
int n = (h + l - (7 * m) + 114) / 31; // This is the month number.
134+
int p = (h + l - (7 * m) + 114) % 31; // This is the date minus one.
135+
136+
return LocalDate.of(year, n, p + 1);
137+
}
138+
113139
/**
114140
* Get a set of holidays for a given year.
115141
*
116142
* @param year
117143
* The year to get holidays for.
118144
* @return The set of dates.
119145
*/
120-
private static Set<Date> getHolidaySet(int year) {
146+
private static NavigableSet<LocalDate> getHolidaySet(int year) {
121147
if (holidays == null) {
122148
holidays = new HashMap<>();
123149
}
124150
if (!holidays.containsKey(year)) {
125-
Set<Date> yearSet = new HashSet<>();
151+
NavigableSet<LocalDate> yearSet = new TreeSet<>();
126152

127153
// Add set holidays.
128-
yearSet.add(getDate(1, Calendar.JANUARY, year));
129-
yearSet.add(getDate(1, Calendar.MAY, year));
130-
yearSet.add(getDate(17, Calendar.MAY, year));
131-
yearSet.add(getDate(25, Calendar.DECEMBER, year));
132-
yearSet.add(getDate(26, Calendar.DECEMBER, year));
154+
yearSet.add(LocalDate.of(year, Month.JANUARY, 1));
155+
yearSet.add(LocalDate.of(year, Month.MAY, 1));
156+
yearSet.add(LocalDate.of(year, Month.MAY, 17));
157+
yearSet.add(LocalDate.of(year, Month.DECEMBER, 25));
158+
yearSet.add(LocalDate.of(year, Month.DECEMBER, 26));
133159

134160
// Add movable holidays - based on easter day.
135-
Calendar easterDay = dateToCalendar(getEasterDay(year));
161+
final LocalDate easterDay = getEasterDay(year);
136162

137163
// Sunday before easter.
138-
yearSet.add(rollGetDate(easterDay, -7));
164+
yearSet.add(easterDay.minusDays(7));
139165

140166
// Thursday before easter.
141-
yearSet.add(rollGetDate(easterDay, -3));
167+
yearSet.add(easterDay.minusDays(3));
142168

143169
// Friday before easter.
144-
yearSet.add(rollGetDate(easterDay, -2));
170+
yearSet.add(easterDay.minusDays(2));
145171

146172
// Easter day.
147-
yearSet.add(easterDay.getTime());
173+
yearSet.add(easterDay);
148174

149175
// Second easter day.
150-
yearSet.add(rollGetDate(easterDay, 1));
176+
yearSet.add(easterDay.plusDays(1));
151177

152178
// "Kristi himmelfart" day.
153-
yearSet.add(rollGetDate(easterDay, 39));
179+
yearSet.add(easterDay.plusDays(39));
154180

155181
// "Pinse" day.
156-
yearSet.add(rollGetDate(easterDay, 49));
182+
yearSet.add(easterDay.plusDays(49));
157183

158184
// Second "Pinse" day.
159-
yearSet.add(rollGetDate(easterDay, 50));
185+
yearSet.add(easterDay.plusDays(50));
160186

161187
holidays.put(year, yearSet);
162188
}
163189
return holidays.get(year);
164190
}
165-
166-
/**
167-
* Will check if the given date is a working day. That is check if the given
168-
* date is a weekend day or a national holiday.
169-
*
170-
* @param cal
171-
* The Calendar object representing the date.
172-
* @return true if the given date is a working day, false otherwise.
173-
*/
174-
private static boolean isWorkingDay(Calendar cal) {
175-
return cal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY
176-
&& !isHoliday(cal);
177-
}
178-
179-
/**
180-
* Check if given Calendar object represents a holiday.
181-
*
182-
* @param cal
183-
* The Calendar to check.
184-
* @return true if holiday, false otherwise.
185-
*/
186-
private static boolean isHoliday(Calendar cal) {
187-
int year = cal.get(Calendar.YEAR);
188-
Set<?> yearSet = getHolidaySet(year);
189-
for (Object aYearSet : yearSet) {
190-
Date date = (Date) aYearSet;
191-
if (checkDate(cal, dateToCalendar(date))) {
192-
return true;
193-
}
194-
}
195-
return false;
196-
}
197-
198-
/**
199-
* Calculates easter day (sunday) by using Spencer Jones formula found here:
200-
* <a href="http://no.wikipedia.org/wiki/P%C3%A5skeformelen">Wikipedia -
201-
* Påskeformelen</a>
202-
*
203-
* @param year
204-
* The year to calculate from.
205-
* @return The Calendar object representing easter day for the given year.
206-
*/
207-
private static Date getEasterDay(int year) {
208-
int a = year % 19;
209-
int b = year / 100;
210-
int c = year % 100;
211-
int d = b / 4;
212-
int e = b % 4;
213-
int f = (b + 8) / 25;
214-
int g = (b - f + 1) / 3;
215-
int h = ((19 * a) + b - d - g + 15) % 30;
216-
int i = c / 4;
217-
int k = c % 4;
218-
int l = (32 + (2 * e) + (2 * i) - h - k) % 7;
219-
int m = (a + (11 * h) + (22 * l)) / 451;
220-
int n = (h + l - (7 * m) + 114) / 31; // This is the month number.
221-
int p = (h + l - (7 * m) + 114) % 31; // This is the date minus one.
222-
223-
Calendar cal = Calendar.getInstance();
224-
cal.set(Calendar.YEAR, year);
225-
cal.set(Calendar.MONTH, n - 1);
226-
cal.set(Calendar.DATE, p + 1);
227-
228-
return cal.getTime();
229-
}
230-
231-
/**
232-
* Check if the given dates match on day and month.
233-
*
234-
* @param cal
235-
* The Calendar representing the first date.
236-
* @param other
237-
* The Calendar representing the second date.
238-
* @return true if they match, false otherwise.
239-
*/
240-
private static boolean checkDate(Calendar cal, Calendar other) {
241-
return checkDate(cal, other.get(Calendar.DATE), other.get(Calendar.MONTH));
242-
}
243-
244-
/**
245-
* Check if the given date represents the given date and month.
246-
*
247-
* @param cal
248-
* The Calendar object representing date to check.
249-
* @param date
250-
* The date.
251-
* @param month
252-
* The month.
253-
* @return true if they match, false otherwise.
254-
*/
255-
private static boolean checkDate(Calendar cal, int date, int month) {
256-
return cal.get(Calendar.DATE) == date && cal.get(Calendar.MONTH) == month;
257-
}
258-
259-
/**
260-
* Convert the given Date object to a Calendar instance.
261-
*
262-
* @param date
263-
* The Date object.
264-
* @return The Calendar instance.
265-
*/
266-
private static Calendar dateToCalendar(Date date) {
267-
Calendar cal = Calendar.getInstance();
268-
cal.setTime(date);
269-
return cal;
270-
}
271-
272-
/**
273-
* Add the given number of days to the calendar and convert to Date.
274-
*
275-
* @param calendar
276-
* The calendar to add to.
277-
* @param days
278-
* The number of days to add.
279-
* @return The date object given by the modified calendar.
280-
*/
281-
private static Date rollGetDate(Calendar calendar, int days) {
282-
Calendar easterSunday = (Calendar) calendar.clone();
283-
easterSunday.add(Calendar.DATE, days);
284-
return easterSunday.getTime();
285-
}
286-
287-
/**
288-
* Get the date for the given values.
289-
*
290-
* @param day
291-
* The day.
292-
* @param month
293-
* The month.
294-
* @param year
295-
* The year.
296-
* @return The date represented by the given values.
297-
*/
298-
private static Date getDate(int day, int month, int year) {
299-
Calendar cal = Calendar.getInstance();
300-
cal.set(Calendar.YEAR, year);
301-
cal.set(Calendar.MONTH, month);
302-
cal.set(Calendar.DATE, day);
303-
return cal.getTime();
304-
}
305191
}

0 commit comments

Comments
 (0)