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 }