/* * Copyright 2011 Federal Chancellery Austria and * Graz University of Technology * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.gv.util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang.time.DateUtils; import org.apache.commons.validator.GenericTypeValidator; import org.apache.commons.validator.GenericValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Thomas Knall */ public final class DateTimeUtil { private static Logger log = LoggerFactory.getLogger(DateTimeUtil.class); public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd"; public static final String DEFAULT_TIME_PATTERN = "HH:mm:ss"; public static final String DEFAULT_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS"; public static int calcAge(Calendar dateOfBirth, Calendar now) { int age = now.get(Calendar.YEAR) - dateOfBirth.get(Calendar.YEAR); int nowM = now.get(Calendar.MONTH); int dobM = dateOfBirth.get(Calendar.MONTH); int nowDOM = now.get(Calendar.DAY_OF_MONTH); int dobDOM = dateOfBirth.get(Calendar.DAY_OF_MONTH); if ((nowM < dobM) || ((nowM == dobM) && (nowDOM < dobDOM))) { age--; } if (age < 0) { throw new IllegalArgumentException( "Calculated age results in negative value."); } return age; } public static int calcAge(Calendar dateOfBirth) { return calcAge(dateOfBirth, Calendar.getInstance()); } public static int calcAge(Date dateOfBirth, Date now) { Calendar dob = Calendar.getInstance(); dob.setTime(dateOfBirth); Calendar nowCal = Calendar.getInstance(); nowCal.setTime(now); return calcAge(dob, nowCal); } public static int calcAge(Date dateOfBirth) { return calcAge(dateOfBirth, new Date()); } public static Date parseDate(String dateString, String datePattern, String checkRegExpPattern) { Date returnDate = null; if (!MiscUtil.isEmpty(checkRegExpPattern)) { if (!GenericValidator.matchRegexp(dateString, checkRegExpPattern)) { return null; } } if (MiscUtil.isEmpty(datePattern)) { datePattern = DEFAULT_DATE_PATTERN; } if (GenericValidator.isDate(dateString, datePattern, false)) { returnDate = GenericTypeValidator.formatDate(dateString, datePattern, false); } return returnDate; } public static Date mergeDateTime(Date date, Date time) { if (MiscUtil.areAllNull(date, time)) { throw new NullPointerException( "Date and time must not be null at the same time."); } if (date == null) { return time; } if (time == null) { return date; } Calendar dateCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); dateCal.setTime(date); Calendar timeCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); timeCal.setTime(time); dateCal.set(Calendar.HOUR_OF_DAY, timeCal.get(Calendar.HOUR_OF_DAY)); dateCal.set(Calendar.MINUTE, timeCal.get(Calendar.MINUTE)); dateCal.set(Calendar.SECOND, timeCal.get(Calendar.SECOND)); dateCal.set(Calendar.MILLISECOND, timeCal.get(Calendar.MILLISECOND)); return dateCal.getTime(); } public static Date parseDate(String dateString, String datePattern) { return parseDate(dateString, datePattern, null); } public static boolean equalsIgnoreMilliseconds(Date date1, Date date2) { if (date1 == date2) { return true; } if (date1 == null || date2 == null) { return false; } return DateUtils.truncate(date1, Calendar.SECOND).equals( DateUtils.truncate(date2, Calendar.SECOND)); } public static Date parseDate(String dateString) { return parseDate(dateString, null, null); } public static Date parseTime(String timeString) { return parseDate(timeString, DEFAULT_TIME_PATTERN, null); } public static String formatXMLDateTimeString(Date date) { if (date == null) { return "null"; } Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.get(Calendar.MILLISECOND); return DateFormatUtils.format(date, cal.get(Calendar.MILLISECOND) == 0 ? "yyyy-MM-dd'T'HH:mm:ssZZ" : "yyyy-MM-dd'T'HH:mm:ss.SSSZZ"); } public static String formatUTCDateTimeString(Date date) { if (date == null) { return "null"; } return DateFormatUtils.formatUTC(date, "yyyy-MM-dd'T'HH:mm:ss'Z'"); } /** *

* Parses a datetime according to ISO 8601. *

* *

* Complete date plus hours and minutes
* YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00) *

* *

* Complete date plus hours, minutes and seconds
* YYYY-MM-DDThh:mm:ssTZD(eg 1997-07-16T19:20:30+01:00) *

* *

* Complete date plus hours, minutes, seconds and a decimal fraction of a * second
* YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) *

* *

* where: *

* * YYYY = four-digit year
* MM = two-digit month (01=January, etc.)
* DD = two-digit day of month (01 through 31)
* hh = two digits of hour (00 through 23) (am/pm NOT allowed)
* mm = two digits of minute (00 through 59)
* ss = two digits of second (00 through 59)
* s = one or more digits representing a decimal fraction of a second
* TZD = time zone designator (Z or +hh:mm or -hh:mm)
* * @see http://www.w3.org/TR/NOTE-datetime * @param dtText * The date. * @param noTimeZoneMeansUTC * @return The parsed date. * @throws ParseException */ public static Date parseXMLDateTimeString(String dtText, boolean noTimeZoneMeansUTC) throws ParseException { if (dtText == null) { throw new NullPointerException("Provided date text must not be null."); } String[] parsePatterns = { "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ssZZ", "yyyy-MM-dd'T'HH:mm:ss.SSSZZ", "yyyy-MM-dd'T'HH:mm:ss.SSS" }; // TODO: fix hack if (dtText.length() > 19) { int li = dtText.lastIndexOf(":"); if (li >= 19) { dtText = new StringBuffer(dtText).deleteCharAt(li).toString(); } if (dtText.endsWith("Z")) { dtText = StringUtils.chop(dtText) + "UTC"; } } else if (noTimeZoneMeansUTC) { log.debug("UTC can be applied because no other offset has been provided."); dtText += "UTC"; } return DateUtils.parseDate(dtText, parsePatterns); } public static Date parseXMLDateTimeString(String dtText) throws ParseException { return parseXMLDateTimeString(dtText, false); } public static String getDatePattern(String languageCode) { String datePattern = DEFAULT_DATE_PATTERN; if (languageCode != null) { if ("de".equalsIgnoreCase(languageCode)) { datePattern = "dd.MM.yyyy"; } else if ("en".equalsIgnoreCase(languageCode)) { datePattern = "yyyy-MM-dd"; } else { log.warn("language code \"" + languageCode + "\" notsupported; using failsafe pattern \"" + datePattern + "\""); } } return datePattern; } public static String formatDate(Date date, String datePattern) { if (date == null) { throw new NullPointerException("date must not be null"); } SimpleDateFormat sdf = new SimpleDateFormat(datePattern); return sdf.format(date); } public static String formatDate(Date date) { return formatDate(date, getDatePattern(null)); } public static String formatTime(Date time) { return formatDate(time, DEFAULT_TIME_PATTERN); } /** * Checks if the given reference date ({@code pReference}) is valid according * to the given date constraints {@code from} and {@code to}. The validation * uses an intuitive approach including {@code from} and {@code to} ignoring * the time component of the date objects:
* e.g. {@code from=2008-09-09}, {@code to=2010-09-08}
* valid reference dates: {@code 2008-09-09T00:00:00.000}, * {@code 2008-09-09T12:34:50.000}, {@code 2010-09-08T00:00:00.000}, * {@code 2010-09-08T23:59:59.999} * * @param from * The date the validity period starts from (inclusive ignoring time * component). * @param to * The date the validity period starts from (inclusive ignoring time * component). * @param pReference * The date to be validated. * @return {@code true} if the reference date is valid, {@code false} if not. * @throws CheckException * Thrown if the given reference date could not be validated. * @author Thomas Knall */ public static boolean validateDateConstraint(Date from, Date to, Date pReference) { if (pReference == null) { throw new NullPointerException( "Reference date needed to verify time constraints."); } Calendar fromCal = null; if (from != null) { fromCal = Calendar.getInstance(); fromCal.setTime(from); fromCal = removeTimeComponent(fromCal); } Calendar toCal = null; if (to != null) { toCal = Calendar.getInstance(); toCal.setTime(to); toCal = removeTimeComponent(toCal); } Calendar refCal = Calendar.getInstance(); refCal.setTime(pReference); refCal = removeTimeComponent(refCal); if ((fromCal != null && refCal.before(fromCal)) || (toCal != null && refCal.after(toCal))) { return false; } return true; } /** * Removes the time component from a date object returning a new date * instance. * * @param cal * The original date object. * @return A new date object (without time component). * @author Thomas Knall */ public static Date removeTimeComponent(Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return removeTimeComponent(cal).getTime(); } /** * Removes the time component from a calendar object returning a new calendar * instance. * * @param cal * The original calendar object. * @return A new calendar object (without time component). * @author Thomas Knall */ public static Calendar removeTimeComponent(Calendar cal) { Calendar newCal = (Calendar) cal.clone(); newCal.set(Calendar.HOUR_OF_DAY, 0); newCal.set(Calendar.MINUTE, 0); newCal.set(Calendar.SECOND, 0); newCal.set(Calendar.MILLISECOND, 0); return newCal; } public static String formatTimeStamp(Date timestamp) { return formatDate(timestamp, DEFAULT_TIMESTAMP_PATTERN); } private DateTimeUtil() { } }