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