/*
* Copyright 2003 Federal Chancellery Austria
* MOA-ID has been developed in a cooperation between BRZ, the Federal
* Chancellery Austria - ICT staff unit, and Graz University of Technology.
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* http://www.osor.eu/eupl/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
package at.gv.egovernment.moa.util;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* Utility for parsing and building XML type dateTime
,
* according to ISO 8601.
*
* @author Patrick Peck
* @version $Id$
* @see http://www.w3.org/2001/XMLSchema-datatypes"
*/
public class DateTimeUtils {
/** Error messages. */
private static MessageProvider msg = MessageProvider.getInstance();
// /**
// * Builds a dateTime
value from a Calendar
value.
// * @param cal the Calendar
value
// * @return the dateTime
value
// */
// public static String buildDateTime(Calendar cal, boolean useUTC) {
//
// if (useUTC)
// return buildDateTimeUTC(cal);
// else {
// StringWriter out = new StringWriter();
// out.write("" + cal.get(Calendar.YEAR));
// out.write("-");
// out.write(to2DigitString(cal.get(Calendar.MONTH) + 1));
// out.write("-");
// out.write(to2DigitString(cal.get(Calendar.DAY_OF_MONTH)));
// out.write("T");
// out.write(to2DigitString(cal.get(Calendar.HOUR_OF_DAY)));
// out.write(":");
// out.write(to2DigitString(cal.get(Calendar.MINUTE)));
// out.write(":");
// out.write(to2DigitString(cal.get(Calendar.SECOND)));
// int tzOffsetMilliseconds =
// cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
// if (tzOffsetMilliseconds != 0) {
// int tzOffsetMinutes = tzOffsetMilliseconds / (1000 * 60);
// int tzOffsetHours = tzOffsetMinutes / 60;
// tzOffsetMinutes -= tzOffsetHours * 60;
// if (tzOffsetMilliseconds > 0) {
// out.write("+");
// out.write(to2DigitString(tzOffsetHours));
// out.write(":");
// out.write(to2DigitString(tzOffsetMinutes));
// } else {
// out.write("-");
// out.write(to2DigitString(-tzOffsetHours));
// out.write(":");
// out.write(to2DigitString(-tzOffsetMinutes));
// }
// }
// return out.toString();
// }
// }
/**
* Builds a dateTime
value in UTC from a Calendar
value.
* @param cal the Calendar
value
* @return the dateTime
value
*/
public static String buildDateTimeUTC(Calendar cal) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
return f.format(cal.getTime());
}
/**
* Builds a dateTime
value in UTC from a Calendar
value.
* @param cal the Calendar
value
* @return the dateTime
value
*/
public static String buildDateTimeUTC(Date cal) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
return f.format(cal);
}
/**
* Builds a dateTime
value from a Calendar
value.
* @param cal the Calendar
value
* @return the dateTime
value
*/
public static String buildDate(Calendar cal) {
StringWriter out = new StringWriter();
out.write("" + cal.get(Calendar.YEAR));
out.write("-");
out.write(to2DigitString(cal.get(Calendar.MONTH) + 1));
out.write("-");
out.write(to2DigitString(cal.get(Calendar.DAY_OF_MONTH)));
return out.toString();
}
/**
* Builds a dateTime
value from a Calendar
value.
* @param cal the Calendar
value
* @return the dateTime
value
*/
public static String buildTime(Calendar cal) {
StringWriter out = new StringWriter();
out.write(to2DigitString(cal.get(Calendar.HOUR_OF_DAY)));
out.write(":");
out.write(to2DigitString(cal.get(Calendar.MINUTE)));
out.write(":");
out.write(to2DigitString(cal.get(Calendar.SECOND)));
return out.toString();
}
/**
* Converts month, day, hour, minute, or second value
* to a 2 digit String.
* @param number the month, day, hour, minute, or second value
* @return 2 digit String
*/
private static String to2DigitString(int number) {
if (number < 10)
return "0" + number;
else
return "" + number;
}
/**
* Parse a String
containing a date and time instant, given in
* ISO 8601 format.
*
* @param dateTime The String
to parse.
* @return The Date
representation of the contents of
* dateTime
.
* @throws ParseException Parsing the dateTime
failed.
*/
public static Date parseDateTime(String dateTime) throws ParseException {
GregorianCalendar calendar;
long time;
int yearSign = 1, year, month, day;
int hour, minute, second;
double fraction = 0.0;
int tzSign = 1, tzHour = 0, tzMinute = 0;
int curPos = 0;
String fractStr;
boolean localTime = false;
char c;
// parse year sign
ensureChars(dateTime, curPos, 1);
c = dateTime.charAt(curPos);
if (c == '+' || c == '-') {
yearSign = c == '+' ? 1 : -1;
curPos++;
}
// parse year
year = parseInt(dateTime, curPos, 4);
curPos += 4;
// parse '-'
ensureChar(dateTime, curPos, '-');
curPos++;
// parse month
month = parseInt(dateTime, curPos, 2);
ensureValue(month, 1, 12, curPos);
curPos += 2;
// parse '-'
ensureChar(dateTime, curPos, '-');
curPos++;
// parse day
day = parseInt(dateTime, curPos, 2);
ensureValue(day, 1, 31, curPos);
curPos += 2;
// parse 'T'
ensureChar(dateTime, curPos, 'T');
curPos++;
// parse hour
hour = parseInt(dateTime, curPos, 2);
ensureValue(hour, 0, 23, curPos);
curPos += 2;
// parse ':'
ensureChar(dateTime, curPos, ':');
curPos++;
// parse minute
minute = parseInt(dateTime, curPos, 2);
ensureValue(minute, 0, 59, curPos);
curPos += 2;
// parse ':'
ensureChar(dateTime, curPos, ':');
curPos++;
// parse second
second = parseInt(dateTime, curPos, 2);
ensureValue(second, 0, 59, curPos);
curPos += 2;
// parse a fraction
if (dateTime.length() > curPos && dateTime.charAt(curPos) == '.') {
curPos++;
ensureDigits(dateTime, curPos, 1);
fractStr = "0.";
fractStr
+= dateTime.substring(curPos, curPos + countDigits(dateTime, curPos));
fraction = Double.parseDouble(fractStr);
curPos += countDigits(dateTime, curPos);
}
// parse a time zone
if (dateTime.length() > curPos) {
c = dateTime.charAt(curPos);
if (c == 'Z') {
curPos++;
} else if (c == '+' || c == '-') {
// parse time zone sign
tzSign = c == '+' ? 1 : -1;
curPos++;
// parse time zone hour
tzHour = parseInt(dateTime, curPos, 2);
ensureValue(tzHour, 0, 14, curPos);
curPos += 2;
// parse ':'
ensureChar(dateTime, curPos, ':');
curPos++;
// parse time zone minute
tzMinute = parseInt(dateTime, curPos, 2);
ensureValue(tzMinute, 0, 59, curPos);
curPos += 2;
}
} else {
localTime = true;
}
// if we have characters left, it's an error
if (dateTime.length() != curPos) {
throw new ParseException(msg.getMessage("datetime.00", null), curPos);
}
// build the Date object
year = year * yearSign;
try {
calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
calendar.set(year, month - 1, day, hour, minute, second);
calendar.set(Calendar.MILLISECOND, 0);
time = calendar.getTime().getTime();
time += (long) (fraction * 1000.0);
time -= tzSign * ((tzHour * 60) + tzMinute) * 60 * 1000;
if (localTime) {
time -= TimeZone.getDefault().getRawOffset();
}
return new Date(time);
} catch (IllegalArgumentException e) {
throw new ParseException(msg.getMessage("datetime.00", null), curPos);
}
}
/**
* Parse an integer value.
*
* @param str The String
containing the digits.
* @param curPos The starting position.
* @param digits The number of digist making up the integer value.
* @return int The integer representation of the digits contained in
* str
.
* @throws ParseException Parsing the integer value failed.
*/
private static int parseInt(String str, int curPos, int digits)
throws ParseException {
ensureDigits(str, curPos, digits);
return Integer.parseInt(str.substring(curPos, curPos + digits));
}
/**
* Count the number of digits following curPos
.
*
* @param str The String
in which to count digits.
* @param curPos The starting position.
* @return int The number of digits.
*/
private static int countDigits(String str, int curPos) {
int i;
for (i = curPos; i < str.length() && Character.isDigit(str.charAt(i)); i++);
return i - curPos;
}
/**
* Ensure that a value falls in a given min/max range.
*
* @param value The value to check.
* @param min The minimum allowed value.
* @param max The maximum allowed value.
* @param curPos To indicate the parsing position in the
* ParseException
.
* @throws ParseException Thrown, if value < min || value >
* max
*/
private static void ensureValue(int value, int min, int max, int curPos)
throws ParseException {
if (value < min || value > max) {
throw new ParseException(msg.getMessage("datetime.00", null), curPos);
}
}
/**
* Ensure that the given String
has a number of characters left.
*
* @param str The String
to check for its length.
* @param curPos The starting position.
* @param count The minimum number of characters that str
must
* contain, starting at from curPos
.
* @throws ParseException Thrown, if
* curPos + count > str.length()
.
*/
private static void ensureChars(String str, int curPos, int count)
throws ParseException {
if (curPos + count > str.length()) {
throw new ParseException(msg.getMessage("datetime.00", null), curPos);
}
}
/**
* Ensure that a given String
contains a certain character at a
* certain position.
*
* @param str The String
in which to look up the character.
* @param curPos The position in str
that must contain the
* character.
* @param c The character value that must be contained at position
* curPos
.
* @throws ParseException Thrown, if the characters do not match or
* curPos
is out of range.
*/
private static void ensureChar(String str, int curPos, char c)
throws ParseException {
ensureChars(str, curPos, 1);
if (str.charAt(curPos) != c) {
throw new ParseException(msg.getMessage("datetime.00", null), curPos);
}
}
/**
* Ensure that a given String
contains a number of digits,
* starting at a given position.
*
* @param str The String
to scan for digits.
* @param curPos The starting postion.
* @param count The number of digits that must be contained in
* str
, starting at curPos
.
* @throws ParseException Thrown, if str
is not long enough, or
* one of the characters following curPos
in str
is
* not a digit.
*/
private static void ensureDigits(String str, int curPos, int count)
throws ParseException {
ensureChars(str, curPos, count);
for (int i = curPos; i < curPos + count; i++) {
if (!Character.isDigit(str.charAt(i))) {
throw new ParseException(msg.getMessage("datetime.00", null), curPos);
}
}
}
/**
* Calculates the age if date of birth is given (for a calendar time stamp)
* @param dateOfBirth Date of Birth
* @param now Calendar time stamp at which the age needs to be calculated for
* @return Age of a person
*/
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;
}
/**
* Calculates the age if date of birth is given as Calendar object
* @param dateOfBirth Date of Birth as Calendar object
* @return Age of a person
*/
public static int calcAge(Calendar dateOfBirth) {
return calcAge(dateOfBirth, Calendar.getInstance());
}
/**
* Calculates the age if date of birth is given (for a date time stamp)
* @param dateOfBirth Date of Birth
* @param now Date time stamp at which the age needs to be calculated for
* @return Age of a person
*/
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);
}
/**
* Calculates the age if date of birth is given as Date object
* @param dateOfBirth Date of Birth as Date object
* @return Age of a person
*/
public static int calcAge(Date dateOfBirth) {
return calcAge(dateOfBirth, new Date());
}
public static String formatPEPSDateToMOADate(String pepsDate) {
if (StringUtils.isEmpty(pepsDate)) {
return null;
}
DateTimeFormatter fmt = null;
switch (pepsDate.length()) {
case 4:
fmt = DateTimeFormat.forPattern("yyyy");
break;
case 6:
fmt = DateTimeFormat.forPattern("yyyyMM");
break;
case 8:
fmt = DateTimeFormat.forPattern("yyyyMMdd");
break;
default:
fmt = DateTimeFormat.forPattern("yyyy-MM-dd");
break;
}
DateTime dt = fmt.parseDateTime(pepsDate);
DateTimeFormatter fmt2 = DateTimeFormat.forPattern("yyyy-MM-dd");
return fmt2.print(dt);
}
/**
* Returns a date as String using a provided format
* @param format Format the date/time should be returned
* @return Date/Time as String formatted according the provided format
*/
public static String getDateTimeWithFormat(String format) {
DateFormat dateFormat = new SimpleDateFormat(format);
Date date = new Date();
return dateFormat.format(date);
}
}