001: // DateParser.java
002: // $Id: DateParser.java,v 1.5 2005/05/16 10:19:19 ylafon Exp $
003: // (c) COPYRIGHT MIT, INRIA and Keio, 2000.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005: package org.w3c.util;
006:
007: import java.util.Calendar;
008: import java.util.Date;
009: import java.util.GregorianCalendar;
010: import java.util.NoSuchElementException;
011: import java.util.StringTokenizer;
012: import java.util.TimeZone;
013:
014: /**
015: * Date parser for ISO 8601 format
016: * http://www.w3.org/TR/1998/NOTE-datetime-19980827
017: * @version $Revision: 1.5 $
018: * @author Benoît Mahé (bmahe@w3.org)
019: * @author Yves Lafon (ylafon@w3.org)
020: */
021: public class DateParser {
022:
023: private static boolean check(StringTokenizer st, String token)
024: throws InvalidDateException {
025: try {
026: if (st.nextToken().equals(token)) {
027: return true;
028: } else {
029: throw new InvalidDateException("Missing [" + token
030: + "]");
031: }
032: } catch (NoSuchElementException ex) {
033: return false;
034: }
035: }
036:
037: private static Calendar getCalendar(String isodate)
038: throws InvalidDateException {
039: // YYYY-MM-DDThh:mm:ss.sTZD
040: StringTokenizer st = new StringTokenizer(isodate, "-T:.+Z",
041: true);
042:
043: Calendar calendar = new GregorianCalendar(TimeZone
044: .getTimeZone("UTC"));
045: calendar.clear();
046: try {
047: // Year
048: if (st.hasMoreTokens()) {
049: int year = Integer.parseInt(st.nextToken());
050: calendar.set(Calendar.YEAR, year);
051: } else {
052: return calendar;
053: }
054: // Month
055: if (check(st, "-") && (st.hasMoreTokens())) {
056: int month = Integer.parseInt(st.nextToken()) - 1;
057: calendar.set(Calendar.MONTH, month);
058: } else {
059: return calendar;
060: }
061: // Day
062: if (check(st, "-") && (st.hasMoreTokens())) {
063: int day = Integer.parseInt(st.nextToken());
064: calendar.set(Calendar.DAY_OF_MONTH, day);
065: } else {
066: return calendar;
067: }
068: // Hour
069: if (check(st, "T") && (st.hasMoreTokens())) {
070: int hour = Integer.parseInt(st.nextToken());
071: calendar.set(Calendar.HOUR_OF_DAY, hour);
072: } else {
073: calendar.set(Calendar.HOUR_OF_DAY, 0);
074: calendar.set(Calendar.MINUTE, 0);
075: calendar.set(Calendar.SECOND, 0);
076: calendar.set(Calendar.MILLISECOND, 0);
077: return calendar;
078: }
079: // Minutes
080: if (check(st, ":") && (st.hasMoreTokens())) {
081: int minutes = Integer.parseInt(st.nextToken());
082: calendar.set(Calendar.MINUTE, minutes);
083: } else {
084: calendar.set(Calendar.MINUTE, 0);
085: calendar.set(Calendar.SECOND, 0);
086: calendar.set(Calendar.MILLISECOND, 0);
087: return calendar;
088: }
089:
090: //
091: // Not mandatory now
092: //
093:
094: // Secondes
095: if (!st.hasMoreTokens()) {
096: return calendar;
097: }
098: String tok = st.nextToken();
099: if (tok.equals(":")) { // secondes
100: if (st.hasMoreTokens()) {
101: int secondes = Integer.parseInt(st.nextToken());
102: calendar.set(Calendar.SECOND, secondes);
103: if (!st.hasMoreTokens()) {
104: return calendar;
105: }
106: // frac sec
107: tok = st.nextToken();
108: if (tok.equals(".")) {
109: // bug fixed, thx to Martin Bottcher
110: String nt = st.nextToken();
111: while (nt.length() < 3) {
112: nt += "0";
113: }
114: nt = nt.substring(0, 3); //Cut trailing chars..
115: int millisec = Integer.parseInt(nt);
116: //int millisec = Integer.parseInt(st.nextToken()) * 10;
117: calendar.set(Calendar.MILLISECOND, millisec);
118: if (!st.hasMoreTokens()) {
119: return calendar;
120: }
121: tok = st.nextToken();
122: } else {
123: calendar.set(Calendar.MILLISECOND, 0);
124: }
125: } else {
126: throw new InvalidDateException(
127: "No secondes specified");
128: }
129: } else {
130: calendar.set(Calendar.SECOND, 0);
131: calendar.set(Calendar.MILLISECOND, 0);
132: }
133: // Timezone
134: if (!tok.equals("Z")) { // UTC
135: if (!(tok.equals("+") || tok.equals("-"))) {
136: throw new InvalidDateException(
137: "only Z, + or - allowed");
138: }
139: boolean plus = tok.equals("+");
140: if (!st.hasMoreTokens()) {
141: throw new InvalidDateException("Missing hour field");
142: }
143: int tzhour = Integer.parseInt(st.nextToken());
144: int tzmin = 0;
145: if (check(st, ":") && (st.hasMoreTokens())) {
146: tzmin = Integer.parseInt(st.nextToken());
147: } else {
148: throw new InvalidDateException(
149: "Missing minute field");
150: }
151: if (plus) {
152: calendar.add(Calendar.HOUR, -tzhour);
153: calendar.add(Calendar.MINUTE, -tzmin);
154: } else {
155: calendar.add(Calendar.HOUR, tzhour);
156: calendar.add(Calendar.MINUTE, tzmin);
157: }
158: }
159: } catch (NumberFormatException ex) {
160: throw new InvalidDateException("[" + ex.getMessage()
161: + "] is not an integer");
162: }
163: return calendar;
164: }
165:
166: /**
167: * Parse the given string in ISO 8601 format and build a Date object.
168: * @param isodate the date in ISO 8601 format
169: * @return a Date instance
170: * @exception InvalidDateException if the date is not valid
171: */
172: public static Date parse(String isodate)
173: throws InvalidDateException {
174: Calendar calendar = getCalendar(isodate);
175: return calendar.getTime();
176: }
177:
178: private static String twoDigit(int i) {
179: if (i >= 0 && i < 10) {
180: return "0" + String.valueOf(i);
181: }
182: return String.valueOf(i);
183: }
184:
185: /**
186: * Generate a ISO 8601 date
187: * @param date a Date instance
188: * @return a string representing the date in the ISO 8601 format
189: */
190: public static String getIsoDate(Date date) {
191: Calendar calendar = new GregorianCalendar(TimeZone
192: .getTimeZone("UTC"));
193: calendar.setTime(date);
194: StringBuffer buffer = new StringBuffer();
195: buffer.append(calendar.get(Calendar.YEAR));
196: buffer.append("-");
197: buffer.append(twoDigit(calendar.get(Calendar.MONTH) + 1));
198: buffer.append("-");
199: buffer.append(twoDigit(calendar.get(Calendar.DAY_OF_MONTH)));
200: buffer.append("T");
201: buffer.append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY)));
202: buffer.append(":");
203: buffer.append(twoDigit(calendar.get(Calendar.MINUTE)));
204: buffer.append(":");
205: buffer.append(twoDigit(calendar.get(Calendar.SECOND)));
206: buffer.append(".");
207: buffer
208: .append(twoDigit(calendar.get(Calendar.MILLISECOND) / 10));
209: buffer.append("Z");
210: return buffer.toString();
211: }
212:
213: /**
214: * Generate a ISO 8601 date
215: * @param date a Date instance
216: * @return a string representing the date in the ISO 8601 format
217: */
218: public static String getIsoDateNoMillis(Date date) {
219: Calendar calendar = new GregorianCalendar(TimeZone
220: .getTimeZone("UTC"));
221: calendar.setTime(date);
222: StringBuffer buffer = new StringBuffer();
223: buffer.append(calendar.get(Calendar.YEAR));
224: buffer.append("-");
225: buffer.append(twoDigit(calendar.get(Calendar.MONTH) + 1));
226: buffer.append("-");
227: buffer.append(twoDigit(calendar.get(Calendar.DAY_OF_MONTH)));
228: buffer.append("T");
229: buffer.append(twoDigit(calendar.get(Calendar.HOUR_OF_DAY)));
230: buffer.append(":");
231: buffer.append(twoDigit(calendar.get(Calendar.MINUTE)));
232: buffer.append(":");
233: buffer.append(twoDigit(calendar.get(Calendar.SECOND)));
234: buffer.append("Z");
235: return buffer.toString();
236: }
237:
238: public static void test(String isodate) {
239: System.out.println("----------------------------------");
240: try {
241: Date date = parse(isodate);
242: System.out.println(">> " + isodate);
243: System.out.println(">> " + date.toString() + " ["
244: + date.getTime() + "]");
245: System.out.println(">> " + getIsoDate(date));
246: } catch (InvalidDateException ex) {
247: System.err.println(isodate + " is invalid");
248: System.err.println(ex.getMessage());
249: }
250: System.out.println("----------------------------------");
251: }
252:
253: public static void test(Date date) {
254: String isodate = null;
255: System.out.println("----------------------------------");
256: try {
257: System.out.println(">> " + date.toString() + " ["
258: + date.getTime() + "]");
259: isodate = getIsoDate(date);
260: System.out.println(">> " + isodate);
261: date = parse(isodate);
262: System.out.println(">> " + date.toString() + " ["
263: + date.getTime() + "]");
264: } catch (InvalidDateException ex) {
265: System.err.println(isodate + " is invalid");
266: System.err.println(ex.getMessage());
267: }
268: System.out.println("----------------------------------");
269: }
270:
271: public static void main(String args[]) {
272: test("1997-07-16T19:20:30.45-02:00");
273: test("1997-07-16T19:20:30+01:00");
274: test("1997-07-16T19:20:30+01:00");
275: test("1997-07-16T12:20:30-06:00");
276: test("1997-07-16T19:20");
277: test("1997-07-16");
278: test("1997-07");
279: test("1997");
280: test(new Date());
281: }
282:
283: }
|