Skip to content

Commit a28219e

Browse files
authored
Merge pull request #72 from bekkopen/noDateUtil
Rewrite NorwegianDateUtil to use java.time
2 parents 0f16b47 + cde231a commit a28219e

File tree

3 files changed

+194
-276
lines changed

3 files changed

+194
-276
lines changed

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@
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+
<scope>test</scope>
133+
</dependency>
128134
<dependency>
129135
<groupId>org.hibernate.validator</groupId>
130136
<artifactId>hibernate-validator</artifactId>

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

+75-188
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,28 @@
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.ZoneId;
33+
import java.time.ZonedDateTime;
34+
import java.util.Comparator;
3235
import java.util.HashMap;
33-
import java.util.HashSet;
3436
import java.util.Map;
37+
import java.util.NavigableSet;
3538
import java.util.Set;
39+
import java.util.TreeSet;
40+
import java.util.stream.Stream;
3641

3742
/**
3843
* Utility class for Norwegian dates.
3944
*/
4045
public class NorwegianDateUtil {
41-
private static Map<Integer, Set<Date>> holidays;
46+
47+
public static final String ZONEID_EUROPE_OSLO = "Europe/Oslo";
48+
public static final ZoneId ZONE_NORWAY = ZoneId.of(ZONEID_EUROPE_OSLO);
49+
50+
private final static Map<Integer, NavigableSet<LocalDate>> holidays = new HashMap<>();
4251

4352
/**
4453
* Adds the given number of working days to the given date. A working day is
@@ -60,19 +69,14 @@ public class NorwegianDateUtil {
6069
* The number of working days to add.
6170
* @return The new date.
6271
*/
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();
72+
public static ZonedDateTime addWorkingDaysToDate(ZonedDateTime date, int days) {
73+
return Stream.iterate(date, (d) -> d.plusDays(1))
74+
.filter(NorwegianDateUtil::isWorkingDay)
75+
.limit(days + 1)
76+
.max(Comparator.naturalOrder())
77+
.orElse(date);
7478
}
75-
79+
7680
/**
7781
* Will check if the given date is a working day. That is check if the given
7882
* date is a weekend day or a national holiday.
@@ -81,8 +85,8 @@ public static Date addWorkingDaysToDate(Date date, int days) {
8185
* The date to check.
8286
* @return true if the given date is a working day, false otherwise.
8387
*/
84-
public static boolean isWorkingDay(Date date) {
85-
return isWorkingDay(dateToCalendar(date));
88+
public static boolean isWorkingDay(ZonedDateTime date) {
89+
return date.getDayOfWeek() != DayOfWeek.SATURDAY && date.getDayOfWeek() != DayOfWeek.SUNDAY && !isHoliday(date);
8690
}
8791

8892
/**
@@ -92,214 +96,97 @@ public static boolean isWorkingDay(Date date) {
9296
* The Date to check.
9397
* @return true if holiday, false otherwise.
9498
*/
95-
public static boolean isHoliday(Date date) {
96-
return isHoliday(dateToCalendar(date));
99+
public static boolean isHoliday(ZonedDateTime date) {
100+
final Set<LocalDate> holidaySet = getHolidaySet(date.getYear());
101+
return holidaySet.contains(date.toLocalDate());
97102
}
98-
103+
99104
/**
100-
* Return a sorted array of holidays for a given year.
105+
* Return a sorted set of holidays for a given year.
101106
*
102107
* @param year
103108
* The year to get holidays for.
104-
* @return The array of holidays, sorted by date.
109+
* @return The set of holidays, naturally sorted by date.
105110
*/
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;
111+
public static NavigableSet<LocalDate> getHolidays(int year) {
112+
return getHolidaySet(year);
111113
}
112114

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

127154
// 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));
155+
yearSet.add(LocalDate.of(year, Month.JANUARY, 1));
156+
yearSet.add(LocalDate.of(year, Month.MAY, 1));
157+
yearSet.add(LocalDate.of(year, Month.MAY, 17));
158+
yearSet.add(LocalDate.of(year, Month.DECEMBER, 25));
159+
yearSet.add(LocalDate.of(year, Month.DECEMBER, 26));
133160

134161
// Add movable holidays - based on easter day.
135-
Calendar easterDay = dateToCalendar(getEasterDay(year));
162+
final LocalDate easterDay = getEasterDay(year);
136163

137164
// Sunday before easter.
138-
yearSet.add(rollGetDate(easterDay, -7));
165+
yearSet.add(easterDay.minusDays(7));
139166

140167
// Thursday before easter.
141-
yearSet.add(rollGetDate(easterDay, -3));
168+
yearSet.add(easterDay.minusDays(3));
142169

143170
// Friday before easter.
144-
yearSet.add(rollGetDate(easterDay, -2));
171+
yearSet.add(easterDay.minusDays(2));
145172

146173
// Easter day.
147-
yearSet.add(easterDay.getTime());
174+
yearSet.add(easterDay);
148175

149176
// Second easter day.
150-
yearSet.add(rollGetDate(easterDay, 1));
177+
yearSet.add(easterDay.plusDays(1));
151178

152179
// "Kristi himmelfart" day.
153-
yearSet.add(rollGetDate(easterDay, 39));
180+
yearSet.add(easterDay.plusDays(39));
154181

155182
// "Pinse" day.
156-
yearSet.add(rollGetDate(easterDay, 49));
183+
yearSet.add(easterDay.plusDays(49));
157184

158185
// Second "Pinse" day.
159-
yearSet.add(rollGetDate(easterDay, 50));
186+
yearSet.add(easterDay.plusDays(50));
160187

161188
holidays.put(year, yearSet);
162189
}
163190
return holidays.get(year);
164191
}
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-
}
305192
}

0 commit comments

Comments
 (0)