aboutsummaryrefslogtreecommitdiff
path: root/common/src/main/java/at/gv/egovernment/moa/util/DateTimeUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/main/java/at/gv/egovernment/moa/util/DateTimeUtils.java')
-rw-r--r--common/src/main/java/at/gv/egovernment/moa/util/DateTimeUtils.java326
1 files changed, 326 insertions, 0 deletions
diff --git a/common/src/main/java/at/gv/egovernment/moa/util/DateTimeUtils.java b/common/src/main/java/at/gv/egovernment/moa/util/DateTimeUtils.java
new file mode 100644
index 000000000..58cc04c4c
--- /dev/null
+++ b/common/src/main/java/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 &lt; min || value &gt;
+ * 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 &gt; 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);
+ }
+ }
+ }
+
+}