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