001    /**
002     * Copyright 2004-2012 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.hr.time.util;
017    
018    import java.math.BigDecimal;
019    import java.net.UnknownHostException;
020    import java.sql.Date;
021    import java.sql.Timestamp;
022    import java.text.ParseException;
023    import java.text.SimpleDateFormat;
024    import java.util.*;
025    
026    import javax.servlet.http.HttpServletRequest;
027    
028    import org.apache.commons.lang.StringUtils;
029    import org.apache.log4j.Logger;
030    import org.joda.time.DateTime;
031    import org.joda.time.DateTimeZone;
032    import org.joda.time.Days;
033    import org.joda.time.Interval;
034    import org.joda.time.Period;
035    import org.kuali.hr.location.service.TimezoneKeyValueFinder;
036    import org.kuali.hr.time.assignment.Assignment;
037    import org.kuali.hr.time.calendar.CalendarEntries;
038    import org.kuali.hr.time.service.base.TkServiceLocator;
039    import org.kuali.hr.time.task.Task;
040    import org.kuali.rice.core.api.config.property.ConfigContext;
041    
042    public class TKUtils {
043    
044        private static final Logger LOG = Logger.getLogger(TKUtils.class);
045    
046        public static java.sql.Date getCurrentDate() {
047            return getTimelessDate(null);
048        }
049    
050        /**
051         * @param dateString the format has to be mm/dd/yyyy
052         * @return dayOfMonth
053         */
054        public static String getDayOfMonthFromDateString(String dateString) {
055            String[] date = dateString.split("/");
056            return date[1];
057        }
058    
059        public static String getSystemTimeZone() {
060            String configTimezone = TimeZone.getDefault().getID();
061            if (ConfigContext.getCurrentContextConfig() != null
062                    && StringUtils.isNotBlank(ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ConfigSettings.KPME_SYSTEM_TIMEZONE).trim())) {
063                String tempTimeZoneId = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ConfigSettings.KPME_SYSTEM_TIMEZONE);
064    
065                if (TimeZone.getTimeZone(tempTimeZoneId) != null) {
066                    configTimezone = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ConfigSettings.KPME_SYSTEM_TIMEZONE);
067                } else {
068                    LOG.error("Timezone set by configuration parameter " + TkConstants.ConfigSettings.KPME_SYSTEM_TIMEZONE + " is not a valid time zone id.  Using the systems default time zone instead.");
069                }
070            }
071    
072    
073            return configTimezone;
074        }
075    
076        public static DateTimeZone getSystemDateTimeZone() {
077            return DateTimeZone.forID(TKUtils.getSystemTimeZone());
078        }
079    
080        /**
081         * Returns a enforced timeless version of the provided date, if the date is
082         * null the current date is returned.
083         *
084         * @param date
085         * @return A java.sql.Date version of the provided date, if provided date is
086         *         null, the current date is returned.
087         */
088        public static java.sql.Date getTimelessDate(java.util.Date date) {
089            java.sql.Date jsd = null;
090            if (date == null) {
091                jsd = new java.sql.Date(System.currentTimeMillis());
092            } else {
093                jsd = new java.sql.Date(date.getTime());
094            }
095            return jsd;
096        }
097    
098        public static long getDaysBetween(Calendar startDate, Calendar endDate) {
099            Calendar date = (Calendar) startDate.clone();
100            long daysBetween = 0;
101            while (date.before(endDate)) {
102                date.add(Calendar.DAY_OF_MONTH, 1);
103                daysBetween++;
104            }
105            return daysBetween;
106        }
107    
108        public static long getDaysBetween(java.util.Date startDate, java.util.Date endDate) {
109            Calendar beginCal = GregorianCalendar.getInstance();
110            Calendar endCal = GregorianCalendar.getInstance();
111            beginCal.setTime(startDate);
112            endCal.setTime(endDate);
113    
114            return getDaysBetween(beginCal, endCal);
115        }
116    
117        public static BigDecimal getHoursBetween(long start, long end) {
118            long diff = end - start;
119            BigDecimal hrsReminder = new BigDecimal((diff / 3600000.0) % 24);
120            // if the hours is exact duplicate of 24 hours
121            if (hrsReminder.compareTo(BigDecimal.ZERO) == 0 && diff > 0) {
122                return new BigDecimal(diff / 3600000.0).setScale(TkConstants.BIG_DECIMAL_SCALE, TkConstants.BIG_DECIMAL_SCALE_ROUNDING).abs();
123            }
124            return hrsReminder.setScale(TkConstants.BIG_DECIMAL_SCALE, TkConstants.BIG_DECIMAL_SCALE_ROUNDING).abs();
125        }
126    
127    
128    
129        public static int getNumberOfWeeks(java.util.Date beginDate, java.util.Date endDate) {
130    
131            DateTime beginTime = new DateTime(beginDate);
132            DateTime endTime = new DateTime(endDate);
133    
134            int numOfDays = Days.daysBetween(beginTime, endTime).getDays();
135            int numOfWeeks = numOfDays / 7;
136            if (numOfDays % 7 != 0) {
137                numOfWeeks++;
138            }
139            return numOfWeeks;
140        }
141    
142        public static String formatAssignmentKey(Long jobNumber, Long workArea, Long task) {
143            Long taskLong = task;
144            if (taskLong == null) {
145                taskLong = new Long("0");
146            }
147            return jobNumber + TkConstants.ASSIGNMENT_KEY_DELIMITER + workArea + TkConstants.ASSIGNMENT_KEY_DELIMITER + taskLong;
148        }
149    
150        public static Map<String, String> formatAssignmentDescription(Assignment assignment) {
151            Map<String, String> assignmentDescriptions = new LinkedHashMap<String, String>();
152            Long task = assignment.getTask();
153            if (task == null) {
154                task = new Long("0");
155            }
156            String assignmentDescKey = formatAssignmentKey(assignment.getJobNumber(), assignment.getWorkArea(), task);
157            String assignmentDescValue = getAssignmentString(assignment);
158            assignmentDescriptions.put(assignmentDescKey, assignmentDescValue);
159    
160            return assignmentDescriptions;
161        }
162    
163        public static String getAssignmentString(Assignment assignment) {
164    
165    
166            if (assignment.getWorkAreaObj() == null
167                    || assignment.getJob() == null
168                    || assignment.getJobNumber() == null) {
169                return "";     // getAssignment() of AssignmentService can return an empty assignment
170            }
171            
172           String stringTemp = assignment.getWorkAreaObj().getDescription() + " : $" 
173                                    + assignment.getJob().getCompRate().setScale(TkConstants.BIG_DECIMAL_SCALE) 
174                                    + " Rcd " + assignment.getJobNumber() + " " + assignment.getJob().getDept();
175           if(assignment.getTask()!= null) {
176                    Task aTask = TkServiceLocator.getTaskService().getTask(assignment.getTask(), assignment.getEffectiveDate());
177                    if(aTask != null) {
178                            // do not display task description if the task is the default one
179                            // default task is created in getTask() of TaskService
180                            if(!aTask.getDescription().equals(TkConstants.TASK_DEFAULT_DESP)) {
181                                    stringTemp += " " +  aTask.getDescription();
182                            }
183                    } 
184           }
185           return stringTemp;
186        }
187    
188        /**
189         * Constructs a list of Day Spans for the pay calendar entry provided. You
190         * must also provide a DateTimeZone so that we can create relative boundaries.
191         *
192         * @param calendarEntry
193         * @param timeZone
194         * @return
195         */
196        public static List<Interval> getDaySpanForCalendarEntry(CalendarEntries calendarEntry, DateTimeZone timeZone) {
197            DateTime beginDateTime = calendarEntry.getBeginLocalDateTime().toDateTime(timeZone);
198            DateTime endDateTime = calendarEntry.getEndLocalDateTime().toDateTime(timeZone);
199    
200            List<Interval> dayIntervals = new ArrayList<Interval>();
201    
202            DateTime currDateTime = beginDateTime;
203            while (currDateTime.isBefore(endDateTime)) {
204                DateTime prevDateTime = currDateTime;
205                currDateTime = currDateTime.plusDays(1);
206                Interval daySpan = new Interval(prevDateTime, currDateTime);
207                dayIntervals.add(daySpan);
208            }
209    
210            return dayIntervals;
211        }
212        
213    
214        /**
215         * Includes partial weeks if the time range provided does not divide evenly
216         * into 7 day spans.
217         *
218         * @param beginDate Starting Date/Time
219         * @param endDate   Ending Date/Time
220         * @return A List of Intervals of 7 day spans. The last Interval in the list
221         *         may be less than seven days.
222         */
223        public static List<Interval> getWeekIntervals(java.util.Date beginDate, java.util.Date endDate) {
224            List<Interval> intervals = new ArrayList<Interval>();
225            DateTime beginTime = new DateTime(beginDate);
226            DateTime endTime = new DateTime(endDate);
227    
228            int dayIncrement = 7;
229            DateTime previous = beginTime;
230            DateTime nextTime = previous.plusDays(dayIncrement);
231            while (nextTime.isBefore(endTime)) {
232                Interval interval = new Interval(previous, nextTime);
233                intervals.add(interval);
234                previous = nextTime;
235                nextTime = previous.plusDays(dayIncrement);
236            }
237    
238            if (previous.isBefore(endTime)) {
239                // add a partial week.
240                Interval interval = new Interval(previous, endTime);
241                intervals.add(interval);
242            }
243    
244            return intervals;
245        }
246    
247        public static long convertHoursToMillis(BigDecimal hours) {
248            return hours.multiply(TkConstants.BIG_DECIMAL_MS_IN_H, TkConstants.MATH_CONTEXT).longValue();
249        }
250    
251        public static BigDecimal convertMillisToHours(long millis) {
252            return (new BigDecimal(millis)).divide(TkConstants.BIG_DECIMAL_MS_IN_H, TkConstants.MATH_CONTEXT);
253        }
254    
255        public static BigDecimal convertMillisToDays(long millis) {
256            BigDecimal hrs = convertMillisToHours(millis);
257            return hrs.divide(TkConstants.BIG_DECIMAL_HRS_IN_DAY, TkConstants.MATH_CONTEXT);
258        }
259    
260        public static BigDecimal convertMinutesToHours(BigDecimal minutes) {
261            return minutes.divide(TkConstants.BIG_DECIMAL_60, TkConstants.MATH_CONTEXT);
262        }
263    
264        public static int convertMillisToWholeDays(long millis) {
265            BigDecimal days = convertMillisToDays(millis);
266            return Integer.parseInt(days.setScale(0, BigDecimal.ROUND_UP).toString());
267        }
268    
269        /*
270          * Compares and confirms if the start of the day is at midnight or on a virtual day boundary
271          * returns true if at midnight false otherwise(assuming 24 hr days)
272          */
273        public static boolean isVirtualWorkDay(Calendar payCalendarStartTime) {
274            return (payCalendarStartTime.get(Calendar.HOUR_OF_DAY) != 0 || payCalendarStartTime.get(Calendar.MINUTE) != 0
275                    && payCalendarStartTime.get(Calendar.AM_PM) != Calendar.AM);
276        }
277    
278        /**
279         * Creates a Timestamp object using Jodatime as an intermediate data structure
280         * from the provided date and time string. (From the form POST and javascript
281         * formats)
282         *
283         * @param dateStr (the format is 01/01/2011)
284         * @param timeStr (the format is 8:0)
285         * @return Timestamp
286         */
287        public static Timestamp convertDateStringToTimestamp(String dateStr, String timeStr) {
288            // the date/time format is defined in tk.calendar.js. For now, the format is 11/17/2010 8:0
289            String[] date = dateStr.split("/");
290            String[] time = timeStr.split(":");
291    
292            DateTimeZone dtz = DateTimeZone.forID(TkServiceLocator.getTimezoneService().getUserTimezone());
293    
294            // this is from the jodattime javadoc:
295            // DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond, DateTimeZone zone)
296            // Noted that the month value is the actual month which is different than the java date object where the month value is the current month minus 1.
297            // I tried to use the actual month in the code as much as I can to reduce the convertions.
298            DateTime dateTime = new DateTime(
299                    Integer.parseInt(date[2]),
300                    Integer.parseInt(date[0]),
301                    Integer.parseInt(date[1]),
302                    Integer.parseInt(time[0]),
303                    Integer.parseInt(time[1]),
304                    0, 0, dtz);
305    
306            return new Timestamp(dateTime.getMillis());
307        }
308        
309        public static Timestamp convertDateStringToTimestampWithoutZone(String dateStr, String timeStr) {
310            // the date/time format is defined in tk.calendar.js. For now, the format is 11/17/2010 8:0
311            String[] date = dateStr.split("/");
312            String[] time = timeStr.split(":");
313    
314            // this is from the jodattime javadoc:
315            // DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond, DateTimeZone zone)
316            // Noted that the month value is the actual month which is different than the java date object where the month value is the current month minus 1.
317            // I tried to use the actual month in the code as much as I can to reduce the convertions.
318            DateTime dateTime = new DateTime(
319                    Integer.parseInt(date[2]),
320                    Integer.parseInt(date[0]),
321                    Integer.parseInt(date[1]),
322                    Integer.parseInt(time[0]),
323                    Integer.parseInt(time[1]),
324                    0, 0);
325    
326            return new Timestamp(dateTime.getMillis());
327        }
328        
329       public static String getIPAddressFromRequest(HttpServletRequest request) {
330            // Check for IPv6 addresses - Not sure what to do with them at this point.
331            // TODO: IPv6 - I see these on my local machine.
332            String ip = request.getRemoteAddr();
333            if (ip.indexOf(':') > -1) {
334                LOG.warn("ignoring IPv6 address for clock-in: " + ip);
335                ip = "";
336            }
337    
338            return ip;
339        }
340    
341        public static Date createDate(int month, int day, int year, int hours, int minutes, int seconds) {
342            DateTime dt = new DateTime(year, month, day, hours, minutes, seconds, 0);
343            return new Date(dt.getMillis());
344        }
345    
346        public static String getIPNumber() {
347            try {
348                return java.net.InetAddress.getLocalHost().getHostAddress();
349            } catch (UnknownHostException e) {
350                return "unknown";
351            }
352        }
353        //Used to preserve active row fetching based on max(timestamp)
354        public static Timestamp subtractOneSecondFromTimestamp(Timestamp originalTimestamp) {
355            DateTime dt = new DateTime(originalTimestamp);
356            dt = dt.minusSeconds(1);
357            return new Timestamp(dt.getMillis());
358        }
359    
360        public static Date subtractOneMillisecondFromDate(java.util.Date date) {
361            DateTime dt = new DateTime(date);
362            dt = dt.minusMillis(1);
363            return new Date(dt.getMillis());
364        }
365    
366        public static String formatDate(Date dt) {
367            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
368            return sdf.format(dt);
369        }
370    
371        
372        public static String formatDateTime(Timestamp timestamp){
373            Date dt = new Date(timestamp.getTime());
374            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
375            return sdf.format(dt);
376        }
377        
378        public static Date formatDateString(String date){
379            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
380            try {
381                            return new Date(sdf.parse(date).getTime());
382                    } catch (ParseException e) {
383                            return null;
384                    }
385        }
386        
387        /**
388         * Method to obtain the timezone offset string for the specified date time.
389         *
390         * Examples:
391         *
392         * -0500
393         * -0800
394         *
395         * @param date
396         * @return
397         */
398        public static String getTimezoneOffset(DateTime date) {
399            StringBuilder o = new StringBuilder();
400    
401            int offsetms = date.getZone().getOffset(date);
402            boolean negative = offsetms < 0;
403            if (negative) offsetms = offsetms * -1;
404    
405            Period period = new Period(offsetms);
406            if (negative) o.append('-');
407            o.append(StringUtils.leftPad(period.getHours() + "", 2, '0'));
408            o.append(StringUtils.leftPad(period.getMinutes() + "", 2, '0'));
409    
410            return o.toString();
411        }
412    
413        public static String arrayToString(String[] stringArray) {
414            StringBuilder str = new StringBuilder();
415            for (String string : stringArray) {
416                str.append(string);
417            }
418            return str.toString();
419        }
420    
421        /**
422         * Get the session timeout time. If it's not defined in the (local) config file, give it a default time.
423         */
424        public static int getSessionTimeoutTime() {
425            return StringUtils.isBlank(ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ConfigSettings.SESSION_TIMEOUT))
426                    ? 2700 :
427                    Integer.parseInt(ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ConfigSettings.SESSION_TIMEOUT));
428        }
429        
430        /**
431         * Creates a Timestamp object using Jodatime as an intermediate data structure
432         * from the provided date and time string. (From the form POST and javascript
433         * formats)
434         *
435         * @param dateStr (the format is 01/01/2011)
436         * @return Timestamp
437         */
438        public static Timestamp convertDateStringToTimestamp(String dateStr) {
439            // the date/time format is defined in tk.calendar.js. the format is 11/17/2010
440            String[] date = dateStr.split("/");
441    
442            DateTimeZone dtz = DateTimeZone.forID(TkServiceLocator.getTimezoneService().getUserTimezone());
443    
444            // this is from the jodattime javadoc:
445            // DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond, DateTimeZone zone)
446            // Noted that the month value is the actual month which is different than the java date object where the month value is the current month minus 1.
447            // I tried to use the actual month in the code as much as I can to reduce the convertions.
448            DateTime dateTime = new DateTime(
449                    Integer.parseInt(date[2]),
450                    Integer.parseInt(date[0]),
451                    Integer.parseInt(date[1]),
452                    0, 0, 0, 0, dtz);
453    
454            return new Timestamp(dateTime.getMillis());
455        }
456    
457        /**
458         * Creates a Timestamp object using Jodatime as an intermediate data structure
459         * from the provided date and time string. (From the form POST and javascript
460         * formats)
461         *
462         * @param dateStr (the format is 01/01/2011)
463         * @return Timestamp
464         */
465        public static Timestamp convertDateStringToTimestampNoTimezone(String dateStr) {
466            // the date/time format is defined in tk.calendar.js. the format is 11/17/2010
467            String[] date = dateStr.split("/");
468    
469            // this is from the jodattime javadoc:
470            // DateTime(int year, int monthOfYear, int dayOfMonth, int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond, DateTimeZone zone)
471            // Noted that the month value is the actual month which is different than the java date object where the month value is the current month minus 1.
472            // I tried to use the actual month in the code as much as I can to reduce the convertions.
473            DateTime dateTime = new DateTime(
474                    Integer.parseInt(date[2]),
475                    Integer.parseInt(date[0]),
476                    Integer.parseInt(date[1]),
477                    0, 0, 0, 0);
478    
479            return new Timestamp(dateTime.getMillis());
480        }
481    
482        
483        public static Timestamp getCurrentTimestamp() {
484            return new Timestamp(System.currentTimeMillis());
485        }
486        
487        public static List<Interval> createDaySpan(DateTime beginDateTime, DateTime endDateTime, DateTimeZone zone) {
488            beginDateTime = beginDateTime.toDateTime(zone);
489            endDateTime = endDateTime.toDateTime(zone);
490            List<Interval> dayIntervals = new ArrayList<Interval>();
491    
492            DateTime currDateTime = beginDateTime;
493            while (currDateTime.isBefore(endDateTime)) {
494                DateTime prevDateTime = currDateTime;
495                currDateTime = currDateTime.plusDays(1);
496                Interval daySpan = new Interval(prevDateTime, currDateTime);
497                dayIntervals.add(daySpan);
498            }
499    
500            return dayIntervals;
501        }
502        
503        public static List<Interval> getDaySpanForCalendarEntry(CalendarEntries calendarEntry) {
504            return getDaySpanForCalendarEntry(calendarEntry, TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
505        }
506    
507        public static List<Interval> getFullWeekDaySpanForCalendarEntry(CalendarEntries calendarEntry) {
508            return getFullWeekDaySpanForCalendarEntry(calendarEntry, TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback());
509        }
510        
511        public static List<Interval> getFullWeekDaySpanForCalendarEntry(CalendarEntries calendarEntry, DateTimeZone timeZone) {
512            DateTime beginDateTime = calendarEntry.getBeginLocalDateTime().toDateTime(timeZone);
513            DateTime endDateTime = calendarEntry.getEndLocalDateTime().toDateTime(timeZone);
514    
515            List<Interval> dayIntervals = new ArrayList<Interval>();
516    
517            DateTime currDateTime = beginDateTime;
518            if (beginDateTime.getDayOfWeek() != 7) {
519                currDateTime = beginDateTime.plusDays(0 - beginDateTime.getDayOfWeek());
520            }
521    
522            int afterEndDate = 6 - endDateTime.getDayOfWeek();
523            if (endDateTime.getDayOfWeek() == 7 && endDateTime.getHourOfDay() != 0) {
524                afterEndDate = 6;
525            }
526            if (endDateTime.getHourOfDay() == 0) {
527                afterEndDate += 1;
528            }
529            DateTime aDate = endDateTime.plusDays(afterEndDate);
530            while (currDateTime.isBefore(aDate)) {
531                DateTime prevDateTime = currDateTime;
532                currDateTime = currDateTime.plusDays(1);
533                Interval daySpan = new Interval(prevDateTime, currDateTime);
534                dayIntervals.add(daySpan);
535            }
536    
537            return dayIntervals;
538        }
539        
540        public static java.util.Date removeTime(java.util.Date date) {    
541            Calendar cal = Calendar.getInstance();  
542            cal.setTime(date);  
543            cal.set(Calendar.HOUR_OF_DAY, 0);  
544            cal.set(Calendar.MINUTE, 0);  
545            cal.set(Calendar.SECOND, 0);  
546            cal.set(Calendar.MILLISECOND, 0);  
547            return cal.getTime(); 
548        }
549        
550        public static int getWorkDays(java.util.Date startDate, java.util.Date endDate) {
551            int dayCounts = 0;
552            if(startDate.after(endDate)) {
553                    return 0;
554            }
555            Calendar cal1 = Calendar.getInstance();
556                    cal1.setTime(startDate);
557                    Calendar cal2 = Calendar.getInstance();
558                    cal2.setTime(endDate);
559                    
560                    while(!cal1.after(cal2)) {
561                            if(cal1.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY 
562                                            && cal1.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY) {
563                                    dayCounts ++;           
564                            }
565                            cal1.add(Calendar.DATE, 1);
566                    }
567            return dayCounts;
568        }
569        
570        public static boolean isWeekend(java.util.Date aDate) {
571            Calendar cal = Calendar.getInstance();
572                    cal.setTime(aDate);
573            if(cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY 
574                                    || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
575                            return true;            
576                    }
577            return false;
578        }
579        
580        public static java.util.Date addDates(java.util.Date aDate, int aNumber) {
581            Calendar gc = new GregorianCalendar();
582                    gc.setTime(aDate);
583                    gc.add(Calendar.DAY_OF_YEAR, aNumber);
584                    return gc.getTime();
585        }
586    }