1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.fosstrak.epcis.utils;
22
23 import java.sql.Timestamp;
24 import java.text.DecimalFormat;
25 import java.text.ParseException;
26 import java.util.Calendar;
27 import java.util.Date;
28 import java.util.GregorianCalendar;
29 import java.util.TimeZone;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public final class TimeParser {
59
60
61
62
63 private static final DecimalFormat XX_FORMAT = new DecimalFormat("00");
64 private static final DecimalFormat XXX_FORMAT = new DecimalFormat("000");
65 private static final DecimalFormat XXXX_FORMAT = new DecimalFormat("0000");
66
67
68
69
70 private TimeParser() {
71 }
72
73
74
75
76
77
78
79
80
81
82 public static Calendar parseAsCalendar(final String text) throws ParseException {
83 return parse(text);
84 }
85
86
87
88
89
90
91
92
93
94
95 public static Date parseAsDate(final String text) throws ParseException {
96 return parse(text).getTime();
97 }
98
99
100
101
102
103
104
105
106
107
108
109 public static Timestamp parseAsTimestamp(final String text) throws ParseException {
110 return convert(parse(text));
111 }
112
113
114
115
116
117
118
119
120
121
122 private static Calendar parse(final String text) throws ParseException {
123 try {
124 String time = text;
125 if (time == null || time.length() == 0) {
126 throw new IllegalArgumentException("Date/Time string may not be null or empty.");
127 }
128 time = time.trim();
129 char sign;
130 int curPos;
131 if (time.startsWith("-")) {
132 sign = '-';
133 curPos = 1;
134 } else if (time.startsWith("+")) {
135 sign = '+';
136 curPos = 1;
137 } else {
138 sign = '+';
139 curPos = 0;
140 }
141
142 int year, month, day, hour, min, sec, ms;
143 String tzID;
144 char delimiter;
145
146
147 try {
148 year = Integer.parseInt(time.substring(curPos, curPos + 4));
149 } catch (NumberFormatException e) {
150 throw new ParseException("Year (YYYY) has wrong format: " + e.getMessage(), curPos);
151 }
152 curPos += 4;
153 delimiter = '-';
154 if (curPos >= time.length() || time.charAt(curPos) != delimiter) {
155 throw new ParseException("expected delimiter '" + delimiter + "' at position " + curPos, curPos);
156 }
157 curPos++;
158
159
160 try {
161 month = Integer.parseInt(time.substring(curPos, curPos + 2));
162 } catch (NumberFormatException e) {
163 throw new ParseException("Month (MM) has wrong format: " + e.getMessage(), curPos);
164 }
165 curPos += 2;
166 delimiter = '-';
167 if (curPos >= time.length() || time.charAt(curPos) != delimiter) {
168 throw new ParseException("expected delimiter '" + delimiter + "' at position " + curPos, curPos);
169 }
170 curPos++;
171
172
173 try {
174 day = Integer.parseInt(time.substring(curPos, curPos + 2));
175 } catch (NumberFormatException e) {
176 throw new ParseException("Day (DD) has wrong format: " + e.getMessage(), curPos);
177 }
178 curPos += 2;
179 delimiter = 'T';
180 if (curPos >= time.length() || time.charAt(curPos) != delimiter) {
181 throw new ParseException("expected delimiter '" + delimiter + "' at position " + curPos, curPos);
182 }
183 curPos++;
184
185
186 try {
187 hour = Integer.parseInt(time.substring(curPos, curPos + 2));
188 } catch (NumberFormatException e) {
189 throw new ParseException("Hour (hh) has wrong format: " + e.getMessage(), curPos);
190 }
191 curPos += 2;
192 delimiter = ':';
193 if (curPos >= time.length() || time.charAt(curPos) != delimiter) {
194 throw new ParseException("expected delimiter '" + delimiter + "' at position " + curPos, curPos);
195 }
196 curPos++;
197
198
199 try {
200 min = Integer.parseInt(time.substring(curPos, curPos + 2));
201 } catch (NumberFormatException e) {
202 throw new ParseException("Minute (mm) has wrong format: " + e.getMessage(), curPos);
203 }
204 curPos += 2;
205 delimiter = ':';
206 if (curPos >= time.length() || time.charAt(curPos) != delimiter) {
207 throw new ParseException("expected delimiter '" + delimiter + "' at position " + curPos, curPos);
208 }
209 curPos++;
210
211
212 try {
213 sec = Integer.parseInt(time.substring(curPos, curPos + 2));
214 } catch (NumberFormatException e) {
215 throw new ParseException("Second (ss) has wrong format: " + e.getMessage(), curPos);
216 }
217 curPos += 2;
218
219
220 delimiter = '.';
221 if (curPos < time.length() && time.charAt(curPos) == delimiter) {
222 curPos++;
223 try {
224
225 StringBuilder millis = new StringBuilder();
226 while (curPos < time.length() && isNumeric(time.charAt(curPos))) {
227 millis.append(time.charAt(curPos));
228 curPos++;
229 }
230
231 if (millis.length() == 1) {
232 ms = 100 * Integer.parseInt(millis.toString());
233 } else if (millis.length() == 2) {
234 ms = 10 * Integer.parseInt(millis.toString());
235 } else if (millis.length() >= 3) {
236 ms = Integer.parseInt(millis.substring(0, 3));
237 if (millis.length() > 3) {
238
239 if (Integer.parseInt(String.valueOf(millis.charAt(3))) >= 5) {
240 ms++;
241 }
242 }
243 } else {
244 ms = 0;
245 }
246 } catch (NumberFormatException e) {
247 throw new ParseException("Millisecond (S) has wrong format: " + e.getMessage(), curPos);
248 }
249 } else {
250 ms = 0;
251 }
252
253
254 if (curPos < time.length() && (time.charAt(curPos) == '+' || time.charAt(curPos) == '-')) {
255
256 tzID = "GMT" + time.substring(curPos);
257 } else if (curPos < time.length() && time.substring(curPos).equals("Z")) {
258 tzID = "UTC";
259 } else {
260
261
262
263 tzID = "UTC";
264 }
265
266 TimeZone tz = TimeZone.getTimeZone(tzID);
267
268 if (!tz.getID().equals(tzID)) {
269 throw new ParseException("invalid time zone '" + tzID + "'", curPos);
270 }
271
272
273 Calendar cal = GregorianCalendar.getInstance(tz);
274 cal.setLenient(false);
275 if (sign == '-' || year == 0) {
276
277 cal.set(Calendar.YEAR, year + 1);
278 cal.set(Calendar.ERA, GregorianCalendar.BC);
279 } else {
280 cal.set(Calendar.YEAR, year);
281 cal.set(Calendar.ERA, GregorianCalendar.AD);
282 }
283 cal.set(Calendar.MONTH, month - 1);
284 cal.set(Calendar.DAY_OF_MONTH, day);
285 cal.set(Calendar.HOUR_OF_DAY, hour);
286 cal.set(Calendar.MINUTE, min);
287 cal.set(Calendar.SECOND, sec);
288 cal.set(Calendar.MILLISECOND, ms);
289
290
291
292 cal.getTime();
293
294 return cal;
295 } catch (StringIndexOutOfBoundsException e) {
296 throw new ParseException("date/time value has invalid format", -1);
297 }
298 }
299
300
301
302
303
304
305
306
307
308 public static String format(final Date date) {
309 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
310 cal.setTimeInMillis(date.getTime());
311 return format(cal);
312 }
313
314
315
316
317
318
319
320
321
322 public static String format(final Timestamp ts) {
323 return format(convert(ts));
324 }
325
326
327
328
329
330
331
332
333
334 public static String format(final Calendar cal) {
335 if (cal == null) {
336 throw new IllegalArgumentException("argument can not be null");
337 }
338
339
340 int year = cal.get(Calendar.YEAR);
341 if (cal.isSet(Calendar.ERA) && cal.get(Calendar.ERA) == GregorianCalendar.BC) {
342
343
344
345
346 year = 0 - year + 1;
347 }
348
349
350
351
352
353
354 StringBuilder buf = new StringBuilder();
355
356 buf.append(XXXX_FORMAT.format(year));
357 buf.append('-');
358
359 buf.append(XX_FORMAT.format(cal.get(Calendar.MONTH) + 1));
360 buf.append('-');
361
362 buf.append(XX_FORMAT.format(cal.get(Calendar.DAY_OF_MONTH)));
363 buf.append('T');
364
365 buf.append(XX_FORMAT.format(cal.get(Calendar.HOUR_OF_DAY)));
366 buf.append(':');
367
368 buf.append(XX_FORMAT.format(cal.get(Calendar.MINUTE)));
369 buf.append(':');
370
371 buf.append(XX_FORMAT.format(cal.get(Calendar.SECOND)));
372 buf.append('.');
373
374 buf.append(XXX_FORMAT.format(cal.get(Calendar.MILLISECOND)));
375
376 TimeZone tz = cal.getTimeZone();
377
378 int offset = tz.getOffset(cal.getTimeInMillis());
379 if (offset != 0) {
380 int hours = Math.abs((offset / (60 * 1000)) / 60);
381 int minutes = Math.abs((offset / (60 * 1000)) % 60);
382 buf.append(offset < 0 ? '-' : '+');
383 buf.append(XX_FORMAT.format(hours));
384 buf.append(':');
385 buf.append(XX_FORMAT.format(minutes));
386 } else {
387 buf.append('Z');
388 }
389 return buf.toString();
390 }
391
392
393
394
395
396
397
398
399
400 private static boolean isNumeric(final char c) {
401 return (((c >= '0') && (c <= '9')) ? true : false);
402 }
403
404
405
406
407
408
409
410
411 public static Calendar convert(final Timestamp ts) {
412 Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
413 cal.setTimeInMillis(ts.getTime());
414 return cal;
415 }
416
417
418
419
420
421
422
423
424 public static Timestamp convert(final Calendar cal) {
425 return new Timestamp(cal.getTimeInMillis());
426 }
427
428 }