View Javadoc
1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.kpme.tklm.time.detail.validation;
17  
18  import java.math.BigDecimal;
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.apache.commons.collections.CollectionUtils;
23  import org.apache.commons.lang.StringUtils;
24  import org.joda.time.*;
25  import org.kuali.kpme.core.assignment.Assignment;
26  import org.kuali.kpme.core.assignment.AssignmentDescriptionKey;
27  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
28  import org.kuali.kpme.core.earncode.EarnCode;
29  import org.kuali.kpme.core.job.Job;
30  import org.kuali.kpme.core.service.HrServiceLocator;
31  import org.kuali.kpme.core.util.HrConstants;
32  import org.kuali.kpme.core.util.HrContext;
33  import org.kuali.kpme.core.util.TKUtils;
34  import org.kuali.kpme.core.util.ValidationUtils;
35  import org.kuali.kpme.tklm.common.CalendarValidationUtil;
36  import org.kuali.kpme.tklm.common.TkConstants;
37  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
38  import org.kuali.kpme.tklm.leave.calendar.validation.LeaveCalendarValidationUtil;
39  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
40  import org.kuali.kpme.tklm.leave.summary.LeaveSummary;
41  import org.kuali.kpme.tklm.time.clocklog.ClockLog;
42  import org.kuali.kpme.tklm.time.detail.web.TimeDetailActionFormBase;
43  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
44  import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
45  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
46  import org.kuali.rice.krad.util.ObjectUtils;
47  
48  public class TimeDetailValidationUtil extends CalendarValidationUtil {
49  
50      public static List<String> validateLeaveEntry(TimeDetailActionFormBase tdaf) throws Exception {
51      	// This validator could be shared between LeaveCalendarValidationUtil and TimeDetailValidationUtil
52      	// if the common parameters required by both are moved to a shared parent of TimeDetailActionFormBase and LeaveCalendarForm.
53      	List<String> errorMsgList = new ArrayList<String>();
54      	CalendarEntry payCalendarEntry = tdaf.getCalendarEntry();
55      	if(ObjectUtils.isNotNull(payCalendarEntry)) {
56  			LeaveBlock lb = null;
57  			if(StringUtils.isNotEmpty(tdaf.getLmLeaveBlockId())) {
58  				lb = LmServiceLocator.getLeaveBlockService().getLeaveBlock(tdaf.getLmLeaveBlockId());
59  			}
60  			errorMsgList.addAll(CalendarValidationUtil.validateEarnCode(tdaf.getSelectedEarnCode(),tdaf.getStartDate(),tdaf.getEndDate()));
61  			if(errorMsgList.isEmpty()) {
62  				LeaveSummary ls = LmServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDate(HrContext.getTargetPrincipalId(), TKUtils.formatDateString(tdaf.getEndDate()));
63  				
64  				  BigDecimal leaveAmount = tdaf.getLeaveAmount();
65                    if(leaveAmount == null) {
66  	                  Long startTime = TKUtils.convertDateStringToDateTimeWithoutZone(tdaf.getStartDate(), tdaf.getStartTime()).getMillis();
67  	                  Long endTime = TKUtils.convertDateStringToDateTimeWithoutZone(tdaf.getEndDate(), tdaf.getEndTime()).getMillis();
68  	                  leaveAmount = TKUtils.getHoursBetween(startTime, endTime);
69                    }
70  				
71  				// Validate LeaveBlock timings and all that
72  				/**
73  				 * In all cases, TIME, AMOUNT, HOUR, DAY, we should validate usage and balance limits. But depending on earn code record method, the derivation of requested
74  				 * leave amount varies. It can be derived from endTime - startTime, or vary by units.
75  				 * 
76  				 * TimeDetailValidationUtil.validateLeaveParametersByEarnCodeRecordMethod offers a form of organization for these validations.
77  				 */
78  				errorMsgList.addAll(TimeDetailValidationUtil.validateLeaveParametersByEarnCodeRecordMethod(tdaf));
79  				errorMsgList.addAll(LeaveCalendarValidationUtil.validateAvailableLeaveBalanceForUsage(tdaf.getSelectedEarnCode(), 
80  						tdaf.getStartDate(), tdaf.getEndDate(), leaveAmount, lb));
81  				//Validate leave block does not exceed max usage. Leave Calendar Validators at this point rely on a leave summary.
82  		        errorMsgList.addAll(LeaveCalendarValidationUtil.validateLeaveAccrualRuleMaxUsage(ls, tdaf.getSelectedEarnCode(),
83  		                tdaf.getStartDate(), tdaf.getEndDate(), leaveAmount, lb));
84  		        errorMsgList.addAll(LeaveCalendarValidationUtil.validateHoursUnderTwentyFour(tdaf.getSelectedEarnCode(),
85  		        		tdaf.getStartDate(), tdaf.getEndDate(),leaveAmount));
86  			}
87  		}
88  		return errorMsgList;
89      }
90      
91      public static List<String> validateLeaveParametersByEarnCodeRecordMethod(TimeDetailActionFormBase lcf) {
92      	List<String> errors = new ArrayList<String>();
93      	if (StringUtils.isNotBlank(lcf.getSelectedEarnCode()) &&  lcf.getCalendarEntry() != null) {
94      		//earn code is validate through the span of the leave entry, could the earn code's record method change between then and the leave period end date?
95      		//Why not use endDateS to retrieve the earn code?
96      		CalendarEntry calendarEntry = lcf.getCalendarEntry();
97      		EarnCode earnCode = HrServiceLocator.getEarnCodeService().getEarnCode(lcf.getSelectedEarnCode(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
98      		if(earnCode != null) {
99      			if(earnCode.getRecordMethod().equalsIgnoreCase(HrConstants.EARN_CODE_TIME)) {
100     		    	return LeaveCalendarValidationUtil.validateTimeParametersForLeaveEntry(earnCode, lcf.getCalendarEntry(), lcf.getStartDate(), lcf.getEndDate(), lcf.getStartTime(), lcf.getEndTime(), lcf.getSelectedAssignment(), lcf.getLmLeaveBlockId(), null);
101     			}
102     			// we should not have any leave earn codes with amount recording method
103 //    			else if (earnCode.getRecordMethod().equalsIgnoreCase(HrConstants.EARN_CODE_AMOUNT)) {
104 //    		    	return validateAmountParametersForLeaveEntry(earnCode, lcf.getCalendarEntry(), lcf.getStartDate(), lcf.getEndDate(), lcf.getSelectedAssignment(), lcf.getLmLeaveBlockId());
105 //    			}
106     			else if (earnCode.getRecordMethod().equalsIgnoreCase(HrConstants.EARN_CODE_HOUR)) {
107     		    	return validateHourParametersForLeaveEntry(earnCode, lcf.getCalendarEntry(), lcf.getStartDate(), lcf.getEndDate(), lcf.getLeaveAmount());
108     			}
109     			else if (earnCode.getRecordMethod().equalsIgnoreCase(HrConstants.EARN_CODE_DAY)) {
110     		    	return validateDayParametersForLeaveEntry(earnCode, lcf.getCalendarEntry(), lcf.getStartDate(), lcf.getEndDate(), lcf.getLeaveAmount());
111     			}
112     		}
113     	}
114     	return errors;
115     }
116 	 /**
117      * Validate the earn code exists on every day within the date rage
118      * @param earnCode
119      * @param startDateString
120      * @param endDateString
121      *
122      * @return A list of error strings.
123      */
124     @Deprecated
125     public static List<String> validateEarnCode(String earnCode, String startDateString, String endDateString) {
126     	List<String> errors = new ArrayList<String>();
127 
128     	LocalDate tempDate = TKUtils.formatDateTimeStringNoTimezone(startDateString).toLocalDate();
129     	LocalDate localEnd = TKUtils.formatDateTimeStringNoTimezone(endDateString).toLocalDate();
130 		// tempDate and localEnd could be the same day
131     	while(!localEnd.isBefore(tempDate)) {
132     		if(!ValidationUtils.validateEarnCode(earnCode, tempDate)) {
133     			 errors.add("Earn Code " + earnCode + " is not available for " + tempDate);
134     			 break;
135     		}
136     		tempDate = tempDate.plusDays(1);
137     	}
138     	
139     	return errors;
140     }
141 	
142     /**
143      * Convenience method for handling validation directly from the form object.
144      * @param tdaf The populated form.
145      *
146      * @return A list of error strings.
147      */
148     public static List<String> validateTimeEntryDetails(TimeDetailActionFormBase tdaf) {
149     	boolean spanningWeeks = false;
150     	boolean acrossDays = false;
151     	
152     	if(tdaf.getAcrossDays() != null) {
153     		acrossDays = tdaf.getAcrossDays().equalsIgnoreCase("y");
154     	}
155     	
156         return validateTimeEntryDetails(
157                 tdaf.getHours(), tdaf.getAmount(), tdaf.getStartTime(), tdaf.getEndTime(),
158                 tdaf.getStartDate(), tdaf.getEndDate(), tdaf.getTimesheetDocument(),
159                 tdaf.getSelectedEarnCode(), tdaf.getSelectedAssignment(),
160                 acrossDays, tdaf.getTkTimeBlockId(), tdaf.getOvertimePref()
161         );
162     }
163 
164     public static List<String> validateTimeEntryDetails(BigDecimal hours, BigDecimal amount, String startTimeS, String endTimeS, String startDateS, String endDateS, TimesheetDocument timesheetDocument, String selectedEarnCode, String selectedAssignment, boolean acrossDays, String timeblockId, String overtimePref) {
165         List<String> errors = new ArrayList<String>();
166 
167         if (timesheetDocument == null) {
168             errors.add("No timesheet document found.");
169         }
170         if (errors.size() > 0) return errors;
171         
172         CalendarEntry payCalEntry = timesheetDocument.getCalendarEntry();
173         EarnCode earnCode = null;
174         if (StringUtils.isNotBlank(selectedEarnCode)) {
175             earnCode = HrServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, TKUtils.formatDateTimeStringNoTimezone(endDateS).toLocalDate());
176         }
177         boolean isTimeRecordMethod = earnCode != null && StringUtils.equalsIgnoreCase(earnCode.getRecordMethod(), HrConstants.EARN_CODE_TIME);
178 
179         errors.addAll(CalendarValidationUtil.validateDates(startDateS, endDateS));
180 
181         if (isTimeRecordMethod) {
182             errors.addAll(CalendarValidationUtil.validateTimes(startTimeS, endTimeS));
183         }
184         if (errors.size() > 0) return errors;
185 
186         Long startTime;
187         Long endTime;
188 
189         if (!isTimeRecordMethod) {
190             startTimeS = "0:0";
191             endTimeS = "0:0";
192         }
193 
194         if (acrossDays && !endTimeS.equals("0:00")) { endDateS = startDateS;}
195 
196         startTime = TKUtils.convertDateStringToDateTimeWithoutZone(startDateS, startTimeS).getMillis();
197         endTime = TKUtils.convertDateStringToDateTimeWithoutZone(endDateS, endTimeS).getMillis();
198 
199         errors.addAll(CalendarValidationUtil.validateInterval(payCalEntry, startTime, endTime));
200         if (errors.size() > 0) return errors;
201 
202         if (isTimeRecordMethod) {
203             if (startTimeS == null) errors.add("The start time is blank.");
204             if (endTimeS == null) errors.add("The end time is blank.");
205             if (startTime - endTime == 0) errors.add("Start time and end time cannot be equivalent");
206         }
207         if (errors.size() > 0) return errors;
208 
209         DateTime startTemp = new DateTime(startTime);
210         DateTime endTemp = new DateTime(endTime);
211 /*
212  * KPME-2687:
213  *
214  * Removed 24 hour limitation. System creates continuous sequence of time blocks when !accrossDays,
215  * hours between startTemp and endTemp may be over 24 hours.
216  *
217         if (errors.size() == 0 && !acrossDays && !StringUtils.equals(TkConstants.EARN_CODE_CPE, overtimePref)) {
218             Hours hrs = Hours.hoursBetween(startTemp, endTemp);
219             if (hrs.getHours() > 24) errors.add("One timeblock cannot exceed 24 hours");
220         }
221         if (errors.size() > 0) return errors;
222 
223  */
224 
225         //Check that assignment is valid within the timeblock span. 
226         AssignmentDescriptionKey assignKey = HrServiceLocator.getAssignmentService().getAssignmentDescriptionKey(selectedAssignment);
227         Assignment assign = HrServiceLocator.getAssignmentService().getAssignmentForTargetPrincipal(assignKey, startTemp.toLocalDate());
228         if (assign == null) errors.add("Assignment is not valid for start date " + TKUtils.formatDate(new LocalDate(startTime)));
229         assign = HrServiceLocator.getAssignmentService().getAssignmentForTargetPrincipal(assignKey, endTemp.toLocalDate());
230         if (assign == null) errors.add("Assignment is not valid for end date " + TKUtils.formatDate(new LocalDate(endTime)));
231         if (errors.size() > 0) return errors;
232 
233         //------------------------
234         // some of the simple validations are in the js side in order to reduce the server calls
235         // 1. check if the begin / end time is empty - tk.calenadr.js
236         // 2. check the time format - timeparse.js
237         // 3. only allows decimals to be entered in the hour field
238         //------------------------
239 
240         //------------------------
241         // check if the begin / end time are valid
242         //------------------------
243         if ((startTime.compareTo(endTime) > 0 || endTime.compareTo(startTime) < 0)) {
244             errors.add("The time or date is not valid.");
245         }
246         if (errors.size() > 0) return errors;
247         
248         // KPME-1446 
249         // -------------------------------
250         // check if there is a weekend day when the include weekends flag is checked
251         //--------------------------------
252        
253         if (errors.size() > 0) return errors;
254 
255         //------------------------
256         // check if the overnight shift is across days
257         //------------------------
258         if (acrossDays && hours == null && amount == null) {
259         	if (startTemp.getHourOfDay() > endTemp.getHourOfDay()
260                     && !(endTemp.getDayOfYear() - startTemp.getDayOfYear() <= 1
261                     && endTemp.getHourOfDay() == 0)) {
262                 errors.add("The \"apply to each day\" box should not be checked.");
263             }
264         }
265         if (errors.size() > 0) return errors;
266 
267         //------------------------
268         // Amount cannot be zero
269         //------------------------
270         if (amount != null && earnCode != null && StringUtils.equals(earnCode.getEarnCodeType(), HrConstants.EARN_CODE_AMOUNT)) {
271             if (amount.equals(BigDecimal.ZERO)) {
272                 errors.add("Amount cannot be zero.");
273             }
274             if (amount.scale() > 2) {
275                 errors.add("Amount cannot have more than two digits after decimal point.");
276             }
277         }
278         if (errors.size() > 0) return errors;
279 
280         //------------------------
281         // check if the hours entered for hourly earn code is greater than 24 hours per day
282         // Hours cannot be zero
283         //------------------------
284         if (hours != null && earnCode != null && StringUtils.equals(earnCode.getEarnCodeType(), HrConstants.EARN_CODE_HOUR)) {
285             if (hours.equals(BigDecimal.ZERO)) {
286                 errors.add("Hours cannot be zero.");
287             }
288             if (hours.scale() > 2) {
289                 errors.add("Hours cannot have more than two digits after decimal point.");
290             }
291 /*
292  * KPME-2671:
293  * 
294  * Replacing this conditional with the one below. Shouldn't matter if the date range spans more than one day,
295  * hours shouldn't exceed 24.
296  * 
297  *          int dayDiff = endTemp.getDayOfYear() - startTemp.getDayOfYear() + 1;
298             if (hours.compareTo(new BigDecimal(dayDiff * 24)) == 1) {
299                 errors.add("Cannot enter more than 24 hours per day.");
300             }
301  */
302         }
303         if (errors.size() > 0) return errors;
304 
305         /**
306          * KPME-2671:
307          * 
308          * Generalize 24 limit to hour field on time entry form.
309          * 
310          */
311     	if(hours != null && hours.compareTo(new BigDecimal(24.0)) > 0) {
312     		errors.add("Hours cannot exceed 24.");
313     	}
314     	//------------------------
315         // check if time blocks overlap with each other. Note that the tkTimeBlockId is used to
316         // determine is it's updating an existing time block or adding a new one
317         //------------------------
318 
319         boolean isRegularEarnCode = StringUtils.equals(assign.getJob().getPayTypeObj().getRegEarnCode(),selectedEarnCode);
320         startTime = TKUtils.convertDateStringToDateTime(startDateS, startTimeS).getMillis();
321         endTime = TKUtils.convertDateStringToDateTime(endDateS, endTimeS).getMillis();
322         
323         errors.addAll(validateOverlap(startTime, endTime, acrossDays, startDateS, endTimeS,startTemp, endTemp, timesheetDocument, timeblockId, isRegularEarnCode, selectedEarnCode));
324         if (errors.size() > 0) return errors;
325 
326         // Accrual Hour Limits Validation
327         //errors.addAll(TkServiceLocator.getTimeOffAccrualService().validateAccrualHoursLimitByEarnCode(timesheetDocument, selectedEarnCode));
328 
329         return errors;
330     }
331 
332     public static List<String> validateOverlap(Long startTime, Long endTime, boolean acrossDays, String startDateS, String endTimeS, DateTime startTemp, DateTime endTemp, TimesheetDocument timesheetDocument, String timeblockId, boolean isRegularEarnCode, String selectedEarnCode) {
333         List<String> errors = new ArrayList<String>();
334         Interval addedTimeblockInterval = new Interval(startTime, endTime);
335         List<Interval> dayInt = new ArrayList<Interval>();
336 
337         //if the user is clocked in, check if this time block overlaps with the clock action
338         ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog(HrContext.getTargetPrincipalId());
339         if(lastClockLog != null &&
340         		(lastClockLog.getClockAction().equals(TkConstants.CLOCK_IN) 
341         				|| lastClockLog.getClockAction().equals(TkConstants.LUNCH_IN))) {
342         	 DateTime lastClockDateTime = lastClockLog.getClockDateTime();
343              //String lastClockZone = lastClockLog.getClockTimestampTimezone();
344              //if (StringUtils.isEmpty(lastClockZone)) {
345              //    lastClockZone = TKUtils.getSystemTimeZone();
346              //}
347              //DateTimeZone zone = DateTimeZone.forID(lastClockZone);
348              //DateTime clockWithZone = lastClockDateTime.withZone(zone);
349              //DateTime currentTime = new DateTime(System.currentTimeMillis(), zone);
350             DateTime currentTime = DateTime.now();
351             if (lastClockDateTime.getMillis() > currentTime.getMillis()) {
352                 currentTime = new DateTime(lastClockDateTime.getMillis());
353             }
354             
355             Interval currentClockInInterval = new Interval(lastClockDateTime, currentTime);
356             if (isRegularEarnCode && addedTimeblockInterval.overlaps(currentClockInInterval)) {
357                  errors.add("The time block you are trying to add overlaps with the current clock action.");
358                  return errors;
359              }
360         }
361        
362         if (acrossDays) {
363         	DateTime start = new DateTime(startTime);
364         	// KPME-2720
365         	// The line below is necessary because "end" needs to have start date to construct the right end datetime to 
366         	// create an interval.  For example, if user selects from 8a 8/19 to 10a 8/21 and checks across days check box, 
367         	// startTime would have 8a 8/19 and endTime would have 10a 8/21.  With the line below, "end" would have 10a 8/19, and
368         	// an interval from 8a 8/19 to 10a 8/19 would be created.  However, since it was using convertDateStringToDateTime,
369         	// which takes into account user time zone, "end" was off.  Use convertDateStringToDateTimeWithoutZone isntead
370         	// so that "end" would have the right time in default time zone.
371         	//        	DateTime end = TKUtils.convertDateStringToDateTimeWithoutZone(startDateS, endTimeS);
372         	
373         	// kpme-3181
374         	// revert the following line of change from kpme-2720 since it's using timezone for starttime but not endtime
375         	// that's causing interval not being build correctly issue
376             DateTime end = TKUtils.convertDateStringToDateTime(startDateS, endTimeS);
377         	
378         	if (endTemp.getDayOfYear() - startTemp.getDayOfYear() < 1) {
379                 end = new DateTime(endTime);
380             }
381             DateTime groupEnd = new DateTime(endTime);
382             Long startLong = start.getMillis();
383             Long endLong = end.getMillis();
384             //create interval span if start is before the end and the end is after the start except
385             //for when the end is midnight ..that converts to midnight of next day
386             DateMidnight midNight = new DateMidnight(endLong);
387             while (start.isBefore(groupEnd.getMillis()) && ((endLong >= startLong) || end.isEqual(midNight))) {
388                 Interval tempInt = null;
389                 if (end.isEqual(midNight)) {
390                     tempInt = addedTimeblockInterval;
391                 } else {
392                     tempInt = new Interval(startLong, endLong);
393                 }
394                 dayInt.add(tempInt);
395                 start = start.plusDays(1);
396                 end = end.plusDays(1);
397                 startLong = start.getMillis();
398                 endLong = end.getMillis();
399             }
400         } else {
401             dayInt.add(addedTimeblockInterval);
402         }
403 
404         for (TimeBlock timeBlock : timesheetDocument.getTimeBlocks()) {
405             if (errors.size() == 0 && StringUtils.equals(timeBlock.getEarnCodeType(), HrConstants.EARN_CODE_TIME)) {
406             	// allow regular time blocks to be added with overlapping non-regular time blocks
407             	Job aJob = HrServiceLocator.getJobService().getJob(timeBlock.getPrincipalId(), timeBlock.getJobNumber(), new LocalDate(timeBlock.getBeginDate()));
408             	if(aJob != null && aJob.getPayTypeObj() != null && isRegularEarnCode && !StringUtils.equals(aJob.getPayTypeObj().getRegEarnCode(),timeBlock.getEarnCode())) {
409             		continue;
410             	}
411             	
412                 Interval timeBlockInterval = new Interval(timeBlock.getBeginTimestamp().getTime(), timeBlock.getEndTimestamp().getTime());
413                 for (Interval intv : dayInt) {
414                 	// KPME-2720
415                 	// timeblockInterval above seems to have the server timezone (America/New York), for example, if you log in as iadetail1 (America/chicago) 
416                 	// and see a timeblock from 8a to 10a on the screen, that time block is actually stored as 9a to 11a in the table because 
417                 	// there is an hour difference.  So, timeblockInterval intervale above is 9a to 11a in America/New York timezone.  
418                 	// However, intv interval seems to have local time with the server timezone.  For example, if you create a timeblock from 10a to 12p
419                 	// as iadetail1, intv interval is 10a to 12p in America/New York timezone.  This is why it was giving the error below because it was
420                 	// comparing a time block with 9a-11a to a time block with 10a-12p (overlapping).  To fix this, we will create
421                 	// an interval with the right time in the server timezone.
422 //                    String start_datetime = TKUtils.formatDateTimeLong(intv.getStart());
423 //                	String start_date = TKUtils.formatDateTimeShort(intv.getStart());
424 //                	String start_time = TKUtils.formatTimeShort(start_datetime);
425 //                	String end_datetime = TKUtils.formatDateTimeLong(intv.getEnd());                	
426 //                	String end_date = TKUtils.formatDateTimeShort(intv.getEnd());
427 //                	String end_time =  TKUtils.formatTimeShort(end_datetime);
428                 	DateTime start_dt_timezone = new DateTime(startTime);
429                 	DateTime end_dt_timezone = new DateTime(endTime);
430                 	Interval converted_intv = new Interval(start_dt_timezone.getMillis(), end_dt_timezone.getMillis()); // interval with start/end datetime in server timezone
431                     List<Interval> intervals = new ArrayList<Interval>();
432                     if (acrossDays) {
433                         List<LocalDate> localDates = new ArrayList<LocalDate>();
434                         LocalDate startDay = new LocalDate(start_dt_timezone);
435                         DateTimeZone userTimeZone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone(timesheetDocument.getPrincipalId()));
436                         if (userTimeZone==null) {
437                             userTimeZone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
438                         }
439 
440 
441                         int days = end_dt_timezone.withZone(userTimeZone).toLocalTime().equals(new LocalTime(0,0,0)) ? Days.daysBetween(startDay, new LocalDate(end_dt_timezone)).getDays() : Days.daysBetween(startDay, new LocalDate(end_dt_timezone)).getDays()+1;
442                         for (int i=0; i < days; i++) {
443                             LocalDate d = startDay.withFieldAdded(DurationFieldType.days(), i);
444                             localDates.add(d);
445                         }
446                         for (LocalDate localDate : localDates) {
447                             DateTime startDateTime = localDate.toDateTime(start_dt_timezone.toLocalTime());
448                             DateTime endDateTime = localDate.toDateTime(end_dt_timezone.toLocalTime());
449                             endDateTime = endDateTime.isBefore(startDateTime) ? endDateTime.plusDays(1) : endDateTime;
450 
451                             intervals.add(new Interval(startDateTime,endDateTime));
452                         }
453 
454                     } else {
455                         intervals.add(converted_intv);
456                     }
457 
458                     for (Interval interval : intervals) {
459                         if (isRegularEarnCode && timeBlockInterval.overlaps(interval) && (timeblockId == null || timeblockId.compareTo(timeBlock.getTkTimeBlockId()) != 0)) {
460                         	errors.add("The time block you are trying to add overlaps with an existing time block.");
461                             return errors;
462                         }else if(timeBlock.getEarnCode().equals(selectedEarnCode) && timeBlockInterval.overlaps(interval) && (timeblockId == null || timeblockId.compareTo(timeBlock.getTkTimeBlockId()) != 0)){
463                         	errors.add("The time block you are trying to add overlaps with an existing time block.");
464                         	return errors;
465                         }
466                     }
467                 }
468             }
469         }
470 
471         return errors;
472     }
473     
474     /*
475      * Moving to CalendarValidationUtil
476      * @param startDateS
477      * @param endDateS
478      * @return
479      */
480     @Deprecated
481     public static List<String> validateDates(String startDateS, String endDateS) {
482         List<String> errors = new ArrayList<String>();
483         if (errors.size() == 0 && StringUtils.isEmpty(startDateS)) errors.add("The start date is blank.");
484         if (errors.size() == 0 && StringUtils.isEmpty(endDateS)) errors.add("The end date is blank.");
485         return errors;
486     }
487     
488     /*
489      * Moving to CalendarValidationUtil
490      * @param startTimeS
491      * @param endTimeS
492      * @return
493      */
494     @Deprecated
495     public static List<String> validateTimes(String startTimeS, String endTimeS) {
496         List<String> errors = new ArrayList<String>();
497         if (errors.size() == 0 && startTimeS == null) errors.add("The start time is blank.");
498         if (errors.size() == 0 && endTimeS == null) errors.add("The end time is blank.");
499         return errors;
500     }
501     
502     /*
503      * Moving to CalendarValidationUtil
504      * @param payCalEntry
505      * @param startTime
506      * @param endTime
507      * @return
508      */
509     @Deprecated
510     public static List<String> validateInterval(CalendarEntry payCalEntry, Long startTime, Long endTime) {
511         List<String> errors = new ArrayList<String>();
512         LocalDateTime pcb_ldt = payCalEntry.getBeginPeriodLocalDateTime();
513         LocalDateTime pce_ldt = payCalEntry.getEndPeriodLocalDateTime();
514 
515         DateTime p_cal_b_dt = pcb_ldt.toDateTime();
516         DateTime p_cal_e_dt = pce_ldt.toDateTime();
517 
518         Interval payInterval = new Interval(p_cal_b_dt, p_cal_e_dt);
519         if (errors.size() == 0 && !payInterval.contains(startTime)) {
520             errors.add("The start date/time is outside the pay period");
521         }
522         if (errors.size() == 0 && !payInterval.contains(endTime) && p_cal_e_dt.getMillis() != endTime) {
523             errors.add("The end date/time is outside the pay period");
524         }
525         return errors;
526     }
527 
528 }