e5cb42cc154c89e05b4cf7b4cb8aca980d7af5e8
[shibboleth/cpp-xmltooling.git] / xmltooling / util / DateTime.cpp
1 /*
2  *  Copyright 2001-2007 Internet2
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * DateTime.cpp
19  *
20  * Manipulation of XML date/time data.
21  */
22
23 /*
24  * This is mostly copied from Xerces-C, but they don't seem inclined to produce a usable
25  * class, so I had to incorporate my own version of it for now. I can't inherit it
26  * since the fields I need are private.
27  */
28
29 #include "internal.h"
30 #include "util/DateTime.h"
31
32 #ifndef WIN32
33 # include <errno.h>
34 #endif
35
36 #include <ctime>
37 #include <sstream>
38 #include <assert.h>
39 #include <xercesc/util/Janitor.hpp>
40
41 using namespace xmltooling;
42 using namespace std;
43
44 //
45 // constants used to process raw data (fBuffer)
46 //
47 // [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}['Z']
48 //                                [{+|-}hh:mm']
49 //
50
51 static const XMLCh DURATION_STARTER     = chLatin_P;              // 'P'
52 static const XMLCh DURATION_Y           = chLatin_Y;              // 'Y'
53 static const XMLCh DURATION_M           = chLatin_M;              // 'M'
54 static const XMLCh DURATION_D           = chLatin_D;              // 'D'
55 static const XMLCh DURATION_H           = chLatin_H;              // 'H'
56 static const XMLCh DURATION_S           = chLatin_S;              // 'S'
57
58 static const XMLCh DATE_SEPARATOR       = chDash;                 // '-'
59 static const XMLCh TIME_SEPARATOR       = chColon;                // ':'
60 static const XMLCh TIMEZONE_SEPARATOR   = chColon;                // ':'
61 static const XMLCh DATETIME_SEPARATOR   = chLatin_T;              // 'T'
62 static const XMLCh MILISECOND_SEPARATOR = chPeriod;               // '.'
63
64 static const XMLCh UTC_STD_CHAR         = chLatin_Z;              // 'Z'
65 static const XMLCh UTC_POS_CHAR         = chPlus;                 // '+'
66 static const XMLCh UTC_NEG_CHAR         = chDash;                 // '-'
67
68 static const XMLCh UTC_SET[]            = {UTC_STD_CHAR           //"Z+-"
69                                          , UTC_POS_CHAR
70                                          , UTC_NEG_CHAR
71                                          , chNull};
72
73 static const int YMD_MIN_SIZE    = 10;   // CCYY-MM-DD
74 static const int YMONTH_MIN_SIZE = 7;    // CCYY_MM
75 static const int TIME_MIN_SIZE   = 8;    // hh:mm:ss
76 static const int TIMEZONE_SIZE   = 5;    // hh:mm
77 static const int DAY_SIZE        = 5;    // ---DD
78 //static const int MONTH_SIZE      = 6;    // --MM--
79 static const int MONTHDAY_SIZE   = 7;    // --MM-DD
80 static const int NOT_FOUND       = -1;
81
82 //define constants to be used in assigning default values for
83 //all date/time excluding duration
84 static const int YEAR_DEFAULT  = 2000;
85 static const int MONTH_DEFAULT = 01;
86 static const int DAY_DEFAULT   = 15;
87
88 // order-relation on duration is a partial order. The dates below are used to
89 // for comparison of 2 durations, based on the fact that
90 // duration x and y is x<=y iff s+x<=s+y
91 // see 3.2.6 duration W3C schema datatype specs
92 //
93 // the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}
94 const int DateTime::DATETIMES[][TOTAL_SIZE] =
95 {
96     {1696, 9, 1, 0, 0, 0, 0, UTC_STD},
97         {1697, 2, 1, 0, 0, 0, 0, UTC_STD},
98         {1903, 3, 1, 0, 0, 0, 0, UTC_STD},
99         {1903, 7, 1, 0, 0, 0, 0, UTC_STD}
100 };
101
102 // ---------------------------------------------------------------------------
103 //  local methods
104 // ---------------------------------------------------------------------------
105 static inline int fQuotient(int a, int b)
106 {
107     div_t div_result = div(a, b);
108     return div_result.quot;
109 }
110
111 static inline int fQuotient(int temp, int low, int high)
112 {
113     return fQuotient(temp - low, high - low);
114 }
115
116 static inline int mod(int a, int b, int quotient)
117 {
118         return (a - quotient*b) ;
119 }
120
121 static inline int modulo (int temp, int low, int high)
122 {
123     //modulo(a - low, high - low) + low
124     int a = temp - low;
125     int b = high - low;
126     return (mod (a, b, fQuotient(a, b)) + low) ;
127 }
128
129 static inline bool isLeapYear(int year)
130 {
131     return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));
132 }
133
134 static int maxDayInMonthFor(int year, int month)
135 {
136
137     if ( month == 4 || month == 6 || month == 9 || month == 11 )
138     {
139         return 30;
140     }
141     else if ( month==2 )
142     {
143         if ( isLeapYear(year) )
144             return 29;
145         else
146             return 28;
147     }
148     else
149     {
150         return 31;
151     }
152
153 }
154
155 // ---------------------------------------------------------------------------
156 //  static methods : for duration
157 // ---------------------------------------------------------------------------
158 /**
159  * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
160  *
161  * 3.2.6.2 Order relation on duration
162  *
163  *     In general, the order-relation on duration is a partial order since there is no
164  *  determinate relationship between certain durations such as one month (P1M) and 30 days (P30D).
165  *  The order-relation of two duration values x and y is x < y iff s+x < s+y for each qualified
166  *  dateTime s in the list below.
167  *
168  *     These values for s cause the greatest deviations in the addition of dateTimes and durations
169  *
170  **/
171 int DateTime::compare(const DateTime* const pDate1
172                        , const DateTime* const pDate2
173                        , bool  strict)
174 {
175     //REVISIT: this is unoptimazed vs of comparing 2 durations
176     //         Algorithm is described in 3.2.6.2 W3C Schema Datatype specs
177     //
178
179     int resultA, resultB = XMLDateTime::INDETERMINATE;
180
181     //try and see if the objects are equal
182     if ( (resultA = compareOrder(pDate1, pDate2)) == XMLDateTime::EQUAL)
183         return XMLDateTime::EQUAL;
184
185     //long comparison algorithm is required
186     DateTime tempA, *pTempA = &tempA;
187     DateTime tempB, *pTempB = &tempB;
188
189     addDuration(pTempA, pDate1, 0);
190     addDuration(pTempB, pDate2, 0);
191     resultA = compareOrder(pTempA, pTempB);
192     if ( resultA == XMLDateTime::INDETERMINATE )
193         return XMLDateTime::INDETERMINATE;
194
195     addDuration(pTempA, pDate1, 1);
196     addDuration(pTempB, pDate2, 1);
197     resultB = compareOrder(pTempA, pTempB);
198     resultA = compareResult(resultA, resultB, strict);
199     if ( resultA == XMLDateTime::INDETERMINATE )
200         return XMLDateTime::INDETERMINATE;
201
202     addDuration(pTempA, pDate1, 2);
203     addDuration(pTempB, pDate2, 2);
204     resultB = compareOrder(pTempA, pTempB);
205     resultA = compareResult(resultA, resultB, strict);
206     if ( resultA == XMLDateTime::INDETERMINATE )
207         return XMLDateTime::INDETERMINATE;
208
209     addDuration(pTempA, pDate1, 3);
210     addDuration(pTempB, pDate2, 3);
211     resultB = compareOrder(pTempA, pTempB);
212     resultA = compareResult(resultA, resultB, strict);
213
214     return resultA;
215
216 }
217
218 //
219 // Form a new DateTime with duration and baseDate array
220 // Note: C++        Java
221 //       fNewDate   duration
222 //       fDuration  date
223 //
224
225 void DateTime::addDuration(DateTime*             fNewDate
226                             , const DateTime* const fDuration
227                             , int index)
228
229 {
230
231     //REVISIT: some code could be shared between normalize() and this method,
232     //         however is it worth moving it? The structures are different...
233     //
234
235     fNewDate->reset();
236     //add months (may be modified additionaly below)
237     int temp = DATETIMES[index][Month] + fDuration->fValue[Month];
238     fNewDate->fValue[Month] = modulo(temp, 1, 13);
239     int carry = fQuotient(temp, 1, 13);
240
241     //add years (may be modified additionaly below)
242     fNewDate->fValue[CentYear] =
243         DATETIMES[index][CentYear] + fDuration->fValue[CentYear] + carry;
244
245     //add seconds
246     temp = DATETIMES[index][Second] + fDuration->fValue[Second];
247     carry = fQuotient (temp, 60);
248     fNewDate->fValue[Second] =  mod(temp, 60, carry);
249
250     //add minutes
251     temp = DATETIMES[index][Minute] + fDuration->fValue[Minute] + carry;
252     carry = fQuotient(temp, 60);
253     fNewDate->fValue[Minute] = mod(temp, 60, carry);
254
255     //add hours
256     temp = DATETIMES[index][Hour] + fDuration->fValue[Hour] + carry;
257     carry = fQuotient(temp, 24);
258     fNewDate->fValue[Hour] = mod(temp, 24, carry);
259
260     fNewDate->fValue[Day] =
261         DATETIMES[index][Day] + fDuration->fValue[Day] + carry;
262
263     while ( true )
264     {
265         temp = maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]);
266         if ( fNewDate->fValue[Day] < 1 )
267         { //original fNewDate was negative
268             fNewDate->fValue[Day] +=
269                 maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]-1);
270             carry = -1;
271         }
272         else if ( fNewDate->fValue[Day] > temp )
273         {
274             fNewDate->fValue[Day] -= temp;
275             carry = 1;
276         }
277         else
278         {
279             break;
280         }
281
282         temp = fNewDate->fValue[Month] + carry;
283         fNewDate->fValue[Month] = modulo(temp, 1, 13);
284         fNewDate->fValue[CentYear] += fQuotient(temp, 1, 13);
285     }
286
287     //fNewDate->fValue[utc] = UTC_STD_CHAR;
288     fNewDate->fValue[utc] = UTC_STD;
289 }
290
291 int DateTime::compareResult(int resultA
292                              , int resultB
293                              , bool strict)
294 {
295
296     if ( resultB == XMLDateTime::INDETERMINATE )
297     {
298         return XMLDateTime::INDETERMINATE;
299     }
300     else if ( (resultA != resultB) &&
301               strict                )
302     {
303         return XMLDateTime::INDETERMINATE;
304     }
305     else if ( (resultA != resultB) &&
306               !strict               )
307     {
308         if ( (resultA != XMLDateTime::EQUAL) &&
309              (resultB != XMLDateTime::EQUAL)  )
310         {
311             return XMLDateTime::INDETERMINATE;
312         }
313         else
314         {
315             return (resultA != XMLDateTime::EQUAL)? resultA : resultB;
316         }
317     }
318
319     return resultA;
320
321 }
322
323 // ---------------------------------------------------------------------------
324 //  static methods : for others
325 // ---------------------------------------------------------------------------
326 int DateTime::compare(const DateTime* const pDate1
327                        , const DateTime* const pDate2)
328 {
329
330     if (pDate1->fValue[utc] == pDate2->fValue[utc])
331     {
332         return DateTime::compareOrder(pDate1, pDate2);
333     }
334
335     int c1, c2;
336
337     if ( pDate1->isNormalized())
338     {
339         c1 = compareResult(pDate1, pDate2, false, UTC_POS);
340         c2 = compareResult(pDate1, pDate2, false, UTC_NEG);
341         return getRetVal(c1, c2);
342     }
343     else if ( pDate2->isNormalized())
344     {
345         c1 = compareResult(pDate1, pDate2, true, UTC_POS);
346         c2 = compareResult(pDate1, pDate2, true, UTC_NEG);
347         return getRetVal(c1, c2);
348     }
349
350     return XMLDateTime::INDETERMINATE;
351 }
352
353 int DateTime::compareResult(const DateTime* const pDate1
354                              , const DateTime* const pDate2
355                              , bool  set2Left
356                              , int   utc_type)
357 {
358     DateTime tmpDate = (set2Left ? *pDate1 : *pDate2);
359
360     tmpDate.fTimeZone[hh] = 14;
361     tmpDate.fTimeZone[mm] = 0;
362     tmpDate.fValue[utc] = utc_type;
363     tmpDate.normalize();
364
365     return (set2Left? DateTime::compareOrder(&tmpDate, pDate2) :
366                       DateTime::compareOrder(pDate1, &tmpDate));
367 }
368
369 int DateTime::compareOrder(const DateTime* const lValue
370                             , const DateTime* const rValue)
371                             //, MemoryManager* const memMgr)
372 {
373     //
374     // If any of the them is not normalized() yet,
375     // we need to do something here.
376     //
377     DateTime lTemp = *lValue;
378     DateTime rTemp = *rValue;
379
380     lTemp.normalize();
381     rTemp.normalize();
382
383     for ( int i = 0 ; i < TOTAL_SIZE; i++ )
384     {
385         if ( lTemp.fValue[i] < rTemp.fValue[i] )
386         {
387             return XMLDateTime::LESS_THAN;
388         }
389         else if ( lTemp.fValue[i] > rTemp.fValue[i] )
390         {
391             return XMLDateTime::GREATER_THAN;
392         }
393     }
394
395     if ( lTemp.fHasTime)
396     {
397         if ( lTemp.fMiliSecond < rTemp.fMiliSecond )
398         {
399             return XMLDateTime::LESS_THAN;
400         }
401         else if ( lTemp.fMiliSecond > rTemp.fMiliSecond )
402         {
403             return XMLDateTime::GREATER_THAN;
404         }
405     }
406
407     return XMLDateTime::EQUAL;
408 }
409
410 // ---------------------------------------------------------------------------
411 //  ctor and dtor
412 // ---------------------------------------------------------------------------
413 DateTime::~DateTime()
414 {
415     delete[] fBuffer;
416 }
417
418 DateTime::DateTime()
419 : fStart(0)
420 , fEnd(0)
421 , fBufferMaxLen(0)
422 , fBuffer(0)
423 , fMiliSecond(0)
424 , fHasTime(false)
425 {
426     reset();
427 }
428
429 DateTime::DateTime(const XMLCh* const aString)
430 : fStart(0)
431 , fEnd(0)
432 , fBufferMaxLen(0)
433 , fBuffer(0)
434 , fMiliSecond(0)
435 , fHasTime(false)
436 {
437     setBuffer(aString);
438 }
439
440 DateTime::DateTime(time_t epoch, bool duration)
441 : fStart(0)
442 , fEnd(0)
443 , fBufferMaxLen(0)
444 , fBuffer(0)
445 , fMiliSecond(0)
446 , fHasTime(false)
447 {
448     if (duration) {
449         ostringstream s;
450         if (epoch < 0) {
451             s << "-";
452             epoch = -epoch;
453         }
454         time_t days = epoch / 86400;
455         epoch %= 86400;
456         time_t hours = epoch / 3600;
457         epoch %= 3600;
458         time_t minutes = epoch / 60;
459         epoch %= 60;
460         s << "P" << days << "DT" << hours << "H" << minutes << "M" << epoch << "S";
461         auto_ptr_XMLCh timeptr(s.str().c_str());
462         setBuffer(timeptr.get());
463     }
464     else {
465 #ifndef HAVE_GMTIME_R
466         struct tm* ptime=gmtime(&epoch);
467 #else
468         struct tm res;
469         struct tm* ptime=gmtime_r(&epoch,&res);
470 #endif
471         char timebuf[32];
472         strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
473         auto_ptr_XMLCh timeptr(timebuf);
474         setBuffer(timeptr.get());
475     }
476 }
477
478 // -----------------------------------------------------------------------
479 // Copy ctor and Assignment operators
480 // -----------------------------------------------------------------------
481
482 DateTime::DateTime(const DateTime &toCopy)
483 : fBufferMaxLen(0)
484 , fBuffer(0)
485 {
486     copy(toCopy);
487 }
488
489 DateTime& DateTime::operator=(const DateTime& rhs)
490 {
491     if (this == &rhs)
492         return *this;
493
494     copy(rhs);
495     return *this;
496 }
497
498 // -----------------------------------------------------------------------
499 // Implementation of Abstract Interface
500 // -----------------------------------------------------------------------
501
502 //
503 // We may simply return the handle to fBuffer
504 //
505 const XMLCh*  DateTime::getRawData() const
506 {
507     //assertBuffer();
508     return fBuffer;
509 }
510
511
512 const XMLCh*  DateTime::getFormattedString() const
513 {
514     return getRawData();
515 }
516
517 int DateTime::getSign() const
518 {
519     return fValue[utc];
520 }
521
522 time_t DateTime::getEpoch(bool duration) const
523 {
524     if (duration) {
525         time_t epoch = getSecond() + (60 * getMinute()) + (3600 * getHour()) + (86400 * getDay());
526         if (getMonth())
527             epoch += (((365 * 4) + 1)/48 * 86400);
528         if (getYear())
529             epoch += 365.25 * 86400;
530         return getSign()!=UTC_NEG ? epoch : -epoch;
531     }
532     else {
533         struct tm t;
534         t.tm_sec=getSecond();
535         t.tm_min=getMinute();
536         t.tm_hour=getHour();
537         t.tm_mday=getDay();
538         t.tm_mon=getMonth()-1;
539         t.tm_year=getYear()-1900;
540         t.tm_isdst=0;
541 #if defined(HAVE_TIMEGM)
542         return timegm(&t);
543 #else
544         // Windows, and hopefully most others...?
545         return mktime(&t) - timezone;
546 #endif
547     }
548 }
549
550 // ---------------------------------------------------------------------------
551 //  Parsers
552 // ---------------------------------------------------------------------------
553
554 //
555 // [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}[TimeZone]
556 //
557 void DateTime::parseDateTime()
558 {
559     initParser();
560     getDate();
561
562     //fStart is supposed to point to 'T'
563     if (fBuffer[fStart++] != DATETIME_SEPARATOR)
564         throw XMLParserException("Invalid separator between date and time.");
565
566     getTime();
567     validateDateTime();
568     normalize();
569     fHasTime = true;
570 }
571
572 //
573 // [-]{CCYY-MM-DD}[TimeZone]
574 //
575 void DateTime::parseDate()
576 {
577     initParser();
578     getDate();
579     parseTimeZone();
580     validateDateTime();
581     normalize();
582 }
583
584 void DateTime::parseTime()
585 {
586     initParser();
587
588     // time initialize to default values
589     fValue[CentYear]= YEAR_DEFAULT;
590     fValue[Month]   = MONTH_DEFAULT;
591     fValue[Day]     = DAY_DEFAULT;
592
593     getTime();
594
595     validateDateTime();
596     normalize();
597     fHasTime = true;
598 }
599
600 //
601 // {---DD}[TimeZone]
602 //  01234
603 //
604 void DateTime::parseDay()
605 {
606     initParser();
607
608     if (fBuffer[0] != DATE_SEPARATOR ||
609         fBuffer[1] != DATE_SEPARATOR ||
610         fBuffer[2] != DATE_SEPARATOR  )
611     {
612         throw XMLParserException("Invalid character in date.");
613     }
614
615     //initialize values
616     fValue[CentYear] = YEAR_DEFAULT;
617     fValue[Month]    = MONTH_DEFAULT;
618     fValue[Day]      = parseInt(fStart+3, fStart+5);
619
620     if ( DAY_SIZE < fEnd )
621     {
622         int sign = findUTCSign(DAY_SIZE);
623         if ( sign < 0 )
624         {
625             throw XMLParserException("Invalid character in date.");
626         }
627         else
628         {
629             getTimeZone(sign);
630         }
631     }
632
633     validateDateTime();
634     normalize();
635 }
636
637 //
638 // {--MM--}[TimeZone]
639 // {--MM}[TimeZone]
640 //  012345
641 //
642 void DateTime::parseMonth()
643 {
644     initParser();
645
646     if (fBuffer[0] != DATE_SEPARATOR ||
647         fBuffer[1] != DATE_SEPARATOR  )
648     {
649         throw XMLParserException("Invalid character in date.");
650     }
651
652     //set constants
653     fValue[CentYear] = YEAR_DEFAULT;
654     fValue[Day]      = DAY_DEFAULT;
655     fValue[Month]    = parseInt(2, 4);
656
657     // REVISIT: allow both --MM and --MM-- now.
658     // need to remove the following lines to disallow --MM--
659     // when the errata is officially in the rec.
660     fStart = 4;
661     if ( fEnd >= fStart+2 && fBuffer[fStart] == DATE_SEPARATOR && fBuffer[fStart+1] == DATE_SEPARATOR )
662     {
663         fStart += 2;
664     }
665
666     //
667     // parse TimeZone if any
668     //
669     if ( fStart < fEnd )
670     {
671         int sign = findUTCSign(fStart);
672         if ( sign < 0 )
673         {
674             throw XMLParserException("Invalid character in date.");
675         }
676         else
677         {
678             getTimeZone(sign);
679         }
680     }
681
682     validateDateTime();
683     normalize();
684 }
685
686 //
687 //[-]{CCYY}[TimeZone]
688 // 0  1234
689 //
690 void DateTime::parseYear()
691 {
692     initParser();
693
694     // skip the first '-' and search for timezone
695     //
696     int sign = findUTCSign((fBuffer[0] == chDash) ? 1 : 0);
697
698     if (sign == NOT_FOUND)
699     {
700         fValue[CentYear] = parseIntYear(fEnd);
701     }
702     else
703     {
704         fValue[CentYear] = parseIntYear(sign);
705         getTimeZone(sign);
706     }
707
708     //initialize values
709     fValue[Month] = MONTH_DEFAULT;
710     fValue[Day]   = DAY_DEFAULT;   //java is 1
711
712     validateDateTime();
713     normalize();
714 }
715
716 //
717 //{--MM-DD}[TimeZone]
718 // 0123456
719 //
720 void DateTime::parseMonthDay()
721 {
722     initParser();
723
724     if (fBuffer[0] != DATE_SEPARATOR ||
725         fBuffer[1] != DATE_SEPARATOR ||
726         fBuffer[4] != DATE_SEPARATOR )
727     {
728         throw XMLParserException("Invalid character in date.");
729     }
730
731
732     //initialize
733     fValue[CentYear] = YEAR_DEFAULT;
734     fValue[Month]    = parseInt(2, 4);
735     fValue[Day]      = parseInt(5, 7);
736
737     if ( MONTHDAY_SIZE < fEnd )
738     {
739         int sign = findUTCSign(MONTHDAY_SIZE);
740         if ( sign<0 )
741         {
742             throw XMLParserException("Invalid character in date.");
743         }
744         else
745         {
746             getTimeZone(sign);
747         }
748     }
749
750     validateDateTime();
751     normalize();
752 }
753
754 void DateTime::parseYearMonth()
755 {
756     initParser();
757
758     // get date
759     getYearMonth();
760     fValue[Day] = DAY_DEFAULT;
761     parseTimeZone();
762
763     validateDateTime();
764     normalize();
765 }
766
767 //
768 //PnYn MnDTnH nMnS: -P1Y2M3DT10H30M
769 //
770 // [-]{'P'{[n'Y'][n'M'][n'D']['T'][n'H'][n'M'][n'S']}}
771 //
772 //  Note: the n above shall be >= 0
773 //        if no time element found, 'T' shall be absent
774 //
775 void DateTime::parseDuration()
776 {
777     initParser();
778
779     // must start with '-' or 'P'
780     //
781     XMLCh c = fBuffer[fStart++];
782     if ( (c != DURATION_STARTER) &&
783          (c != chDash)            )
784     {
785         throw XMLParserException("Invalid character in time.");
786     }
787
788     // 'P' must ALWAYS be present in either case
789     if ( (c == chDash) &&
790          (fBuffer[fStart++]!= DURATION_STARTER ))
791     {
792         throw XMLParserException("Invalid character in time.");
793     }
794
795     // java code
796     //date[utc]=(c=='-')?'-':0;
797     //fValue[utc] = UTC_STD;
798     fValue[utc] = (fBuffer[0] == chDash? UTC_NEG : UTC_STD);
799
800     int negate = ( fBuffer[0] == chDash ? -1 : 1);
801
802     //
803     // No negative value is allowed after 'P'
804     //
805     // eg P-1234, invalid
806     //
807     if (indexOf(fStart, fEnd, chDash) != NOT_FOUND)
808     {
809         throw XMLParserException("Invalid character in time.");
810     }
811
812     //at least one number and designator must be seen after P
813     bool designator = false;
814
815     int endDate = indexOf(fStart, fEnd, DATETIME_SEPARATOR);
816     if ( endDate == NOT_FOUND )
817     {
818         endDate = fEnd;  // 'T' absent
819     }
820
821     //find 'Y'
822     int end = indexOf(fStart, endDate, DURATION_Y);
823     if ( end != NOT_FOUND )
824     {
825         //scan year
826         fValue[CentYear] = negate * parseInt(fStart, end);
827         fStart = end+1;
828         designator = true;
829     }
830
831     end = indexOf(fStart, endDate, DURATION_M);
832     if ( end != NOT_FOUND )
833     {
834         //scan month
835         fValue[Month] = negate * parseInt(fStart, end);
836         fStart = end+1;
837         designator = true;
838     }
839
840     end = indexOf(fStart, endDate, DURATION_D);
841     if ( end != NOT_FOUND )
842     {
843         //scan day
844         fValue[Day] = negate * parseInt(fStart,end);
845         fStart = end+1;
846         designator = true;
847     }
848
849     if ( (fEnd == endDate) &&   // 'T' absent
850          (fStart != fEnd)   )   // something after Day
851     {
852         throw XMLParserException("Invalid character in time.");
853     }
854
855     if ( fEnd != endDate ) // 'T' present
856     {
857         //scan hours, minutes, seconds
858         //
859
860         // skip 'T' first
861         end = indexOf(++fStart, fEnd, DURATION_H);
862         if ( end != NOT_FOUND )
863         {
864             //scan hours
865             fValue[Hour] = negate * parseInt(fStart, end);
866             fStart = end+1;
867             designator = true;
868         }
869
870         end = indexOf(fStart, fEnd, DURATION_M);
871         if ( end != NOT_FOUND )
872         {
873             //scan min
874             fValue[Minute] = negate * parseInt(fStart, end);
875             fStart = end+1;
876             designator = true;
877         }
878
879         end = indexOf(fStart, fEnd, DURATION_S);
880         if ( end != NOT_FOUND )
881         {
882             //scan seconds
883             int mlsec = indexOf (fStart, end, MILISECOND_SEPARATOR);
884
885             /***
886              * Schema Errata: E2-23
887              * at least one digit must follow the decimal point if it appears.
888              * That is, the value of the seconds component must conform
889              * to the following pattern: [0-9]+(.[0-9]+)?
890              */
891             if ( mlsec != NOT_FOUND )
892             {
893                 /***
894                  * make usure there is something after the '.' and before the end.
895                  */
896                 if ( mlsec+1 == end )
897                 {
898                     throw XMLParserException("Invalid character in time.");
899                 }
900
901                 fValue[Second]     = negate * parseInt(fStart, mlsec);
902                 fMiliSecond        = negate * parseMiliSecond(mlsec+1, end);
903             }
904             else
905             {
906                 fValue[Second] = negate * parseInt(fStart,end);
907             }
908
909             fStart = end+1;
910             designator = true;
911         }
912
913         // no additional data should appear after last item
914         // P1Y1M1DT is illigal value as well
915         if ( (fStart != fEnd) ||
916               fBuffer[--fStart] == DATETIME_SEPARATOR )
917         {
918             throw XMLParserException("Invalid character in time.");
919         }
920     }
921
922     if ( !designator )
923     {
924         throw XMLParserException("Invalid character in time.");
925     }
926
927 }
928
929 // ---------------------------------------------------------------------------
930 //  Scanners
931 // ---------------------------------------------------------------------------
932
933 //
934 // [-]{CCYY-MM-DD}
935 //
936 // Note: CCYY could be more than 4 digits
937 //       Assuming fStart point to the beginning of the Date Section
938 //       fStart updated to point to the position right AFTER the second 'D'
939 //       Since the lenght of CCYY might be variable, we can't check format upfront
940 //
941 void DateTime::getDate()
942 {
943
944     // Ensure enough chars in buffer
945     if ( (fStart+YMD_MIN_SIZE) > fEnd)
946         throw XMLParserException("Date/time string not complete.");
947
948     getYearMonth();    // Scan YearMonth and
949                        // fStart point to the next '-'
950
951     if (fBuffer[fStart++] != DATE_SEPARATOR)
952     {
953         throw XMLParserException("CCYY-MM must be followed by '-' sign.");
954     }
955
956     fValue[Day] = parseInt(fStart, fStart+2);
957     fStart += 2 ;  //fStart points right after the Day
958
959     return;
960 }
961
962 //
963 // hh:mm:ss[.msssss]['Z']
964 // hh:mm:ss[.msssss][['+'|'-']hh:mm]
965 // 012345678
966 //
967 // Note: Assuming fStart point to the beginning of the Time Section
968 //       fStart updated to point to the position right AFTER the second 's'
969 //                                                  or ms if any
970 //
971 void DateTime::getTime()
972 {
973
974     // Ensure enough chars in buffer
975     if ( (fStart+TIME_MIN_SIZE) > fEnd)
976         throw XMLParserException("Incomplete Time Format.");
977
978     // check (fixed) format first
979     if ((fBuffer[fStart + 2] != TIME_SEPARATOR) ||
980         (fBuffer[fStart + 5] != TIME_SEPARATOR)  )
981     {
982         throw XMLParserException("Error in parsing time.");
983     }
984
985     //
986     // get hours, minute and second
987     //
988     fValue[Hour]   = parseInt(fStart + 0, fStart + 2);
989     fValue[Minute] = parseInt(fStart + 3, fStart + 5);
990     fValue[Second] = parseInt(fStart + 6, fStart + 8);
991     fStart += 8;
992
993     // to see if any ms and/or utc part after that
994     if (fStart >= fEnd)
995         return;
996
997     //find UTC sign if any
998     int sign = findUTCSign(fStart);
999
1000     //parse miliseconds
1001     int milisec = (fBuffer[fStart] == MILISECOND_SEPARATOR)? fStart : NOT_FOUND;
1002     if ( milisec != NOT_FOUND )
1003     {
1004         fStart++;   // skip the '.'
1005         // make sure we have some thing between the '.' and fEnd
1006         if (fStart >= fEnd)
1007         {
1008             throw XMLParserException("ms should be present once '.' is present.");
1009         }
1010
1011         if ( sign == NOT_FOUND )
1012         {
1013             fMiliSecond = parseMiliSecond(fStart, fEnd);  //get ms between '.' and fEnd
1014             fStart = fEnd;
1015         }
1016         else
1017         {
1018             fMiliSecond = parseMiliSecond(fStart, sign);  //get ms between UTC sign and fEnd
1019         }
1020         }
1021     else if(sign == 0 || sign != fStart)
1022     {
1023         throw XMLParserException("Seconds has more than 2 digits.");
1024     }
1025
1026     //parse UTC time zone (hh:mm)
1027     if ( sign > 0 ) {
1028         getTimeZone(sign);
1029     }
1030
1031 }
1032
1033 //
1034 // [-]{CCYY-MM}
1035 //
1036 // Note: CCYY could be more than 4 digits
1037 //       fStart updated to point AFTER the second 'M' (probably meet the fEnd)
1038 //
1039 void DateTime::getYearMonth()
1040 {
1041
1042     // Ensure enough chars in buffer
1043     if ( (fStart+YMONTH_MIN_SIZE) > fEnd)
1044         throw XMLParserException("Incomplete YearMonth Format.");
1045
1046     // skip the first leading '-'
1047     int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;
1048
1049     //
1050     // search for year separator '-'
1051     //
1052     int yearSeparator = indexOf(start, fEnd, DATE_SEPARATOR);
1053     if ( yearSeparator == NOT_FOUND)
1054         throw XMLParserException("Year separator is missing or misplaced.");
1055
1056     fValue[CentYear] = parseIntYear(yearSeparator);
1057     fStart = yearSeparator + 1;  //skip the '-' and point to the first M
1058
1059     //
1060     //gonna check we have enough byte for month
1061     //
1062     if ((fStart + 2) > fEnd )
1063         throw XMLParserException("No month in buffer.");
1064
1065     fValue[Month] = parseInt(fStart, yearSeparator + 3);
1066     fStart += 2;  //fStart points right after the MONTH
1067
1068     return;
1069 }
1070
1071 void DateTime::parseTimeZone()
1072 {
1073     if ( fStart < fEnd )
1074     {
1075         int sign = findUTCSign(fStart);
1076         if ( sign < 0 )
1077         {
1078             throw XMLParserException("Error in month parsing.");
1079         }
1080         else
1081         {
1082             getTimeZone(sign);
1083         }
1084     }
1085
1086     return;
1087 }
1088
1089 //
1090 // 'Z'
1091 // ['+'|'-']hh:mm
1092 //
1093 // Note: Assuming fStart points to the beginning of TimeZone section
1094 //       fStart updated to meet fEnd
1095 //
1096 void DateTime::getTimeZone(const int sign)
1097 {
1098
1099     if ( fBuffer[sign] == UTC_STD_CHAR )
1100     {
1101         if ((sign + 1) != fEnd )
1102         {
1103             throw XMLParserException("Error in parsing time zone.");
1104         }
1105
1106         return;
1107     }
1108
1109     //
1110     // otherwise, it has to be this format
1111     // '[+|-]'hh:mm
1112     //    1   23456 7
1113     //   sign      fEnd
1114     //
1115     if ( ( ( sign + TIMEZONE_SIZE + 1) != fEnd )      ||
1116          ( fBuffer[sign + 3] != TIMEZONE_SEPARATOR ) )
1117     {
1118         throw XMLParserException("Error in parsing time zone.");
1119     }
1120
1121     fTimeZone[hh] = parseInt(sign+1, sign+3);
1122     fTimeZone[mm] = parseInt(sign+4, fEnd);
1123
1124     return;
1125 }
1126
1127 // ---------------------------------------------------------------------------
1128 //  Validator and normalizer
1129 // ---------------------------------------------------------------------------
1130
1131 /**
1132  * If timezone present - normalize dateTime  [E Adding durations to dateTimes]
1133  *
1134  * @param date   CCYY-MM-DDThh:mm:ss+03
1135  * @return CCYY-MM-DDThh:mm:ssZ
1136  */
1137 void DateTime::normalize()
1138 {
1139
1140     if ((fValue[utc] == UTC_UNKNOWN) ||
1141         (fValue[utc] == UTC_STD)      )
1142         return;
1143
1144     int negate = (fValue[utc] == UTC_POS)? -1: 1;
1145
1146     // add mins
1147     int temp = fValue[Minute] + negate * fTimeZone[mm];
1148     int carry = fQuotient(temp, 60);
1149     fValue[Minute] = mod(temp, 60, carry);
1150
1151     //add hours
1152     temp = fValue[Hour] + negate * fTimeZone[hh] + carry;
1153     carry = fQuotient(temp, 24);
1154     fValue[Hour] = mod(temp, 24, carry);
1155
1156     fValue[Day] += carry;
1157
1158     while (1)
1159     {
1160         temp = maxDayInMonthFor(fValue[CentYear], fValue[Month]);
1161         if (fValue[Day] < 1)
1162         {
1163             fValue[Day] += maxDayInMonthFor(fValue[CentYear], fValue[Month] - 1);
1164             carry = -1;
1165         }
1166         else if ( fValue[Day] > temp )
1167         {
1168             fValue[Day] -= temp;
1169             carry = 1;
1170         }
1171         else
1172         {
1173             break;
1174         }
1175
1176         temp = fValue[Month] + carry;
1177         fValue[Month] = modulo(temp, 1, 13);
1178         fValue[CentYear] += fQuotient(temp, 1, 13);
1179     }
1180
1181     // set to normalized
1182     fValue[utc] = UTC_STD;
1183
1184     return;
1185 }
1186
1187 void DateTime::validateDateTime() const
1188 {
1189
1190     //REVISIT: should we throw an exception for not valid dates
1191     //          or reporting an error message should be sufficient?
1192     if ( fValue[CentYear] == 0 )
1193     {
1194         throw XMLParserException("The year \"0000\" is an illegal year value");
1195     }
1196
1197     if ( fValue[Month] < 1  ||
1198          fValue[Month] > 12  )
1199     {
1200         throw XMLParserException("The month must have values 1 to 12");
1201     }
1202
1203     //validate days
1204     if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) ||
1205          fValue[Day] == 0 )
1206     {
1207         throw XMLParserException("The day must have values 1 to 31");
1208     }
1209
1210     //validate hours
1211     if ((fValue[Hour] < 0)  ||
1212         (fValue[Hour] > 24) ||
1213         ((fValue[Hour] == 24) && ((fValue[Minute] !=0) ||
1214                                   (fValue[Second] !=0) ||
1215                                   (fMiliSecond    !=0))))
1216     {
1217         throw XMLParserException("Hour must have values 0-23");
1218     }
1219
1220     //validate minutes
1221     if ( fValue[Minute] < 0 ||
1222          fValue[Minute] > 59 )
1223     {
1224         throw XMLParserException("Minute must have values 0-59");
1225     }
1226
1227     //validate seconds
1228     if ( fValue[Second] < 0 ||
1229          fValue[Second] > 60 )
1230     {
1231         throw XMLParserException("Second must have values 0-60");
1232     }
1233
1234     //validate time-zone hours
1235     if ( (abs(fTimeZone[hh]) > 14) ||
1236          ((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) )
1237     {
1238         throw XMLParserException("Time zone should have range -14..+14");
1239     }
1240
1241     //validate time-zone minutes
1242     if ( abs(fTimeZone[mm]) > 59 )
1243     {
1244         throw XMLParserException("Minute must have values 0-59");
1245     }
1246
1247     return;
1248 }
1249
1250 // -----------------------------------------------------------------------
1251 // locator and converter
1252 // -----------------------------------------------------------------------
1253 int DateTime::indexOf(const int start, const int end, const XMLCh ch) const
1254 {
1255     for ( int i = start; i < end; i++ )
1256         if ( fBuffer[i] == ch )
1257             return i;
1258
1259     return NOT_FOUND;
1260 }
1261
1262 int DateTime::findUTCSign (const int start)
1263 {
1264     int  pos;
1265     for ( int index = start; index < fEnd; index++ )
1266     {
1267         pos = XMLString::indexOf(UTC_SET, fBuffer[index]);
1268         if ( pos != NOT_FOUND)
1269         {
1270             fValue[utc] = pos+1;   // refer to utcType, there is 1 diff
1271             return index;
1272         }
1273     }
1274
1275     return NOT_FOUND;
1276 }
1277
1278 //
1279 // Note:
1280 //    start: starting point in fBuffer
1281 //    end:   ending point in fBuffer (exclusive)
1282 //    fStart NOT updated
1283 //
1284 int DateTime::parseInt(const int start, const int end) const
1285 {
1286     unsigned int retVal = 0;
1287     for (int i=start; i < end; i++) {
1288
1289         if (fBuffer[i] < chDigit_0 || fBuffer[i] > chDigit_9)
1290             throw XMLParserException("Invalid non-numeric characters.");
1291
1292         retVal = (retVal * 10) + (unsigned int) (fBuffer[i] - chDigit_0);
1293     }
1294
1295     return (int) retVal;
1296 }
1297
1298 //
1299 // Note:
1300 //    start: pointing to the first digit after the '.'
1301 //    end:   pointing to one position after the last digit
1302 //    fStart NOT updated
1303 //
1304 double DateTime::parseMiliSecond(const int start, const int end) const
1305 {
1306
1307     unsigned int  miliSecLen = (end-1) - (start-1) + 1; //to include the '.'
1308     XMLCh* miliSecData = new XMLCh[miliSecLen + 1];
1309     ArrayJanitor<XMLCh> janMili(miliSecData);
1310     XMLString::copyNString(miliSecData, &(fBuffer[start-1]), miliSecLen);
1311     *(miliSecData + miliSecLen) = chNull;
1312
1313     char *nptr = XMLString::transcode(miliSecData);
1314     ArrayJanitor<char> jan(nptr);
1315     size_t   strLen = strlen(nptr);
1316     char *endptr = 0;
1317     errno = 0;
1318
1319     //printf("milisec=<%s>\n", nptr);
1320
1321     double retVal = strtod(nptr, &endptr);
1322
1323     // check if all chars are valid char
1324     if ( (endptr - nptr) != strLen)
1325         throw XMLParserException("Invalid non-numeric characters.");
1326
1327     // we don't check underflow occurs since
1328     // nothing we can do about it.
1329     return retVal;
1330 }
1331
1332 //
1333 // [-]CCYY
1334 //
1335 // Note: start from fStart
1336 //       end (exclusive)
1337 //       fStart NOT updated
1338 //
1339 int DateTime::parseIntYear(const int end) const
1340 {
1341     // skip the first leading '-'
1342     int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;
1343
1344     int length = end - start;
1345     if (length < 4)
1346     {
1347         throw XMLParserException("Year must have 'CCYY' format");
1348     }
1349     else if (length > 4 &&
1350              fBuffer[start] == chDigit_0)
1351     {
1352         throw XMLParserException("Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden.");
1353     }
1354
1355     bool negative = (fBuffer[0] == chDash);
1356     int  yearVal = parseInt((negative ? 1 : 0), end);
1357     return ( negative ? (-1) * yearVal : yearVal );
1358 }
1359
1360 /***
1361  * E2-41
1362  *
1363  *  3.2.7.2 Canonical representation
1364  *
1365  *  Except for trailing fractional zero digits in the seconds representation,
1366  *  '24:00:00' time representations, and timezone (for timezoned values),
1367  *  the mapping from literals to values is one-to-one. Where there is more
1368  *  than one possible representation, the canonical representation is as follows:
1369  *  redundant trailing zero digits in fractional-second literals are prohibited.
1370  *  An hour representation of '24' is prohibited. Timezoned values are canonically
1371  *  represented by appending 'Z' to the nontimezoned representation. (All
1372  *  timezoned dateTime values are UTC.)
1373  *
1374  *  .'24:00:00' -> '00:00:00'
1375  *  .milisecond: trailing zeros removed
1376  *  .'Z'
1377  *
1378  ***/
1379 XMLCh* DateTime::getDateTimeCanonicalRepresentation() const
1380 {
1381     XMLCh *miliStartPtr, *miliEndPtr;
1382     searchMiliSeconds(miliStartPtr, miliEndPtr);
1383     size_t miliSecondsLen = miliEndPtr - miliStartPtr;
1384
1385     XMLCh* retBuf = new XMLCh[21 + miliSecondsLen + 2];
1386     XMLCh* retPtr = retBuf;
1387
1388     // (-?) cc+yy-mm-dd'T'hh:mm:ss'Z'    ('.'s+)?
1389     //      2+  8       1      8   1
1390     //
1391     int additionalLen = fillYearString(retPtr, CentYear);
1392     if(additionalLen != 0)
1393     {
1394         // very bad luck; have to resize the buffer...
1395         XMLCh *tmpBuf = new XMLCh[additionalLen+21+miliSecondsLen +2];
1396         XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen);
1397         retPtr = tmpBuf+(retPtr-retBuf);
1398         delete[] retBuf;
1399         retBuf = tmpBuf;
1400     }
1401     *retPtr++ = DATE_SEPARATOR;
1402     fillString(retPtr, Month, 2);
1403     *retPtr++ = DATE_SEPARATOR;
1404     fillString(retPtr, Day, 2);
1405     *retPtr++ = DATETIME_SEPARATOR;
1406
1407     fillString(retPtr, Hour, 2);
1408     if (fValue[Hour] == 24)
1409     {
1410         *(retPtr - 2) = chDigit_0;
1411         *(retPtr - 1) = chDigit_0;
1412     }
1413     *retPtr++ = TIME_SEPARATOR;
1414     fillString(retPtr, Minute, 2);
1415     *retPtr++ = TIME_SEPARATOR;
1416     fillString(retPtr, Second, 2);
1417
1418     if (miliSecondsLen)
1419     {
1420         *retPtr++ = chPeriod;
1421         XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);
1422         retPtr += miliSecondsLen;
1423     }
1424
1425     *retPtr++ = UTC_STD_CHAR;
1426     *retPtr = chNull;
1427
1428     return retBuf;
1429 }
1430
1431 /***
1432  * 3.2.8 time
1433  *
1434  *  . either the time zone must be omitted or,
1435  *    if present, the time zone must be Coordinated Universal Time (UTC) indicated by a "Z".
1436  *
1437  *  . Additionally, the canonical representation for midnight is 00:00:00.
1438  *
1439 ***/
1440 XMLCh* DateTime::getTimeCanonicalRepresentation() const
1441 {
1442     XMLCh *miliStartPtr, *miliEndPtr;
1443     searchMiliSeconds(miliStartPtr, miliEndPtr);
1444     size_t miliSecondsLen = miliEndPtr - miliStartPtr;
1445
1446     XMLCh* retBuf = new XMLCh[10 + miliSecondsLen + 2];
1447     XMLCh* retPtr = retBuf;
1448
1449     // 'hh:mm:ss'Z'    ('.'s+)?
1450     //      8    1
1451     //
1452
1453     fillString(retPtr, Hour, 2);
1454     if (fValue[Hour] == 24)
1455     {
1456         *(retPtr - 2) = chDigit_0;
1457         *(retPtr - 1) = chDigit_0;
1458     }
1459     *retPtr++ = TIME_SEPARATOR;
1460     fillString(retPtr, Minute, 2);
1461     *retPtr++ = TIME_SEPARATOR;
1462     fillString(retPtr, Second, 2);
1463
1464     if (miliSecondsLen)
1465     {
1466         *retPtr++ = chPeriod;
1467         XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);
1468         retPtr += miliSecondsLen;
1469     }
1470
1471     *retPtr++ = UTC_STD_CHAR;
1472     *retPtr = chNull;
1473
1474     return retBuf;
1475 }
1476
1477 void DateTime::fillString(XMLCh*& ptr, valueIndex ind, int expLen) const
1478 {
1479     XMLCh strBuffer[16];
1480     assert(expLen < 16);
1481     XMLString::binToText(fValue[ind], strBuffer, expLen, 10);
1482     int   actualLen = XMLString::stringLen(strBuffer);
1483     int   i;
1484     //append leading zeros
1485     for (i = 0; i < expLen - actualLen; i++)
1486     {
1487         *ptr++ = chDigit_0;
1488     }
1489
1490     for (i = 0; i < actualLen; i++)
1491     {
1492         *ptr++ = strBuffer[i];
1493     }
1494
1495 }
1496
1497 int DateTime::fillYearString(XMLCh*& ptr, valueIndex ind) const
1498 {
1499     XMLCh strBuffer[16];
1500     // let's hope we get no years of 15 digits...
1501     XMLString::binToText(fValue[ind], strBuffer, 15, 10);
1502     int   actualLen = XMLString::stringLen(strBuffer);
1503     // don't forget that years can be negative...
1504     int negativeYear = 0;
1505     if(strBuffer[0] == chDash)
1506     {
1507         *ptr++ = strBuffer[0];
1508         negativeYear = 1;
1509     }
1510     int   i;
1511     //append leading zeros
1512     for (i = 0; i < 4 - actualLen+negativeYear; i++)
1513     {
1514         *ptr++ = chDigit_0;
1515     }
1516
1517     for (i = negativeYear; i < actualLen; i++)
1518     {
1519         *ptr++ = strBuffer[i];
1520     }
1521     if(actualLen > 4)
1522         return actualLen-4;
1523     return 0;
1524 }
1525
1526 /***
1527  *
1528  *   .check if the rawData has the mili second component
1529  *   .capture the substring
1530  *
1531  ***/
1532 void DateTime::searchMiliSeconds(XMLCh*& miliStartPtr, XMLCh*& miliEndPtr) const
1533 {
1534     miliStartPtr = miliEndPtr = 0;
1535
1536     int milisec = XMLString::indexOf(fBuffer, MILISECOND_SEPARATOR);
1537     if (milisec == -1)
1538         return;
1539
1540     miliStartPtr = fBuffer + milisec + 1;
1541     miliEndPtr   = miliStartPtr;
1542     while (*miliEndPtr)
1543     {
1544         if ((*miliEndPtr < chDigit_0) || (*miliEndPtr > chDigit_9))
1545             break;
1546
1547         miliEndPtr++;
1548     }
1549
1550     //remove trailing zeros
1551     while( *(miliEndPtr - 1) == chDigit_0)
1552         miliEndPtr--;
1553
1554     return;
1555 }
1556