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