Line 0
Link Here
|
|
|
1 |
package org.apache.poi.ss.formula.atp; |
2 |
|
3 |
import java.util.Calendar; |
4 |
import java.util.Date; |
5 |
|
6 |
import org.apache.poi.ss.usermodel.DateUtil; |
7 |
|
8 |
/** |
9 |
* A calculator for workdays, considering dates as excel representations. |
10 |
* |
11 |
* @author jfaenomoto@gmail.com |
12 |
*/ |
13 |
public class WorkdayCalculator { |
14 |
|
15 |
public static final WorkdayCalculator instance = new WorkdayCalculator(); |
16 |
|
17 |
/** |
18 |
* Constructor. |
19 |
*/ |
20 |
private WorkdayCalculator() { |
21 |
// enforcing singleton |
22 |
} |
23 |
|
24 |
/** |
25 |
* Calculate how many workdays are there between a start and an end date, as excel representations, considering a range of holidays. |
26 |
* |
27 |
* @param start start date. |
28 |
* @param end end date. |
29 |
* @param holidays an array of holidays. |
30 |
* @return number of workdays between start and end dates, including both dates. |
31 |
*/ |
32 |
public int calculateWorkdays(double start, double end, double[] holidays) { |
33 |
int saturdaysPast = this.pastDaysOfWeek(start, end, Calendar.SATURDAY); |
34 |
int sundaysPast = this.pastDaysOfWeek(start, end, Calendar.SUNDAY); |
35 |
int nonWeekendHolidays = this.calculateNonWeekendHolidays(start, end, holidays); |
36 |
return (int) (end - start + 1) - saturdaysPast - sundaysPast - nonWeekendHolidays; |
37 |
} |
38 |
|
39 |
/** |
40 |
* Calculate the workday past x workdays from a starting date, considering a range of holidays. |
41 |
* |
42 |
* @param start start date. |
43 |
* @param workdays number of workdays to be past from starting date. |
44 |
* @param holidays an array of holidays. |
45 |
* @return date past x workdays. |
46 |
*/ |
47 |
public Date calculateWorkdays(double start, int workdays, double[] holidays) { |
48 |
Date startDate = DateUtil.getJavaDate(start); |
49 |
Calendar endDate = Calendar.getInstance(); |
50 |
endDate.setTime(startDate); |
51 |
endDate.add(Calendar.DAY_OF_YEAR, workdays); |
52 |
int skippedDays = 0; |
53 |
do { |
54 |
double end = DateUtil.getExcelDate(endDate.getTime()); |
55 |
int saturdaysPast = this.pastDaysOfWeek(start, end, Calendar.SATURDAY); |
56 |
int sundaysPast = this.pastDaysOfWeek(start, end, Calendar.SUNDAY); |
57 |
int nonWeekendHolidays = this.calculateNonWeekendHolidays(start, end, holidays); |
58 |
skippedDays = saturdaysPast + sundaysPast + nonWeekendHolidays; |
59 |
endDate.add(Calendar.DAY_OF_YEAR, skippedDays); |
60 |
start = end + isNonWorkday(end, holidays); |
61 |
} while (skippedDays != 0); |
62 |
return endDate.getTime(); |
63 |
} |
64 |
|
65 |
/** |
66 |
* Calculates how many days of week past between a start and an end date. |
67 |
* |
68 |
* @param start start date. |
69 |
* @param end end date. |
70 |
* @param dayOfWeek a day of week as represented by {@link Calendar} constants. |
71 |
* @return how many days of week past in this interval. |
72 |
*/ |
73 |
protected int pastDaysOfWeek(double start, double end, int dayOfWeek) { |
74 |
int pastDaysOfWeek = 0; |
75 |
int startDay = (int) Math.floor(start < end ? start : end); |
76 |
int endDay = (int) Math.floor(end > start ? end : start); |
77 |
for (; startDay <= endDay; startDay++) { |
78 |
Calendar today = Calendar.getInstance(); |
79 |
today.setTime(DateUtil.getJavaDate(startDay)); |
80 |
if (today.get(Calendar.DAY_OF_WEEK) == dayOfWeek) { |
81 |
pastDaysOfWeek++; |
82 |
} |
83 |
} |
84 |
return start < end ? pastDaysOfWeek : -pastDaysOfWeek; |
85 |
} |
86 |
|
87 |
/** |
88 |
* Calculates how many holidays in a list are workdays, considering an interval of dates. |
89 |
* |
90 |
* @param start start date. |
91 |
* @param end end date. |
92 |
* @param holidays an array of holidays. |
93 |
* @return number of holidays that occur in workdays, between start and end dates. |
94 |
*/ |
95 |
protected int calculateNonWeekendHolidays(double start, double end, double[] holidays) { |
96 |
int nonWeekendHolidays = 0; |
97 |
double startDay = start < end ? start : end; |
98 |
double endDay = end > start ? end : start; |
99 |
for (int i = 0; i < holidays.length; i++) { |
100 |
if (isInARange(startDay, endDay, holidays[i])) { |
101 |
if (!isWeekend(holidays[i])) { |
102 |
nonWeekendHolidays++; |
103 |
} |
104 |
} |
105 |
} |
106 |
return start < end ? nonWeekendHolidays : -nonWeekendHolidays; |
107 |
} |
108 |
|
109 |
/** |
110 |
* @param aDate a given date. |
111 |
* @return <code>true</code> if date is weekend, <code>false</code> otherwise. |
112 |
*/ |
113 |
protected boolean isWeekend(double aDate) { |
114 |
Calendar date = Calendar.getInstance(); |
115 |
date.setTime(DateUtil.getJavaDate(aDate)); |
116 |
return date.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || date.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY; |
117 |
} |
118 |
|
119 |
/** |
120 |
* @param aDate a given date. |
121 |
* @param holidays an array of holidays. |
122 |
* @return <code>true</code> if date is a holiday, <code>false</code> otherwise. |
123 |
*/ |
124 |
protected boolean isHoliday(double aDate, double[] holidays) { |
125 |
for (int i = 0; i < holidays.length; i++) { |
126 |
if (Math.round(holidays[i]) == Math.round(aDate)) { |
127 |
return true; |
128 |
} |
129 |
} |
130 |
return false; |
131 |
} |
132 |
|
133 |
/** |
134 |
* @param aDate a given date. |
135 |
* @param holidays an array of holidays. |
136 |
* @return <code>1</code> is not a workday, <code>0</code> otherwise. |
137 |
*/ |
138 |
protected int isNonWorkday(double aDate, double[] holidays) { |
139 |
return isWeekend(aDate) || isHoliday(aDate, holidays) ? 1 : 0; |
140 |
} |
141 |
|
142 |
/** |
143 |
* @param start start date. |
144 |
* @param end end date. |
145 |
* @param aDate a date to be analyzed. |
146 |
* @return <code>true</code> if aDate is between start and end dates, <code>false</code> otherwise. |
147 |
*/ |
148 |
protected boolean isInARange(double start, double end, double aDate) { |
149 |
return aDate >= start && aDate <= end; |
150 |
} |
151 |
} |