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 }