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 }