001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.kfs.module.endow.businessobject.lookup;
017
018 import java.util.List;
019 import java.sql.Date;
020 import java.util.Calendar;
021 import org.kuali.rice.kns.service.DateTimeService;
022 import org.apache.commons.lang.StringUtils;
023 import org.kuali.kfs.module.endow.EndowConstants;
024 import org.kuali.kfs.module.endow.EndowPropertyConstants;
025 import org.kuali.kfs.module.endow.document.service.KEMService;
026
027 public class CalculateProcessDateUsingFrequencyCodeService {
028
029 protected DateTimeService dateTimeService;
030 protected KEMService kemService;
031
032 /**
033 * This method uses frequency code to derive the next processing date
034 *
035 * @param frquencyCode frequencyCode
036 * @return returns the processing date
037 */
038 public Date calculateProcessDate(String frequencyCode) {
039 Date currentDate = dateTimeService.getCurrentSqlDate();
040
041 String frequencyType = frequencyCode.substring(0, 1);
042
043 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.DAILY)) {
044 return currentDate;
045 }
046
047 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.WEEKLY)) {
048 String dayOfWeek = frequencyCode.substring(1, 4).toUpperCase();
049 return calculateNextWeeklyDate(dayOfWeek, currentDate);
050 }
051
052 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_MONTHLY)) {
053 String dayOfSemiMonthly = frequencyCode.substring(1, 3);
054 return calculateNextSemiMonthlyDate(dayOfSemiMonthly, currentDate);
055 }
056
057 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.MONTHLY)) {
058 String dayOfMonth = frequencyCode.substring(1, 3);
059 return calculateNextMonthlyDate(dayOfMonth, currentDate);
060 }
061
062 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.QUARTERLY) ||
063 frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_ANNUALLY) ||
064 frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.ANNUALLY)) {
065 String month = frequencyCode.substring(1, 2);
066 String dayOfMonth = frequencyCode.substring(2, 4);
067 return calculateNextQuarterlyOrSemiAnnuallyOrAnnuallyProcessDate(month, dayOfMonth, frequencyType, currentDate);
068 }
069
070 return currentDate;
071 }
072
073 /**
074 * Method to calculate the next processing week date based on the frequency type
075 * adds the appropriate number of days to the current date
076 * @param dayOfWeek
077 * @return next processing date
078 */
079 protected Date calculateNextWeeklyDate(String dayOfWeekFromFrequencyCode, Date currentDate) {
080 Calendar calendar = Calendar.getInstance();
081 calendar.setTime(currentDate);
082
083 int daysToAdd = 0;
084 int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // today's day of the week
085 int maximumDaysInWeek = calendar.getActualMaximum(Calendar.DAY_OF_WEEK);
086
087 if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.MONDAY)) {
088 if (dayOfWeek < Calendar.MONDAY)
089 daysToAdd = Calendar.MONDAY - dayOfWeek;
090 else
091 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.MONDAY;
092 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.TUESDAY)) {
093 if (dayOfWeek < Calendar.TUESDAY)
094 daysToAdd = Calendar.TUESDAY - dayOfWeek;
095 else
096 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.TUESDAY;
097 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.WEDNESDAY)) {
098 if (dayOfWeek < Calendar.WEDNESDAY)
099 daysToAdd = Calendar.WEDNESDAY - dayOfWeek;
100 else
101 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.WEDNESDAY;
102 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.THURSDAY)) {
103 if (dayOfWeek < Calendar.THURSDAY)
104 daysToAdd = Calendar.THURSDAY - dayOfWeek;
105 else
106 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.THURSDAY;
107 } else if (dayOfWeekFromFrequencyCode.equalsIgnoreCase(EndowConstants.FrequencyWeekDays.FRIDAY)) {
108 if (dayOfWeek < Calendar.FRIDAY)
109 daysToAdd = Calendar.FRIDAY - dayOfWeek;
110 else
111 daysToAdd = maximumDaysInWeek - dayOfWeek + Calendar.FRIDAY;
112 }
113
114 calendar.add(Calendar.DAY_OF_MONTH, daysToAdd);
115
116 return new java.sql.Date(calendar.getTimeInMillis());
117 }
118
119 /**
120 * Method to calculate the next processing semi-monthly date based on the frequency type
121 * Sets the day of the month and then returns the processing date
122 * @param dayOfSemiMonthly
123 * @return next processing date
124 */
125 protected Date calculateNextSemiMonthlyDate(String dayOfSemiMonthly, Date currentDate) {
126 Calendar calendar = Calendar.getInstance();
127 calendar.setTime(currentDate);
128
129 int dayOfMonthToSet = Integer.parseInt(dayOfSemiMonthly);
130 int dayOfMonthNextToSet = dayOfMonthToSet + 15;
131
132 calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthToSet);
133
134 if (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
135 calendar.add(Calendar.DAY_OF_MONTH, dayOfMonthNextToSet);
136 }
137 if (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
138 calendar.set(Calendar.DAY_OF_MONTH, dayOfMonthToSet);
139 calendar.add(Calendar.MONTH, 1);
140 }
141
142 return new java.sql.Date(calendar.getTimeInMillis());
143 }
144
145 /**
146 * Method to calculate the next processing monthly date based on the frequency type
147 * Sets the day in the calendar based on day part of the frequency code.
148 * @param dayOfMonth
149 * @return next processing date
150 */
151 protected Date calculateNextMonthlyDate(String dayOfMonth, Date currentDate) {
152 int dayInMonthToSet;
153
154 Calendar calendar = Calendar.getInstance();
155 calendar.setTime(currentDate);
156 setCalendarWithDays(calendar, dayOfMonth);
157 while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
158 calendar.add(Calendar.MONTH, 1);
159 }
160
161 return new java.sql.Date(calendar.getTimeInMillis());
162 }
163
164 /**
165 * Method to calculate the next processing quarterly or semi-annually or annually date based on the frequency type
166 * Sets the day in the calendar based on day part of the frequency code.
167 * @param frequencyType frequency code for quarterly, month, dayOfMonth
168 * @return next processing date
169 */
170 protected Date calculateNextQuarterlyOrSemiAnnuallyOrAnnuallyProcessDate(String month, String dayOfMonth, String frequencyType, Date currentDate) {
171 Calendar calendar = Calendar.getInstance();
172 calendar.setTime(currentDate);
173 calendar = setCaledarWithMonth(month, currentDate);
174 setCalendarWithDays(calendar, dayOfMonth);
175
176 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.QUARTERLY)) {
177 while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
178 calendar.add(Calendar.MONTH, 3);
179 setCalendarWithDays(calendar, dayOfMonth);
180 }
181 }
182 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.SEMI_ANNUALLY)) {
183 while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
184 calendar.add(Calendar.MONTH, 6);
185 setCalendarWithDays(calendar, dayOfMonth);
186 }
187 }
188 if (frequencyType.equalsIgnoreCase(EndowConstants.FrequencyTypes.ANNUALLY)) {
189 while (new java.sql.Date(calendar.getTimeInMillis()).before(currentDate)) {
190 calendar.add(Calendar.MONTH, 12);
191 setCalendarWithDays(calendar, dayOfMonth);
192 }
193 }
194
195 return new java.sql.Date(calendar.getTimeInMillis());
196 }
197
198 /**
199 * This method will check the current month and set the calendar to that month
200 * @param month month to set the calendar
201 * @return calendar calendar is set to the month selected
202 */
203 protected Calendar setCaledarWithMonth(String month, Date currentDate) {
204 Calendar calendar = Calendar.getInstance();
205 calendar.setTime(currentDate);
206 int calendarMonth = 1;
207
208 if (EndowConstants.FrequencyMonths.JANUARY.equalsIgnoreCase(month)) {
209 calendarMonth = Calendar.JANUARY;
210 } else if (EndowConstants.FrequencyMonths.FEBRUARY.equalsIgnoreCase(month)) {
211 calendarMonth = Calendar.FEBRUARY;
212 } else if (EndowConstants.FrequencyMonths.MARCH.equalsIgnoreCase(month)) {
213 calendarMonth = Calendar.MARCH;
214 } else if (EndowConstants.FrequencyMonths.APRIL.equalsIgnoreCase(month)) {
215 calendarMonth = Calendar.APRIL;
216 } else if (EndowConstants.FrequencyMonths.MAY.equalsIgnoreCase(month)) {
217 calendarMonth = Calendar.MAY;
218 } else if (EndowConstants.FrequencyMonths.JUNE.equalsIgnoreCase(month)) {
219 calendarMonth = Calendar.JUNE;
220 } else if (EndowConstants.FrequencyMonths.JULY.equalsIgnoreCase(month)) {
221 calendarMonth = Calendar.JULY;
222 } else if (EndowConstants.FrequencyMonths.AUGUST.equalsIgnoreCase(month)) {
223 calendarMonth = Calendar.AUGUST;
224 } else if (EndowConstants.FrequencyMonths.SEPTEMBER.equalsIgnoreCase(month)) {
225 calendarMonth = Calendar.SEPTEMBER;
226 } else if (EndowConstants.FrequencyMonths.OCTOBER.equalsIgnoreCase(month)) {
227 calendarMonth = Calendar.OCTOBER;
228 } else if (EndowConstants.FrequencyMonths.NOVEMBER.equalsIgnoreCase(month)) {
229 calendarMonth = Calendar.NOVEMBER;
230 } else if (EndowConstants.FrequencyMonths.DECEMBER.equalsIgnoreCase(month)) {
231 calendarMonth = Calendar.DECEMBER;
232 }
233
234 calendar.set(Calendar.MONTH, calendarMonth);
235
236 return calendar;
237 }
238
239 /**
240 * This method will check the current month and set the calendar to that month
241 * @param month, dayOfMonth month to set the calendar, dayOfMonth day of the month to set to
242 * @return calendar calendar is set to the month selected
243 */
244 protected void setCalendarWithDays(Calendar calendar, String dayOfMonth) {
245 int dayInMonthToSet;
246 int calendarMonth = calendar.get(Calendar.MONTH);
247
248 if (StringUtils.equalsIgnoreCase(dayOfMonth, EndowConstants.FrequencyMonthly.MONTH_END)) { // month end for the month so need to get max days...
249 dayInMonthToSet = checkMaximumDaysInMonth(calendar.get(Calendar.MONTH));
250 } else {
251 dayInMonthToSet = Integer.parseInt(dayOfMonth);
252
253 if (dayInMonthToSet > 29 && calendarMonth == Calendar.FEBRUARY) {
254 dayInMonthToSet = checkMaximumDaysInFebruary();
255 } else if (dayInMonthToSet > 30 && (calendarMonth == Calendar.APRIL || calendarMonth == Calendar.JUNE ||
256 calendarMonth == Calendar.SEPTEMBER || calendarMonth == Calendar.NOVEMBER)) {
257 dayInMonthToSet = 30;
258 dayInMonthToSet = checkMaximumDaysInMonth(calendarMonth);
259 }
260 }
261
262 calendar.set(Calendar.DAY_OF_MONTH, dayInMonthToSet);
263 }
264
265 /**
266 * This method will check and return either maximum days in the month as 28 or 29 for leap year.
267 * It first sets the month to February and then checks the maximum days..
268 * @return maxDays Maximum number of days in the month of February for calendar.
269 */
270 protected int checkMaximumDaysInFebruary() {
271 int maxDays;
272 Calendar februaryMonthlyDateCalendar = Calendar.getInstance();
273 februaryMonthlyDateCalendar.set(Calendar.MONTH, Calendar.FEBRUARY);
274 maxDays = februaryMonthlyDateCalendar.getActualMaximum(Calendar.DAY_OF_MONTH);
275
276 return maxDays;
277 }
278
279 /**
280 * This method will check and return maximum days in a month.
281 * @param monthNumber The number of the month to test for maximum days..
282 * @return maxDays Maximum number of days in the month of February for calendar.
283 */
284 protected int checkMaximumDaysInMonth(int monthNumber) {
285 int maxDays;
286
287 Calendar calendar = Calendar.getInstance();
288 calendar.set(Calendar.MONTH, monthNumber);
289 maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
290
291 return maxDays;
292 }
293
294 public void setDateTimeService(DateTimeService dateTimeService) {
295 this.dateTimeService = dateTimeService;
296 }
297
298 protected DateTimeService getDateTimeService() {
299 return dateTimeService;
300 }
301
302 protected KEMService getKemService() {
303 return kemService;
304 }
305
306 public void setKemService(KEMService kemService) {
307 this.kemService = kemService;
308 }
309 }