001package arcommon; 002 003import java.text.ParseException; 004import java.text.SimpleDateFormat; 005import java.util.Calendar; 006import java.util.Date; 007import java.util.GregorianCalendar; 008import java.util.regex.Pattern; 009 010public class DateUtil { 011 public static final SimpleDateFormat DATE_FORMAT = createSimpleDateFormatLenient("yyyy-MM-dd"); 012 public static final Pattern DATE_FORMAT_PATTERN = calculatePattern(DATE_FORMAT); 013 public static final SimpleDateFormat DATE_FORMAT_DANISH = createSimpleDateFormatLenient("dd-MM-yyyy"); 014 public static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR = createSimpleDateFormatLenient("dd-MM"); 015 public static final SimpleDateFormat DATE_FORMAT_YEAR_ONLY = createSimpleDateFormatLenient("yyyy"); 016 public static final SimpleDateFormat TIME_FORMAT = createSimpleDateFormatLenient("HH:mm:ss"); 017 public static final SimpleDateFormat DATE_AND_TIME_FORMAT = createSimpleDateFormatLenient("yyyy-MM-dd HH:mm:ss"); 018 public static final SimpleDateFormat DATE_AND_TIME_WITH_MILLISECONDS_FORMAT = createSimpleDateFormatLenient("yyyy-MM-dd HH:mm:ss SSS"); 019 public static final SimpleDateFormat WEEKDAY_FORMAT = createSimpleDateFormatLenient("EEEEEEEE"); 020 public static final SimpleDateFormat WEEK_NUMBER_FORMAT = createSimpleDateFormatLenient("w"); 021 022 public static final long MILLISECOND_IN_NANOSECONDS = 1000000; 023 public static final int SECOND_IN_MILLISECONDS = 1000; 024 public static final long SECOND_IN_NANOSECONDS = SECOND_IN_MILLISECONDS * MILLISECOND_IN_NANOSECONDS; 025 public static final int MINUTE_IN_SECONDS = 60; 026 public static final int MINUTE_IN_MILLISECONDS = SECOND_IN_MILLISECONDS * MINUTE_IN_SECONDS; 027 public static final long MINUTE_IN_NANOSECONDS = SECOND_IN_NANOSECONDS * MINUTE_IN_SECONDS; 028 public static final int HOUR_IN_MINUTES = 60; 029 public static final int HOUR_IN_SECONDS = HOUR_IN_MINUTES * MINUTE_IN_SECONDS; 030 public static final long HOUR_IN_MILLISECONDS = MINUTE_IN_MILLISECONDS * HOUR_IN_MINUTES; 031 public static final long HOUR_IN_NANOSECONDS = MINUTE_IN_NANOSECONDS * HOUR_IN_MINUTES; 032 public static final int DAY_IN_HOURS = 24; 033 public static final int DAY_IN_SECONDS = DAY_IN_HOURS * HOUR_IN_SECONDS; 034 public static final long DAY_IN_MILLISECONDS = HOUR_IN_MILLISECONDS * DAY_IN_HOURS; 035 public static final long DAY_IN_NANOSECONDS = HOUR_IN_NANOSECONDS * DAY_IN_HOURS; 036 public static final int YEAR_IN_DAYS = 365; 037 public static final int YEAR_IN_MONTHS = 12; 038 public static final long YEAR_IN_SECONDS = YEAR_IN_DAYS * DAY_IN_SECONDS; 039 public static final long YEAR_IN_MILLISECONDS = DAY_IN_MILLISECONDS * YEAR_IN_DAYS; 040 public static final long YEAR_IN_NANOSECONDS = DAY_IN_NANOSECONDS * YEAR_IN_DAYS; 041 042 public static final long MONTHS_INTERVAL = -1; 043 044 public static int calculateWeekday(Date date) { 045 Calendar c = Calendar.getInstance(); 046 c.setTime(date); 047 int dayOfWeek = c.get(Calendar.DAY_OF_WEEK); 048 return dayOfWeek; 049 } 050 051 public static int calculateWeekNumber(Date date) { 052 String string = dateToString(WEEK_NUMBER_FORMAT, date); 053 int week = Integer.parseInt(string); 054 return week; 055 } 056 057 public static int calculateYear(Date date) { 058 String string = dateToString(DATE_FORMAT_YEAR_ONLY, date); 059 int year = Integer.parseInt(string); 060 return year; 061 } 062 063 public static String dateToDanishString(Date date) { 064 return dateToString(DATE_FORMAT_DANISH, date); 065 } 066 067 public static String dateToDateSortableString(Date date) { 068 return dateToString(DATE_FORMAT, date); 069 } 070 071 public static String dateToStringWithoutYear(Date date) { 072 return dateToString(DATE_FORMAT_WITHOUT_YEAR, date); 073 } 074 075 public static String dateToDateAndTimeString(Date date) { 076 return dateToString(DATE_AND_TIME_FORMAT, date); 077 } 078 079 public static String dateToTimeString(Date date) { 080 return dateToString(TIME_FORMAT, date); 081 } 082 083 public static String dateToWeekdayWithDateString(Date date, String delimiter) { 084 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEEEEEE'" + delimiter + "'dd-MM-yyyy"); 085 return simpleDateFormat.format(date); 086 } 087 088 public static String dateToWeekdayString(Date date) { 089 return dateToString(WEEKDAY_FORMAT, date); 090 } 091 092 public static String dateToString(SimpleDateFormat format, Date date) { 093 synchronized (format) { // To make it thread safe 094 return format.format(date); 095 } 096 } 097 098 public static boolean isStringADate(SimpleDateFormat format, String string) { 099 try { 100 Date date = stringToDate(format, string, false); 101 return date != null; 102 } catch (ParseException e) { 103 throw new RuntimeException(e); 104 } 105 } 106 107 public static Date stringToDate(SimpleDateFormat format, String string) { 108 try { 109 return stringToDate(format, string, true); 110 } catch (ParseException e) { 111 throw new RuntimeException(e); 112 } 113 } 114 115 public static Date stringToDate(SimpleDateFormat format, String string, boolean throwOnFail) throws ParseException { 116 synchronized (format) { // To make it thread safe 117 try { 118 return format.parse(string); 119 } catch (ParseException e) { 120 if (throwOnFail) { 121 throw e; 122 } 123 return null; 124 } 125 } 126 } 127 128 public static SimpleDateFormat createSimpleDateFormatLenient(String dateFormat) { 129 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); 130 simpleDateFormat.setLenient(false); 131 return simpleDateFormat; 132 } 133 134 private static Pattern calculatePattern(SimpleDateFormat dateFormat) { 135 String patternString = dateFormat.toPattern(); 136 patternString = patternString.replace("yyyy", "\\d{4}"); 137 patternString = patternString.replace("MM", "\\d{1,2}"); 138 patternString = patternString.replace("dd", "\\d{1,2}"); 139 return Pattern.compile(patternString); 140 } 141 142 public static String dateIntervalToDateString(Date date, boolean sortable, long interval) { 143 String result; 144 145 // Years 146 if (interval >= YEAR_IN_MILLISECONDS) { 147 if (interval % YEAR_IN_MILLISECONDS != 0) { 148 throw new RuntimeException("interval % YEAR_IN_MILLISECONDS != 0"); 149 } 150 long dateLong = date.getTime(); 151 long year = calculateYear(new Date(dateLong)); 152 interval = interval / YEAR_IN_MILLISECONDS; 153 year = (year / interval) * interval; 154 if (interval > 1) { 155 if (sortable) { 156 result = year + "-01-01"; 157 } else { 158 result = "01-01-" + year; 159 } 160 result = result + " - "; 161 year = year + interval - 1; 162 if (sortable) { 163 result = result + year + "-12-31"; 164 } else { 165 result = result + "31-12-" + year; 166 } 167 } else { 168 result = "" + year; 169 } 170 return result; 171 } 172 173 if (interval <= MONTHS_INTERVAL && interval % YEAR_IN_MONTHS == 0) { 174 interval = YEAR_IN_MILLISECONDS * (-interval / YEAR_IN_MONTHS); 175 } 176 177 // Months 178 if (interval <= MONTHS_INTERVAL) { 179 interval = -interval; 180 Calendar calendar = Calendar.getInstance(); 181 calendar.setTime(date); 182 int year = calendar.get(Calendar.YEAR); 183 int month = calendar.get(Calendar.MONTH) + 1; 184 calendar.set(Calendar.DAY_OF_MONTH, 1); 185 int yearMonth = year * 12 + month; 186 int yearMonthInterval = yearMonth / (int)interval * (int)interval + 1; 187 if (yearMonthInterval != yearMonth) { 188 calendar.add(Calendar.MONTH, yearMonthInterval - yearMonth); 189 } 190 year = calendar.get(Calendar.YEAR); 191 month = calendar.get(Calendar.MONTH) + 1; 192 result = StringUtil.longToStringWithFixLengthWith0(year, 4); 193 result = result + "-" + StringUtil.longToStringWithFixLengthWith0(month, 2); 194 if (interval > 1) { 195 result = result + "-01"; 196 calendar.set(Calendar.MONTH, month + (int)interval - 1); 197 calendar.set(Calendar.DAY_OF_MONTH, 1); 198 calendar.add(Calendar.DAY_OF_MONTH, -1); 199 year = calendar.get(Calendar.YEAR); 200 month = calendar.get(Calendar.MONTH) + 1; 201 result = result + " - " + StringUtil.longToStringWithFixLengthWith0(year, 4); 202 result = result + "-" + StringUtil.longToStringWithFixLengthWith0(month, 2); 203 result = result + "-" + StringUtil.longToStringWithFixLengthWith0(calendar.get(Calendar.DAY_OF_MONTH), 2); 204 } 205 return result; 206 } 207 208 // Days 209 if (interval >= DAY_IN_MILLISECONDS && interval < DAY_IN_MILLISECONDS * YEAR_IN_DAYS) { 210 if (interval % DAY_IN_MILLISECONDS != 0) { 211 throw new RuntimeException("interval % DAY_IN_MILLISECONDS != 0"); 212 } 213 long dateLong = date.getTime(); 214 dateLong = (dateLong / interval) * interval; 215 if (sortable) { 216 result = dateToDateSortableString(new Date(dateLong)); 217 } else { 218 result = dateToDanishString(new Date(dateLong)); 219 } 220 if (interval / DAY_IN_MILLISECONDS > 1) { 221 dateLong += interval - DAY_IN_MILLISECONDS; 222 result = result + " 00:00 - "; 223 if (sortable) { 224 result = result + dateToDateSortableString(new Date(dateLong)); 225 } else { 226 result = result + dateToDanishString(new Date(dateLong)); 227 } 228 result = result + " 23:59"; 229 } 230 return result; 231 } 232 233 if (sortable) { 234 result = dateToDateSortableString(date); 235 } else { 236 result = dateToDanishString(date); 237 } 238 Calendar calendar = Calendar.getInstance(); 239 calendar.setTime(date); 240 241 // Hours 242 if (interval >= HOUR_IN_MILLISECONDS && interval < DAY_IN_MILLISECONDS) { 243 if (interval % HOUR_IN_MILLISECONDS != 0) { 244 throw new RuntimeException("interval % HOUR_IN_MILLISECONDS != 0"); 245 } 246 int hour = calendar.get(Calendar.HOUR_OF_DAY); 247 interval = interval / HOUR_IN_MILLISECONDS; 248 if (DAY_IN_HOURS % interval != 0) { 249 throw new RuntimeException("DAY_IN_HOURS % interval != 0 " + DAY_IN_HOURS % interval + " " + interval); 250 } 251 result = result + " " + StringUtil.longToStringWithFixLengthWith0((hour / interval) * interval, 2) + ":00"; 252 if (interval > 1) { 253 result = result + " - "; 254 result = result + StringUtil.longToStringWithFixLengthWith0((hour / interval + 1) * interval - 1, 2) + ":59"; 255 } 256 return result; 257 } 258 259 // Minutes 260 if (interval >= MINUTE_IN_MILLISECONDS && interval < HOUR_IN_MILLISECONDS) { 261 String hour = StringUtil.longToStringWithFixLengthWith0(calendar.get(Calendar.HOUR_OF_DAY), 2); 262 int minute = calendar.get(Calendar.MINUTE); 263 if (interval % MINUTE_IN_MILLISECONDS != 0) { 264 throw new RuntimeException("interval % MINUTE_IN_MILLISECONDS != 0"); 265 } 266 interval = interval / MINUTE_IN_MILLISECONDS; 267 if (HOUR_IN_MINUTES % interval != 0) { 268 throw new RuntimeException("HOUR_IN_MINUTES % interval != 0 " + HOUR_IN_MINUTES % interval + " " + interval); 269 } 270 result = result + " " + hour + ":" + StringUtil.longToStringWithFixLengthWith0(minute / interval * interval, 2); 271 if (interval > 1) { 272 result = result + " - " + hour + ":"; 273 result = result + StringUtil.longToStringWithFixLengthWith0((minute / interval + 1) * interval - 1, 2); 274 } 275 return result; 276 } 277 278 // Seconds 279 if (interval >= SECOND_IN_MILLISECONDS && interval < MINUTE_IN_MILLISECONDS) { 280 String hour = StringUtil.longToStringWithFixLengthWith0(calendar.get(Calendar.HOUR_OF_DAY), 2); 281 String minute = StringUtil.longToStringWithFixLengthWith0(calendar.get(Calendar.MINUTE), 2); 282 int second = calendar.get(Calendar.SECOND); 283 if (interval % SECOND_IN_MILLISECONDS != 0) { 284 throw new RuntimeException("interval % SECOND_IN_MILLISECONDS != 0 " + interval % SECOND_IN_MILLISECONDS); 285 } 286 interval = interval / SECOND_IN_MILLISECONDS; 287 if (MINUTE_IN_SECONDS % interval != 0) { 288 throw new RuntimeException("MINUTE_IN_SECONDS % interval != 0 " + MINUTE_IN_SECONDS % interval + " " + interval); 289 } 290 result = result + " " + hour + ":" + minute + ":" + StringUtil.longToStringWithFixLengthWith0(second / interval * interval, 2); 291 if (interval > 1) { 292 result = result + " - " + hour + ":" + minute + ":"; 293 result = result + StringUtil.longToStringWithFixLengthWith0((second / interval + 1) * interval - 1, 2); 294 } 295 return result; 296 } 297 298 throw new RuntimeException("Internal error: " + interval); 299 } 300 301 public static String nanoTimeDiffToString(long timeInNano, boolean withUnit) { 302 int sign = MathUtil.signum(timeInNano); 303 long nano = Math.abs(timeInNano); 304 long micro = nano / 1000; 305 long milliseconds = micro / 1000; 306 long seconds = milliseconds / 1000; 307 long minutes = seconds / 60; 308 long hours = minutes / 60; 309 long days = hours / 24; 310 nano = nano % 1000; 311 micro = micro % 1000; 312 milliseconds = milliseconds % 1000; 313 seconds = seconds % 60; 314 minutes = minutes % 60; 315 hours = hours % 24; 316 String result = ""; 317 String unit = null; 318 319 if (days > 0) { 320 result = result + days + " "; 321 unit = "day" + (days != 1 ? "s" : ""); 322 } 323 324 if (hours > 0 || result.length() > 0) { 325 if (result.length() > 0) { 326 result = result + StringUtil.longToStringWithFixLengthWith0(hours, 2) + ":"; 327 } else { 328 result = result + hours + ":"; 329 } 330 if (unit == null) { 331 unit = "hour" + (hours != 1 ? "s" : ""); 332 } 333 } 334 335 if (minutes > 0 || result.length() > 0) { 336 if (result.length() > 0) { 337 result = result + StringUtil.longToStringWithFixLengthWith0(minutes, 2) + ":"; 338 } else { 339 result = result + minutes + ":"; 340 } 341 if (unit == null) { 342 unit = "minute" + (minutes != 1 ? "s" : ""); 343 } 344 } 345 346 if (seconds > 0 || result.length() > 0) { 347 if (result.length() > 0) { 348 result = result + StringUtil.longToStringWithFixLengthWith0(seconds, 2); 349 } else { 350 result = result + seconds; 351 if (seconds <= 9 && milliseconds > 0) { 352 result = result + "," + StringUtil.longToStringWithFixLengthWith0(milliseconds, 3); 353 } 354 } 355 if (unit == null) { 356 unit = "second" + (seconds != 1 ? "s" : ""); 357 } 358 } 359 360 if (milliseconds > 0 && result.length() == 0) { 361 result = "" + milliseconds; 362 if (milliseconds <= 9 && micro > 0) { 363 result = result + "," + StringUtil.longToStringWithFixLengthWith0(micro, 3); 364 } 365 unit = "ms"; 366 } 367 368 if (micro > 0 && result.length() == 0) { 369 result = "" + micro; 370 if (micro <= 9 && nano > 0) { 371 result = result + "," + StringUtil.longToStringWithFixLengthWith0(nano, 3); 372 } 373 unit = "\u00B5s"; 374 } 375 376 if (result.length() == 0) { 377 result = "" + nano; 378 unit = "ns"; 379 } 380 381 if (withUnit) { 382 result += " " + unit; 383 } 384 385 if (sign < 0) { 386 result = "-" + result; 387 } 388 389 return result; 390 } 391 392 public static String millisecondsDiffToString(long time, boolean withUnit) { 393 return timeDiffToString(time, true, withUnit); 394 } 395 396 public static String secondsDiffToString(long time, boolean withUnit) { 397 return timeDiffToString(time, false, withUnit); 398 } 399 400 private static String timeDiffToString(long time, boolean isMilliseconds, boolean withUnit) { 401 long milliseconds; 402 long seconds; 403 if (isMilliseconds) { 404 milliseconds = time; 405 seconds = milliseconds / 1000; 406 } else { 407 milliseconds = -1; 408 seconds = time; 409 } 410 long minutes = seconds / 60; 411 long hours = minutes / 60; 412 long days = hours / 24; 413 milliseconds = milliseconds % 1000; 414 seconds = seconds % 60; 415 minutes = minutes % 60; 416 hours = hours % 24; 417 String result = ""; 418 String unit = null; 419 if (days > 0) { 420 result = result + days + " "; 421 unit = "day" + (days != 1 ? "s" : ""); 422 } 423 424 if (hours > 0 || result.length() > 0) { 425 if (result.length() > 0) { 426 result = result + StringUtil.longToStringWithFixLengthWith0(hours, 2) + ":"; 427 } else { 428 result = result + hours + ":"; 429 } 430 if (unit == null) { 431 unit = "hour" + (hours != 1 ? "s" : ""); 432 } 433 } 434 435 if (minutes > 0 || result.length() > 0) { 436 if (result.length() > 0) { 437 result = result + StringUtil.longToStringWithFixLengthWith0(minutes, 2) + ":"; 438 } else { 439 result = result + minutes + ":"; 440 } 441 if (unit == null) { 442 unit = "minute" + (minutes != 1 ? "s" : ""); 443 } 444 } 445 446 if (result.length() > 0) { 447 result = result + StringUtil.longToStringWithFixLengthWith0(seconds, 2); 448 } else { 449 result = result + seconds; 450 } 451 if (isMilliseconds) { 452 result = result + ","; 453 } 454 if (unit == null) { 455 unit = "second" + (seconds != 1 ? "s" : ""); 456 } 457 458 if (isMilliseconds) { 459 result = result + StringUtil.longToStringWithFixLengthWith0(milliseconds, 3); 460 } 461 if (withUnit) { 462 result += " " + unit; 463 } 464 return result; 465 } 466 467 public static void copyDate(Date from, Date to) { 468 Calendar calendar = Calendar.getInstance(); 469 calendar.setTime(from); 470 int d = calendar.get(Calendar.DAY_OF_MONTH); 471 int m = calendar.get(Calendar.MONTH) + 1; 472 int y = calendar.get(Calendar.YEAR); 473 calendar.setTime(to); 474 calendar.set(Calendar.DAY_OF_MONTH, d); 475 calendar.set(Calendar.MONTH, m - 1); 476 calendar.set(Calendar.YEAR, y); 477 to.setTime(calendar.getTimeInMillis()); 478 } 479 480 public static Date clearTime(Date date) { 481 GregorianCalendar gregorianCalendar = new GregorianCalendar(); 482 gregorianCalendar.setTime(date); 483 clearTime(gregorianCalendar); 484 return gregorianCalendar.getTime(); 485 } 486 487 public static void clearTime(GregorianCalendar gregorianCalendar) { 488 gregorianCalendar.set(GregorianCalendar.HOUR_OF_DAY, 0); 489 gregorianCalendar.set(GregorianCalendar.MINUTE, 0); 490 gregorianCalendar.set(GregorianCalendar.SECOND, 0); 491 gregorianCalendar.set(GregorianCalendar.MILLISECOND, 0); 492 } 493 494 public static Date newDate(int year, int month, int date) { 495 GregorianCalendar gregorianCalendar = new GregorianCalendar(year, month - 1, date); 496 return gregorianCalendar.getTime(); 497 } 498 499 public static boolean beforeOrEquals(Date d1, Date d2) { 500 return d1.before(d2) || d1.equals(d2); 501 } 502 503 public static boolean afterOrEquals(Date d1, Date d2) { 504 return d1.after(d2) || d1.equals(d2); 505 } 506 507 public static Date calculateEaster(int year) { 508 // The algorithm is from http://en.wikipedia.org/wiki/Computus 509 int a = year % 19; // a = year mod 19 510 int b = year % 4; // b = year mod 4 511 int c = year % 7; // c = year mod 7 512 int k = year / 100; // k = floor (year/100) 513 int p = (13 + 8 * k) / 25; // p = floor ((13 + 8k)/25) 514 int q = k / 4; // q = floor (k/4) 515 int M = (15 - p + k - q) % 30; // M = (15 - p + k - q) mod 30 516 int N = (4 + k - q) % 7; // N = (4 + k - q) mod 7 517 int d = (19 * a + M) % 30; // d = (19a + M) mod 30 518 int e = (2 * b + 4 * c + 6 * d + N) % 7; // e = (2b + 4c + 6d + N) mod 7 519 520 // Gregorian Easter is 22 + d + e March or d + e - 9 April 521 if (22 + d + e <= 30) { 522 return newDate(year, 3, 22 + d + e); 523 } 524 int aprilDate = d + e - 9; 525 526 // if d = 29 and e = 6, replace 26 April with 19 April 527 if (d == 29 && e == 6) { 528 aprilDate = 19; 529 } 530 531 // if d = 28, e = 6, and (11M + 11) mod 30 < 19, replace 25 April with 18 April 532 if (d == 28 && e == 6 && (11 * M + 11) % 30 < 19) { 533 aprilDate = 18; 534 } 535 536 return newDate(year, 4, aprilDate); 537 } 538}