source: icXML/icXML-devel/src/xercesc/util/XMLDateTime.cpp @ 2722

Last change on this file since 2722 was 2722, checked in by cameron, 6 years ago

Original Xerces files with import mods for icxercesc

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