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