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