1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.ws.security.util;
21
22 import java.text.DateFormat;
23 import java.text.FieldPosition;
24 import java.text.ParsePosition;
25 import java.text.SimpleDateFormat;
26 import java.text.ParseException;
27 import java.util.Date;
28 import java.util.TimeZone;
29
30
31
32
33
34
35
36
37
38 public class XmlSchemaDateFormat extends DateFormat {
39
40
41
42 private static final long serialVersionUID = 5152684993503882396L;
43
44
45
46
47 private static final org.apache.commons.logging.Log LOG =
48 org.apache.commons.logging.LogFactory.getLog(XmlSchemaDateFormat.class);
49
50
51
52
53 private static final DateFormat DATEFORMAT_XSD_ZULU = new SimpleDateFormat(
54 "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
55
56 static {
57 DATEFORMAT_XSD_ZULU.setTimeZone(TimeZone.getTimeZone("UTC"));
58 }
59
60 @Override
61 public void setLenient(boolean lenient) {
62 DATEFORMAT_XSD_ZULU.setLenient(lenient);
63 }
64
65
66
67
68
69
70
71
72 public Date parse(String src, ParsePosition parsePos) {
73 Date date;
74
75
76 int index = 0;
77 try {
78 if (src != null) {
79 if ((src.charAt(0) == '+') || (src.charAt(0) == '-')) {
80 src = src.substring(1);
81 }
82
83 if (src.length() < 19) {
84 parsePos.setIndex(src.length() - 1);
85 handleParseError(parsePos, "TOO_FEW_CHARS");
86 }
87 validateChar(src, parsePos, index = 4, '-', "EXPECTED_DASH");
88 validateChar(src, parsePos, index = 7, '-', "EXPECTED_DASH");
89 validateChar(src, parsePos, index = 10, 'T', "EXPECTED_CAPITAL_T");
90 validateChar(src, parsePos, index = 13, ':', "EXPECTED_COLON_IN_TIME");
91 validateChar(src, parsePos, index = 16, ':', "EXPECTED_COLON_IN_TIME");
92 }
93
94
95 synchronized (DATEFORMAT_XSD_ZULU) {
96 date = DATEFORMAT_XSD_ZULU.parse((src == null) ? null
97 : (src.substring(0, 19) + ".000Z"));
98 }
99
100 index = 19;
101
102
103 if (src != null) {
104 if ((index < src.length()) && (src.charAt(index) == '.')) {
105 int milliseconds = 0;
106 int start = ++index;
107
108 while ((index < src.length())
109 && Character.isDigit(src.charAt(index))) {
110 index++;
111 }
112
113 String decimal = src.substring(start, index);
114
115 if (decimal.length() == 3) {
116 milliseconds = Integer.parseInt(decimal);
117 } else if (decimal.length() < 3) {
118 milliseconds = Integer.parseInt((decimal + "000")
119 .substring(0, 3));
120 } else {
121 milliseconds = Integer
122 .parseInt(decimal.substring(0, 3));
123
124 if (decimal.charAt(3) >= '5') {
125 ++milliseconds;
126 }
127 }
128
129
130 date.setTime(date.getTime() + milliseconds);
131 }
132
133
134 if (((index + 5) < src.length())
135 && ((src.charAt(index) == '+') || (src.charAt(index) == '-'))) {
136 validateCharIsDigit(src, parsePos, index + 1, "EXPECTED_NUMERAL");
137 validateCharIsDigit(src, parsePos, index + 2, "EXPECTED_NUMERAL");
138 validateChar(src, parsePos, index + 3, ':', "EXPECTED_COLON_IN_TIMEZONE");
139 validateCharIsDigit(src, parsePos, index + 4, "EXPECTED_NUMERAL");
140 validateCharIsDigit(src, parsePos, index + 5, "EXPECTED_NUMERAL");
141
142 final int hours = (((src.charAt(index + 1) - '0') * 10) + src
143 .charAt(index + 2)) - '0';
144 final int mins = (((src.charAt(index + 4) - '0') * 10) + src
145 .charAt(index + 5)) - '0';
146 int millisecs = ((hours * 60) + mins) * 60 * 1000;
147
148
149 if (src.charAt(index) == '+') {
150 millisecs = -millisecs;
151 }
152
153 date.setTime(date.getTime() + millisecs);
154 index += 6;
155 }
156
157 if ((index < src.length()) && (src.charAt(index) == 'Z')) {
158 index++;
159 }
160
161 if (index < src.length()) {
162 handleParseError(parsePos, "TOO_MANY_CHARS");
163 }
164 }
165 } catch (ParseException pe) {
166 LOG.error(pe.toString(), pe);
167 index = 0;
168 parsePos.setErrorIndex(index);
169 date = null;
170 }
171 parsePos.setIndex(index);
172 return date;
173 }
174
175
176
177
178 public StringBuffer format(Date date, StringBuffer appendBuf,
179 FieldPosition fieldPos) {
180 String str;
181
182 synchronized (DATEFORMAT_XSD_ZULU) {
183 str = DATEFORMAT_XSD_ZULU.format(date);
184 }
185
186 if (appendBuf == null) {
187 appendBuf = new StringBuffer();
188 }
189
190 appendBuf.append(str);
191
192 return appendBuf;
193 }
194
195 private void validateChar(String str, ParsePosition parsePos, int index,
196 char expected, String errorReason) throws ParseException {
197 if (str.charAt(index) != expected) {
198 handleParseError(parsePos, errorReason);
199 }
200 }
201
202 private void validateCharIsDigit(String str, ParsePosition parsePos,
203 int index, String errorReason) throws ParseException {
204 if (!Character.isDigit(str.charAt(index))) {
205 handleParseError(parsePos, errorReason);
206 }
207 }
208
209 private void handleParseError(ParsePosition parsePos, String errorReason)
210 throws ParseException {
211 throw new ParseException(
212 "INVALID_XSD_DATETIME: " + errorReason,
213 parsePos.getErrorIndex()
214 );
215 }
216
217 }