diff options
Diffstat (limited to 'common/src/at/gv/egovernment/moa/util/DateTimeUtils.java')
-rw-r--r-- | common/src/at/gv/egovernment/moa/util/DateTimeUtils.java | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/common/src/at/gv/egovernment/moa/util/DateTimeUtils.java b/common/src/at/gv/egovernment/moa/util/DateTimeUtils.java new file mode 100644 index 000000000..58cc04c4c --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/DateTimeUtils.java @@ -0,0 +1,326 @@ +package at.gv.egovernment.moa.util; + +import java.io.StringWriter; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + * Utility for parsing and building XML type <code>dateTime</code>, + * according to ISO 8601. + * + * @author Patrick Peck + * @version $Id$ + * @see <code>http://www.w3.org/2001/XMLSchema-datatypes"</code> + */ +public class DateTimeUtils { + /** Error messages. */ + private static MessageProvider msg = MessageProvider.getInstance(); + + /** + * Builds a <code>dateTime</code> value from a <code>Calendar</code> value. + * @param cal the <code>Calendar</code> value + * @return the <code>dateTime</code> value + */ + public static String buildDateTime(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))); + 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(); + } + + /** + * 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 <code>String</code> containing a date and time instant, given in + * ISO 8601 format. + * + * @param dateTime The <code>String</code> to parse. + * @return The <code>Date</code> representation of the contents of + * <code>dateTime</code>. + * @throws ParseException Parsing the <code>dateTime</code> 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 <code>String</code> 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 + * <code>str</code>. + * @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 <code>curPos</code>. + * + * @param str The <code>String</code> 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 + * <code>ParseException</code>. + * @throws ParseException Thrown, if <code>value < min || value > + * max</code> + */ + 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 <code>String</code> has a number of characters left. + * + * @param str The <code>String</code> to check for its length. + * @param curPos The starting position. + * @param count The minimum number of characters that <code>str</code> must + * contain, starting at from <code>curPos</code>. + * @throws ParseException Thrown, if + * <code>curPos + count > str.length()</code>. + */ + 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 <code>String</code> contains a certain character at a + * certain position. + * + * @param str The <code>String</code> in which to look up the character. + * @param curPos The position in <code>str</code> that must contain the + * character. + * @param c The character value that must be contained at position + * <code>curPos</code>. + * @throws ParseException Thrown, if the characters do not match or + * <code>curPos</code> 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 <code>String</code> contains a number of digits, + * starting at a given position. + * + * @param str The <code>String</code> to scan for digits. + * @param curPos The starting postion. + * @param count The number of digits that must be contained in + * <code>str</code>, starting at <code>curPos</code>. + * @throws ParseException Thrown, if <code>str</code> is not long enough, or + * one of the characters following <code>curPos</code> in <code>str</code> 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); + } + } + } + +} |