22
33package net .servicestack .client ;
44
5- import com .google .gson .Gson ;
6-
75import org .json .JSONArray ;
86import org .json .JSONException ;
97import org .json .JSONObject ;
1311import java .io .InputStream ;
1412import java .io .InputStreamReader ;
1513import java .io .UnsupportedEncodingException ;
14+ import java .lang .reflect .Field ;
15+ import java .lang .reflect .Modifier ;
1616import java .net .HttpURLConnection ;
1717import java .nio .ByteBuffer ;
18- import java .text .ParseException ;
1918import java .text .SimpleDateFormat ;
2019import java .util .ArrayList ;
20+ import java .util .Arrays ;
21+ import java .util .Calendar ;
2122import java .util .Date ;
2223import java .util .Iterator ;
24+ import java .util .List ;
2325import java .util .UUID ;
2426
2527// Generic Utils
@@ -161,6 +163,14 @@ protected SimpleDateFormat initialValue()
161163 }
162164 };
163165
166+ private static final ThreadLocal <SimpleDateFormat > iso8601FormatterWithMs = new ThreadLocal <SimpleDateFormat >(){
167+ @ Override
168+ protected SimpleDateFormat initialValue ()
169+ {
170+ return new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss.SSSZ" );
171+ }
172+ };
173+
164174 public static String toJsonDate (Date date ) {
165175 return "/Date(" + date .getTime () + "-0000)/" ;
166176 }
@@ -180,19 +190,153 @@ public static Date parseDate(String string) {
180190 return fromIsoDateString (string );
181191 }
182192
193+ public final static int isoDateLength = "YYYY-MM-DDT00:00:00+00:00" .length ();
194+ public final static int isoDateWithMsLength = "YYYY-MM-DDT00:00:00.000+00:00" .length ();
195+
196+ public final static int isoDateWithSubMsMin = "YYYY-MM-DDT00:00:00.0000+00:00" .length ();
197+ public final static int isoDateWithSubMsMax = "YYYY-MM-DDT00:00:00.0000000+00:00" .length ();
198+
199+ public static String stripSubMillis (String iso8601string ){
200+ if (iso8601string .length () < isoDateWithSubMsMin || iso8601string .length () > isoDateWithSubMsMax )
201+ return iso8601string ;
202+
203+ String [] parts = splitOnFirst (iso8601string , '.' );
204+
205+ String suffix = parts [1 ].substring (parts [1 ].length () - 6 ); //+00:00
206+ String ms = parts [1 ].substring (0 , 3 );
207+
208+ return parts [0 ] + "." + ms + suffix ;
209+ }
210+
183211 public static Date fromIsoDateString (String iso8601string ){
184212 if (iso8601string == null )
185213 return null ;
186214
187215 String s = iso8601string .replace ("Z" , "+00:00" );
188216 try {
189217 s = s .substring (0 , 22 ) + s .substring (23 ); // to get rid of the ":"
218+ s = stripSubMillis (s );
219+
220+ if (s .length () == isoDateWithMsLength )
221+ return iso8601FormatterWithMs .get ().parse (s );
222+
190223 return iso8601Formatter .get ().parse (s );
191- } catch (IndexOutOfBoundsException e ) {
192- throw new RuntimeException ("Invalid length" );
193- } catch (ParseException e ) {
194- throw new RuntimeException (e );
224+
225+ } catch (Exception e ) {
226+ return ParseManual (iso8601string );
227+ }
228+ }
229+
230+ public static Date ParseManual (String dateTimeStr )
231+ {
232+ if (dateTimeStr == null || dateTimeStr .length () < "yyyy-MM-dd" .length ())
233+ return null ;
234+
235+ if (dateTimeStr .endsWith ("Z" ))
236+ dateTimeStr = dateTimeStr .substring (0 , dateTimeStr .length () - 1 );
237+
238+ String [] parts = dateTimeStr .split ("T" );
239+ if (parts .length == 1 )
240+ parts = Utils .splitOnFirst (dateTimeStr , ' ' );
241+
242+ String [] dateParts = parts [0 ].split ("-" );
243+ int hh = 0 , min = 0 , ss = 0 , ms = 0 ;
244+ double subMs = 0 ;
245+ int offsetMultiplier = 0 ;
246+
247+ if (parts .length == 1 )
248+ {
249+ return new Date (
250+ Utils .tryParseInt (dateParts [0 ]) - 1900 ,
251+ Utils .tryParseInt (dateParts [1 ]) - 1 ,
252+ Utils .tryParseInt (dateParts [2 ]),
253+ 0 , 0 , 0 );
195254 }
255+ else if (parts .length == 2 )
256+ {
257+ String [] timeStringParts = parts [1 ].split ("\\ +" );
258+ if (timeStringParts .length == 2 )
259+ {
260+ offsetMultiplier = -1 ;
261+ }
262+ else
263+ {
264+ timeStringParts = parts [1 ].split ("-" );
265+ if (timeStringParts .length == 2 )
266+ {
267+ offsetMultiplier = 1 ;
268+ }
269+ }
270+
271+ String timeOffset = timeStringParts .length == 2 ? timeStringParts [1 ] : null ;
272+ String [] timeParts = timeStringParts [0 ].split (":" );
273+
274+ if (timeParts .length == 3 )
275+ {
276+ Integer val = null ;
277+ if ((val = Utils .tryParseInt (timeParts [0 ])) != null )
278+ hh = val ;
279+
280+ if ((val = Utils .tryParseInt (timeParts [1 ])) != null )
281+ min = val ;
282+
283+ String [] secParts = timeParts [2 ].split ("\\ ." );
284+
285+ if ((val = Utils .tryParseInt (secParts [0 ])) != null )
286+ ss = val ;
287+
288+ if (secParts .length == 2 )
289+ {
290+ String msStr = String .format ("%03d" , Utils .tryParseInt (secParts [1 ]));
291+ ms = Utils .tryParseInt (msStr .substring (0 , 3 ));
292+
293+ if (msStr .length () > 3 )
294+ {
295+ String subMsStr = msStr .substring (3 );
296+ // subMs = Utils.tryParseDouble(subMsStr) / Math.pow(10, subMsStr.length());
297+ }
298+ }
299+ }
300+
301+ Date dateTime = new Date (
302+ Utils .tryParseInt (dateParts [0 ]) - 1900 ,
303+ Utils .tryParseInt (dateParts [1 ]) - 1 ,
304+ Utils .tryParseInt (dateParts [2 ]),
305+ hh , min , ss );
306+
307+ Calendar cal = Calendar .getInstance ();
308+ cal .setTime (dateTime );
309+
310+ if (ms > 0 ){
311+ cal .add (Calendar .MILLISECOND , ms );
312+ }
313+
314+ // if (subMs != 0)
315+ // dateTime=dateTime.AddMilliseconds(subMs); //Doesn't support sub millis
316+
317+ if (offsetMultiplier != 0 && timeOffset != null )
318+ {
319+ timeParts = timeOffset .split (":" );
320+ if (timeParts .length == 2 )
321+ {
322+ hh = Utils .tryParseInt (timeParts [0 ]);
323+ min = Utils .tryParseInt (timeParts [1 ]);
324+ }
325+ else
326+ {
327+ hh = Utils .tryParseInt (timeOffset .substring (0 , 2 ));
328+ min = Utils .tryParseInt (timeOffset .substring (2 ));
329+ }
330+
331+ cal .add (Calendar .HOUR , offsetMultiplier * hh );
332+ cal .add (Calendar .MINUTE , offsetMultiplier * min );
333+ }
334+
335+ dateTime = cal .getTime ();
336+ return dateTime ;
337+ }
338+
339+ return null ;
196340 }
197341
198342 /*String Utils*/
0 commit comments