sbStringUtils.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 #include "sbStringUtils.h"
26 
27 #include <prprf.h>
28 #include <nsDataHashtable.h>
29 #include <nsIStringBundle.h>
30 #include <nsIStringEnumerator.h>
31 #include <nsICharsetConverterManager.h>
32 #include <nsIUnicodeDecoder.h>
33 #include <nsServiceManagerUtils.h>
34 #include <sbIStringBundleService.h>
35 #include <sbMemoryUtils.h>
36 
37 PRInt32
38 nsString_FindCharInSet(const nsAString& aString,
39  const char *aPattern,
40  PRInt32 aOffset)
41 {
42  const PRUnichar *begin, *end;
43  aString.BeginReading(&begin, &end);
44  for (const PRUnichar *current = begin + aOffset; current < end; ++current)
45  {
46  for (const char *pattern = aPattern; *pattern; ++pattern)
47  {
48  if (NS_UNLIKELY(*current == PRUnichar(*pattern)))
49  {
50  return current - begin;
51  }
52  }
53  }
54  return -1;
55 }
56 
57 void
58 AppendInt(nsAString& str, PRUint64 val)
59 {
60  char buf[32];
61  PR_snprintf(buf, sizeof(buf), "%llu", val);
62  str.Append(NS_ConvertASCIItoUTF16(buf));
63 }
64 
65 PRInt64
66 nsString_ToInt64(const nsAString& str, nsresult* rv)
67 {
68  PRInt64 result;
69  NS_LossyConvertUTF16toASCII narrow(str);
70  PRInt32 converted = PR_sscanf(narrow.get(), "%lld", &result);
71  if (converted != 1) {
72  if (rv) {
73  *rv = NS_ERROR_INVALID_ARG;
74  }
75  return 0;
76  }
77 
78 #ifdef DEBUG
79  nsString check;
80  AppendInt(check, result);
81  NS_ASSERTION(check.Equals(str), "Conversion failed");
82 #endif
83 
84  if (rv) {
85  *rv = NS_OK;
86  }
87  return result;
88 }
89 
90 PRUint64
91 nsString_ToUint64(const nsAString& str, nsresult* rv)
92 {
93  PRUint64 result;
94  NS_LossyConvertUTF16toASCII narrow(str);
95  PRInt32 converted = PR_sscanf(narrow.get(), "%llu", &result);
96  if (converted != 1) {
97  if (rv) {
98  *rv = NS_ERROR_INVALID_ARG;
99  }
100  return 0;
101  }
102 
103 #ifdef DEBUG
104  nsString check;
105  AppendInt(check, result);
106  NS_ASSERTION(check.Equals(str), "Conversion failed");
107 #endif
108 
109  if (rv) {
110  *rv = NS_OK;
111  }
112  return result;
113 }
114 
119 void
120 SB_CompressWhitespace(nsAString& aString, PRBool aLeading, PRBool aTrailing)
121 {
122  PRUnichar *start;
123  PRUint32 len = NS_StringGetMutableData(aString, PR_UINT32_MAX, &start);
124  PRUnichar *end = start + len;
125  PRUnichar *from = start, *to = start;
126 
127  while (from < end && NS_IsAsciiWhitespace(*from))
128  from++;
129 
130  if (!aLeading)
131  to = from;
132 
133  while (from < end) {
134  PRUnichar theChar = *from++;
135  if (NS_IsAsciiWhitespace(theChar)) {
136  // We found a whitespace char, so skip over any more
137  while (from < end && NS_IsAsciiWhitespace(*from))
138  from++;
139 
140  // Turn all whitespace into spaces
141  theChar = ' ';
142  }
143 
144  if (from == end && theChar == ' ') {
145  to = from;
146  } else {
147  *to++ = theChar;
148  }
149  }
150 
151  // Drop any trailing space
152  if (aTrailing) {
153  while (to > start && to[-1] == ' ')
154  to--;
155  }
156 
157  // Re-terminate the string
158  *to = '\0';
159 
160  // Set the new length
161  aString.SetLength(to - start);
162 }
163 
164 nsresult
166  nsIStringEnumerator* aRight,
167  PRBool* _retval)
168 {
169  NS_ENSURE_ARG_POINTER(aLeft);
170  NS_ENSURE_ARG_POINTER(aRight);
171  NS_ENSURE_ARG_POINTER(_retval);
172 
173  nsresult rv;
174 
175  nsDataHashtable<nsStringHashKey, PRUint32> leftValues;
176  PRBool success = leftValues.Init();
177  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
178 
179  PRBool hasMore;
180  while (NS_SUCCEEDED(aLeft->HasMore(&hasMore)) && hasMore) {
181  nsString value;
182  rv = aLeft->GetNext(value);
183  NS_ENSURE_SUCCESS(rv, rv);
184 
185  PRUint32 count = 1;
186  if (leftValues.Get(value, &count)) {
187  count++;
188  }
189 
190  PRBool success = leftValues.Put(value, count);
191  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
192  }
193 
194  while (NS_SUCCEEDED(aRight->HasMore(&hasMore)) && hasMore) {
195  nsString value;
196  rv = aRight->GetNext(value);
197  NS_ENSURE_SUCCESS(rv, rv);
198 
199  PRUint32 count;
200  if (!leftValues.Get(value, &count)) {
201  *_retval = PR_FALSE;
202  return NS_OK;
203  }
204 
205  count--;
206  if (count) {
207  PRBool success = leftValues.Put(value, count);
208  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
209  }
210  else {
211  leftValues.Remove(value);
212  }
213  }
214 
215  if (leftValues.Count() == 0) {
216  *_retval = PR_TRUE;
217  }
218  else {
219  *_retval = PR_FALSE;
220  }
221 
222  return NS_OK;
223 }
224 
225 void nsString_ReplaceChar(/* inout */ nsAString& aString,
226  const nsAString& aOldChars,
227  const PRUnichar aNewChar)
228 {
229  PRUint32 length = aString.Length();
230  for (PRUint32 index = 0; index < length; index++) {
231  PRUnichar currentChar = aString.CharAt(index);
232  PRInt32 oldCharsIndex = aOldChars.FindChar(currentChar);
233  if (oldCharsIndex > -1)
234  aString.Replace(index, 1, aNewChar);
235  }
236 }
237 
238 void
239 nsCString_ReplaceChars(nsACString& aOldString,
240  const nsACString& aOldChars,
241  const char aNewChar)
242 {
243  PRUint32 length = aOldString.Length();
244  for (PRUint32 index = 0; index < length; index++) {
245  char currentChar = aOldString.CharAt(index);
246  PRInt32 oldCharsIndex = aOldChars.FindChar(currentChar);
247  if (oldCharsIndex > -1)
248  aOldString.Replace(index, 1, aNewChar);
249  }
250 }
251 
252 void nsString_ReplaceSubstring(/* inout */ nsAString &aString,
253  const nsAString &aOldString,
254  const nsAString &aNewString)
255 {
256  if (aOldString.Length() == 0) {
257  return;
258  }
259 
260  PRUint32 i = 0;
261  while (i < aString.Length())
262  {
263  PRInt32 r = aString.Find(aOldString, i);
264  if (r == -1)
265  break;
266 
267  aString.Replace(r, aOldString.Length(), aNewString);
268  i += r + aNewString.Length();
269  }
270 
271  return;
272 }
273 
274 PRBool IsLikelyUTF8(const nsACString& aString)
275 {
276  if (aString.IsEmpty()) {
277  return PR_TRUE;
278  }
279 
280  // number of bytes following given this prefix byte
281  // -1 = invalid prefix, is a continuation; -2 = invalid byte anywhere
282  const PRInt32 prefix_table[] = {
283  // 0 1 2 3 4 5 6 7 8 9 A B C D E F
284  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
285  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
286  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2
287  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
288  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
289  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
290  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
291  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
292  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8
293  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9
294  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A
295  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // B
296  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C
297  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D
298  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // E
299  3, 3, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 // F
300  };
301 
302  PRInt32 bytesRemaining = 0; // for multi-byte sequences
303  const nsACString::char_type *begin, *end;
304  aString.BeginReading(&begin, &end);
305 
306  for (; begin != end; ++begin) {
307  PRInt32 next = prefix_table[(unsigned char)(*begin)];
308  if (bytesRemaining) {
309  if (next != -1) {
310  // expected more bytes but didn't get a continuation
311  return PR_FALSE;
312  }
313  --bytesRemaining;
314  continue;
315  }
316 
317  // expecting the next byte to be a lead byte
318  if (next < 0) {
319  // but isn't
320  return PR_FALSE;
321  }
322 
323  bytesRemaining = next;
324  }
325  return PR_TRUE;
326 }
327 
328 PRBool IsUTF8(const nsACString& aString)
329 {
330  nsresult rv = NS_OK;
331  nsCOMPtr<nsICharsetConverterManager> converterManager =
332  do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
333  NS_ENSURE_SUCCESS(rv, PR_FALSE);
334 
335  nsCOMPtr<nsIUnicodeDecoder> decoder;
336  rv = converterManager->GetUnicodeDecoderRaw("UTF-8", getter_AddRefs(decoder));
337  NS_ENSURE_SUCCESS(rv, PR_FALSE);
338 
339  PRInt32 dataLen = aString.Length();
340  PRInt32 size;
341  rv = decoder->GetMaxLength(aString.BeginReading(), dataLen, &size);
342  NS_ENSURE_SUCCESS(rv, PR_FALSE);
343 
344  PRUnichar *wstr = reinterpret_cast< PRUnichar * >( NS_Alloc( (size + 1) * sizeof( PRUnichar ) ) );
345  rv = decoder->Convert(aString.BeginReading(), &dataLen, wstr, &size);
346  NS_Free(wstr);
347  NS_ENSURE_SUCCESS(rv, PR_FALSE);
348 
349  return PR_TRUE;
350 }
351 
352 void
353 nsString_Split(const nsAString& aString,
354  const nsAString& aDelimiter,
355  nsTArray<nsString>& aSubStringArray)
356 {
357  // Clear out sub-string array.
358  aSubStringArray.Clear();
359 
360  // Get the delimiter length. Just put the entire string in the array if the
361  // delimiter is empty.
362  PRUint32 delimiterLength = aDelimiter.Length();
363  if (delimiterLength == 0) {
364  aSubStringArray.AppendElement(aString);
365  return;
366  }
367 
368  // Split string into sub-strings.
369  PRInt32 stringLength = aString.Length();
370  PRInt32 currentOffset = 0;
371  PRInt32 delimiterIndex = 0;
372  do {
373  // Find the index of the next delimiter. If delimiter cannot be found, set
374  // the index to the end of the string.
375  delimiterIndex = aString.Find(aDelimiter, currentOffset);
376  if (delimiterIndex < 0)
377  delimiterIndex = stringLength;
378 
379  // Add the next sub-string to the array.
380  PRUint32 subStringLength = delimiterIndex - currentOffset;
381  if (subStringLength > 0) {
382  nsDependentSubstring subString(aString, currentOffset, subStringLength);
383  aSubStringArray.AppendElement(subString);
384  } else {
385  aSubStringArray.AppendElement(NS_LITERAL_STRING(""));
386  }
387 
388  // Advance to the next sub-string.
389  currentOffset = delimiterIndex + delimiterLength;
390  } while (delimiterIndex < stringLength);
391 }
392 
393 void
394 nsCString_Split(const nsACString& aString,
395  const nsACString& aDelimiter,
396  nsTArray<nsCString>& aSubStringArray)
397 {
398  // Clear out sub-string array.
399  aSubStringArray.Clear();
400 
401  // Get the delimiter length. Just put the entire string in the array if the
402  // delimiter is empty.
403  PRUint32 delimiterLength = aDelimiter.Length();
404  if (delimiterLength == 0) {
405  aSubStringArray.AppendElement(aString);
406  return;
407  }
408 
409  // Split string into sub-strings.
410  PRInt32 stringLength = aString.Length();
411  PRInt32 currentOffset = 0;
412  PRInt32 delimiterIndex = 0;
413  do {
414  // Find the index of the next delimiter. If delimiter cannot be found, set
415  // the index to the end of the string.
416  delimiterIndex = aString.Find(aDelimiter, currentOffset);
417  if (delimiterIndex < 0)
418  delimiterIndex = stringLength;
419 
420  // Add the next sub-string to the array.
421  PRUint32 subStringLength = delimiterIndex - currentOffset;
422  if (subStringLength > 0) {
423  nsDependentCSubstring subString(aString, currentOffset, subStringLength);
424  aSubStringArray.AppendElement(subString);
425  } else {
426  aSubStringArray.AppendElement(NS_LITERAL_CSTRING(""));
427  }
428 
429  // Advance to the next sub-string.
430  currentOffset = delimiterIndex + delimiterLength;
431  } while (delimiterIndex < stringLength);
432 }
433 
434 nsString SB_FormatISO8601TimeString(PRTime aTime)
435 {
436  PRExplodedTime exploded;
437  PR_ExplodeTime(aTime, PR_GMTParameters, &exploded);
438  char buffer[64];
439  PR_FormatTime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S.0Z", &exploded);
440  return NS_ConvertASCIItoUTF16(buffer);
441 }
442 
443 nsresult
444 SB_ParseISO8601TimeString(const nsAString& aISO8601TimeString,
445  PRTime* aTime)
446 {
447  // Validate arguments.
448  NS_ENSURE_ARG_POINTER(aTime);
449 
450  // Function variables.
451  nsTArray<nsCString> components;
452  PRStatus status;
453 
454  // Convert the ISO 8601 string to "MM-DD-YYYY HH:MM:SS.SSSS TZ" format so that
455  // PR_ParseTimeString can parse it.
456  // E.g., Convert "1970-01-31T01:02:03.4567Z" to
457  // "01-31-1970 01:02:03.4567 GMT".
458  //
459  // TODO: support other ISO 8601 formats.
460 
461  // Split the ISO 8601 time string into separate time and date components.
462  nsCAutoString
463  iso8601TimeString = NS_LossyConvertUTF16toASCII(aISO8601TimeString);
464  nsCString_Split(iso8601TimeString, NS_LITERAL_CSTRING("T"), components);
465  NS_ENSURE_TRUE(components.Length() == 2, NS_ERROR_INVALID_ARG);
466  nsCAutoString date = components[0];
467  nsCAutoString time = components[1];
468 
469  // Split the date into year, month, and day components.
470  nsCString_Split(date, NS_LITERAL_CSTRING("-"), components);
471  NS_ENSURE_TRUE(components.Length() == 3, NS_ERROR_INVALID_ARG);
472  nsCAutoString year = components[0];
473  nsCAutoString month = components[1];
474  nsCAutoString day = components[2];
475 
476  // Check for local or GMT timezone.
477  nsCAutoString timezone;
478  if (time[time.Length() - 1] == 'Z') {
479  timezone.Assign(NS_LITERAL_CSTRING(" GMT"));
480  time.SetLength(time.Length() - 1);
481  }
482 
483  // Produce the format for PR_ParseTimeString.
484  sbAutoSmprintf timeString = PR_smprintf("%s-%s-%s %s%s",
485  month.get(),
486  day.get(),
487  year.get(),
488  time.get(),
489  timezone.get());
490 
491  // Parse the time string.
492  status = PR_ParseTimeString(timeString, PR_FALSE, aTime);
493  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
494 
495  return NS_OK;
496 }
497 
512 nsresult
513 SBGetLocalizedString(nsAString& aString,
514  const nsAString& aKey,
515  const nsAString& aDefault,
516  class nsIStringBundle* aStringBundle)
517 {
518  nsresult rv;
519 
520  // Set default result.
521  if (!aDefault.IsVoid())
522  aString = aDefault;
523  else
524  aString = aKey;
525 
526  // Get the string bundle.
527  nsCOMPtr<nsIStringBundle> stringBundle = aStringBundle;
528 
529  // If no string bundle was provided, get the default string bundle.
530  if (!stringBundle) {
531  nsCOMPtr<nsIStringBundleService> stringBundleService =
532  do_GetService(SB_STRINGBUNDLESERVICE_CONTRACTID, &rv);
533  NS_ENSURE_SUCCESS(rv, rv);
534  rv = stringBundleService->CreateBundle(SB_STRING_BUNDLE_URL,
535  getter_AddRefs(stringBundle));
536  NS_ENSURE_SUCCESS(rv, rv);
537  }
538 
539  // Get the string from the bundle.
540  nsAutoString stringValue;
541  rv = stringBundle->GetStringFromName(aKey.BeginReading(),
542  getter_Copies(stringValue));
543  NS_ENSURE_SUCCESS(rv, rv);
544  aString = stringValue;
545 
546  return NS_OK;
547 }
548 
549 nsresult
550 SBGetLocalizedString(nsAString& aString,
551  const nsAString& aKey)
552 {
553  return SBGetLocalizedString(aString, aKey, SBVoidString());
554 }
555 
556 nsresult
557 SBGetLocalizedString(nsAString& aString,
558  const char* aKey,
559  const char* aDefault,
560  class nsIStringBundle* aStringBundle)
561 {
562  nsAutoString key;
563  if (aKey)
564  key = NS_ConvertUTF8toUTF16(aKey);
565  else
566  key = SBVoidString();
567 
568  nsAutoString defaultString;
569  if (aDefault)
570  defaultString = NS_ConvertUTF8toUTF16(aDefault);
571  else
572  defaultString = SBVoidString();
573 
574  return SBGetLocalizedString(aString, key, defaultString, aStringBundle);
575 }
576 
592 nsresult
593 SBGetLocalizedFormattedString(nsAString& aString,
594  const nsAString& aKey,
595  const nsTArray<nsString>& aParams,
596  const nsAString& aDefault,
597  class nsIStringBundle* aStringBundle)
598 {
599  nsresult rv;
600 
601  // Set default result.
602  if (!aDefault.IsVoid())
603  aString = aDefault;
604  else
605  aString = aKey;
606 
607  // Get the string bundle.
608  nsCOMPtr<nsIStringBundle> stringBundle = aStringBundle;
609 
610  // If no string bundle was provided, get the default string bundle.
611  if (!stringBundle) {
612  nsCOMPtr<nsIStringBundleService> stringBundleService =
613  do_GetService(SB_STRINGBUNDLESERVICE_CONTRACTID, &rv);
614  NS_ENSURE_SUCCESS(rv, rv);
615  rv = stringBundleService->CreateBundle(SB_STRING_BUNDLE_URL,
616  getter_AddRefs(stringBundle));
617  NS_ENSURE_SUCCESS(rv, rv);
618  }
619 
620  // Convert the parameter array to a new array and set the new array up for
621  // auto-disposal.
622  PRUint32 paramCount = aParams.Length();
623  const PRUnichar** params = static_cast<const PRUnichar**>
624  (NS_Alloc(paramCount * sizeof(PRUnichar*)));
625  NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
626  sbAutoNSTypePtr<const PRUnichar*> autoParams(params);
627  for (PRUint32 i = 0; i < paramCount; i++) {
628  params[i] = aParams[i].get();
629  }
630 
631  // Get the string from the bundle.
632  nsAutoString stringValue;
633  rv = stringBundle->FormatStringFromName(aKey.BeginReading(),
634  params,
635  paramCount,
636  getter_Copies(stringValue));
637  NS_ENSURE_SUCCESS(rv, rv);
638  aString = stringValue;
639 
640  return NS_OK;
641 }
642 
function start(ch)
return NS_OK
nsresult SBGetLocalizedFormattedString(nsAString &aString, const nsAString &aKey, const nsTArray< nsString > &aParams, const nsAString &aDefault, class nsIStringBundle *aStringBundle)
void nsCString_ReplaceChars(nsACString &aOldString, const nsACString &aOldChars, const char aNewChar)
_setDateDatepicker date
void SB_CompressWhitespace(nsAString &aString, PRBool aLeading, PRBool aTrailing)
nsresult SB_StringEnumeratorEquals(nsIStringEnumerator *aLeft, nsIStringEnumerator *aRight, PRBool *_retval)
PRUint64 nsString_ToUint64(const nsAString &str, nsresult *rv)
void nsString_ReplaceSubstring(nsAString &aString, const nsAString &aOldString, const nsAString &aNewString)
#define SB_STRINGBUNDLESERVICE_CONTRACTID
_changeFirstDay day
#define SB_STRING_BUNDLE_URL
void nsString_ReplaceChar(nsAString &aString, const nsAString &aOldChars, const PRUnichar aNewChar)
PRBool IsUTF8(const nsACString &aString)
restoreDimensions aLeft
var count
Definition: test_bug7406.js:32
void nsString_Split(const nsAString &aString, const nsAString &aDelimiter, nsTArray< nsString > &aSubStringArray)
this _dialogInput val(dateText)
nsString SB_FormatISO8601TimeString(PRTime aTime)
void nsCString_Split(const nsACString &aString, const nsACString &aDelimiter, nsTArray< nsCString > &aSubStringArray)
void AppendInt(nsAString &str, PRUint64 val)
function check(ch, cx)
const PR_UINT32_MAX
Definition: httpd.js:55
nsresult SBGetLocalizedString(nsAString &aString, const nsAString &aKey, const nsAString &aDefault, class nsIStringBundle *aStringBundle)
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
PRInt64 nsString_ToInt64(const nsAString &str, nsresult *rv)
_getSelectedPageStyle s i
PRInt32 nsString_FindCharInSet(const nsAString &aString, const char *aPattern, PRInt32 aOffset)
PRBool IsLikelyUTF8(const nsACString &aString)
function next()
nsresult SB_ParseISO8601TimeString(const nsAString &aISO8601TimeString, PRTime *aTime)