gcc const fix, converted linefeeds
[shibboleth/cpp-xmltooling.git] / xmltooling / util / DateTime.cpp
index 7877c50..6e27cb2 100644 (file)
-/*\r
- *  Copyright 2001-2006 Internet2\r
- * \r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * DateTime.cpp\r
- * \r
- * Manipulation of XML date/time data. \r
- */\r
-\r
-/*\r
- * This is mostly copied from Xerces-C, but they don't seem inclined to produce a usable\r
- * class, so I had to incorporate my own version of it for now. I can't inherit it\r
- * since the fields I need are private.\r
- */\r
-\r
-#include "internal.h"\r
-#include "util/DateTime.h"\r
-\r
-#ifndef WIN32\r
-# include <errno.h>\r
-#endif\r
-\r
-#include <ctime>\r
-#include <xercesc/util/Janitor.hpp>\r
-#include <xercesc/util/NumberFormatException.hpp>\r
-\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-//\r
-// constants used to process raw data (fBuffer)\r
-//\r
-// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}['Z']\r
-//                                [{+|-}hh:mm']\r
-//\r
-\r
-static const XMLCh DURATION_STARTER     = chLatin_P;              // 'P'\r
-static const XMLCh DURATION_Y           = chLatin_Y;              // 'Y'\r
-static const XMLCh DURATION_M           = chLatin_M;              // 'M'\r
-static const XMLCh DURATION_D           = chLatin_D;              // 'D'\r
-static const XMLCh DURATION_H           = chLatin_H;              // 'H'\r
-static const XMLCh DURATION_S           = chLatin_S;              // 'S'\r
-\r
-static const XMLCh DATE_SEPARATOR       = chDash;                 // '-'\r
-static const XMLCh TIME_SEPARATOR       = chColon;                // ':'\r
-static const XMLCh TIMEZONE_SEPARATOR   = chColon;                // ':'\r
-static const XMLCh DATETIME_SEPARATOR   = chLatin_T;              // 'T'\r
-static const XMLCh MILISECOND_SEPARATOR = chPeriod;               // '.'\r
-\r
-static const XMLCh UTC_STD_CHAR         = chLatin_Z;              // 'Z'\r
-static const XMLCh UTC_POS_CHAR         = chPlus;                 // '+'\r
-static const XMLCh UTC_NEG_CHAR         = chDash;                 // '-'\r
-\r
-static const XMLCh UTC_SET[]            = {UTC_STD_CHAR           //"Z+-"\r
-                                         , UTC_POS_CHAR\r
-                                         , UTC_NEG_CHAR\r
-                                         , chNull};\r
-\r
-static const int YMD_MIN_SIZE    = 10;   // CCYY-MM-DD\r
-static const int YMONTH_MIN_SIZE = 7;    // CCYY_MM\r
-static const int TIME_MIN_SIZE   = 8;    // hh:mm:ss\r
-static const int TIMEZONE_SIZE   = 5;    // hh:mm\r
-static const int DAY_SIZE        = 5;    // ---DD\r
-//static const int MONTH_SIZE      = 6;    // --MM--\r
-static const int MONTHDAY_SIZE   = 7;    // --MM-DD\r
-static const int NOT_FOUND       = -1;\r
-\r
-//define constants to be used in assigning default values for\r
-//all date/time excluding duration\r
-static const int YEAR_DEFAULT  = 2000;\r
-static const int MONTH_DEFAULT = 01;\r
-static const int DAY_DEFAULT   = 15;\r
-\r
-// order-relation on duration is a partial order. The dates below are used to\r
-// for comparison of 2 durations, based on the fact that\r
-// duration x and y is x<=y iff s+x<=s+y\r
-// see 3.2.6 duration W3C schema datatype specs\r
-//\r
-// the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}\r
-const int DateTime::DATETIMES[][TOTAL_SIZE] =\r
-{\r
-    {1696, 9, 1, 0, 0, 0, 0, UTC_STD},\r
-       {1697, 2, 1, 0, 0, 0, 0, UTC_STD},\r
-       {1903, 3, 1, 0, 0, 0, 0, UTC_STD},\r
-       {1903, 7, 1, 0, 0, 0, 0, UTC_STD}\r
-};\r
-\r
-// ---------------------------------------------------------------------------\r
-//  local methods\r
-// ---------------------------------------------------------------------------\r
-static inline int fQuotient(int a, int b)\r
-{\r
-    div_t div_result = div(a, b);\r
-    return div_result.quot;\r
-}\r
-\r
-static inline int fQuotient(int temp, int low, int high)\r
-{\r
-    return fQuotient(temp - low, high - low);\r
-}\r
-\r
-static inline int mod(int a, int b, int quotient)\r
-{\r
-       return (a - quotient*b) ;\r
-}\r
-\r
-static inline int modulo (int temp, int low, int high)\r
-{\r
-    //modulo(a - low, high - low) + low\r
-    int a = temp - low;\r
-    int b = high - low;\r
-    return (mod (a, b, fQuotient(a, b)) + low) ;\r
-}\r
-\r
-static inline bool isLeapYear(int year)\r
-{\r
-    return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));\r
-}\r
-\r
-static int maxDayInMonthFor(int year, int month)\r
-{\r
-\r
-    if ( month == 4 || month == 6 || month == 9 || month == 11 )\r
-    {\r
-        return 30;\r
-    }\r
-    else if ( month==2 )\r
-    {\r
-        if ( isLeapYear(year) )\r
-            return 29;\r
-        else\r
-            return 28;\r
-    }\r
-    else\r
-    {\r
-        return 31;\r
-    }\r
-\r
-}\r
-\r
-// ---------------------------------------------------------------------------\r
-//  static methods : for duration\r
-// ---------------------------------------------------------------------------\r
-/**\r
- * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")\r
- *\r
- * 3.2.6.2 Order relation on duration\r
- *\r
- *     In general, the order-relation on duration is a partial order since there is no\r
- *  determinate relationship between certain durations such as one month (P1M) and 30 days (P30D).\r
- *  The order-relation of two duration values x and y is x < y iff s+x < s+y for each qualified\r
- *  dateTime s in the list below.\r
- *\r
- *     These values for s cause the greatest deviations in the addition of dateTimes and durations\r
- *\r
- **/\r
-int DateTime::compare(const DateTime* const pDate1\r
-                       , const DateTime* const pDate2\r
-                       , bool  strict)\r
-{\r
-    //REVISIT: this is unoptimazed vs of comparing 2 durations\r
-    //         Algorithm is described in 3.2.6.2 W3C Schema Datatype specs\r
-    //\r
-\r
-    int resultA, resultB = XMLDateTime::INDETERMINATE;\r
-\r
-    //try and see if the objects are equal\r
-    if ( (resultA = compareOrder(pDate1, pDate2)) == XMLDateTime::EQUAL)\r
-        return XMLDateTime::EQUAL;\r
-\r
-    //long comparison algorithm is required\r
-    DateTime tempA, *pTempA = &tempA;\r
-    DateTime tempB, *pTempB = &tempB;\r
-\r
-    addDuration(pTempA, pDate1, 0);\r
-    addDuration(pTempB, pDate2, 0);\r
-    resultA = compareOrder(pTempA, pTempB);\r
-    if ( resultA == XMLDateTime::INDETERMINATE )\r
-        return XMLDateTime::INDETERMINATE;\r
-\r
-    addDuration(pTempA, pDate1, 1);\r
-    addDuration(pTempB, pDate2, 1);\r
-    resultB = compareOrder(pTempA, pTempB);\r
-    resultA = compareResult(resultA, resultB, strict);\r
-    if ( resultA == XMLDateTime::INDETERMINATE )\r
-        return XMLDateTime::INDETERMINATE;\r
-\r
-    addDuration(pTempA, pDate1, 2);\r
-    addDuration(pTempB, pDate2, 2);\r
-    resultB = compareOrder(pTempA, pTempB);\r
-    resultA = compareResult(resultA, resultB, strict);\r
-    if ( resultA == XMLDateTime::INDETERMINATE )\r
-        return XMLDateTime::INDETERMINATE;\r
-\r
-    addDuration(pTempA, pDate1, 3);\r
-    addDuration(pTempB, pDate2, 3);\r
-    resultB = compareOrder(pTempA, pTempB);\r
-    resultA = compareResult(resultA, resultB, strict);\r
-\r
-    return resultA;\r
-\r
-}\r
-\r
-//\r
-// Form a new DateTime with duration and baseDate array\r
-// Note: C++        Java\r
-//       fNewDate   duration\r
-//       fDuration  date\r
-//\r
-\r
-void DateTime::addDuration(DateTime*             fNewDate\r
-                            , const DateTime* const fDuration\r
-                            , int index)\r
-\r
-{\r
-\r
-    //REVISIT: some code could be shared between normalize() and this method,\r
-    //         however is it worth moving it? The structures are different...\r
-    //\r
-\r
-    fNewDate->reset();\r
-    //add months (may be modified additionaly below)\r
-    int temp = DATETIMES[index][Month] + fDuration->fValue[Month];\r
-    fNewDate->fValue[Month] = modulo(temp, 1, 13);\r
-    int carry = fQuotient(temp, 1, 13);\r
-\r
-    //add years (may be modified additionaly below)\r
-    fNewDate->fValue[CentYear] =\r
-        DATETIMES[index][CentYear] + fDuration->fValue[CentYear] + carry;\r
-\r
-    //add seconds\r
-    temp = DATETIMES[index][Second] + fDuration->fValue[Second];\r
-    carry = fQuotient (temp, 60);\r
-    fNewDate->fValue[Second] =  mod(temp, 60, carry);\r
-               \r
-    //add minutes\r
-    temp = DATETIMES[index][Minute] + fDuration->fValue[Minute] + carry;\r
-    carry = fQuotient(temp, 60);\r
-    fNewDate->fValue[Minute] = mod(temp, 60, carry);\r
-\r
-    //add hours\r
-    temp = DATETIMES[index][Hour] + fDuration->fValue[Hour] + carry;\r
-    carry = fQuotient(temp, 24);\r
-    fNewDate->fValue[Hour] = mod(temp, 24, carry);\r
-               \r
-    fNewDate->fValue[Day] =\r
-        DATETIMES[index][Day] + fDuration->fValue[Day] + carry;\r
-\r
-    while ( true )\r
-    {\r
-        temp = maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]);\r
-        if ( fNewDate->fValue[Day] < 1 )\r
-        { //original fNewDate was negative\r
-            fNewDate->fValue[Day] +=\r
-                maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]-1);\r
-            carry = -1;\r
-        }\r
-        else if ( fNewDate->fValue[Day] > temp )\r
-        {\r
-            fNewDate->fValue[Day] -= temp;\r
-            carry = 1;\r
-        }\r
-        else\r
-        {\r
-            break;\r
-        }\r
-\r
-        temp = fNewDate->fValue[Month] + carry;\r
-        fNewDate->fValue[Month] = modulo(temp, 1, 13);\r
-        fNewDate->fValue[CentYear] += fQuotient(temp, 1, 13);\r
-    }\r
-\r
-    //fNewDate->fValue[utc] = UTC_STD_CHAR;\r
-    fNewDate->fValue[utc] = UTC_STD;\r
-}\r
-\r
-int DateTime::compareResult(int resultA\r
-                             , int resultB\r
-                             , bool strict)\r
-{\r
-\r
-    if ( resultB == XMLDateTime::INDETERMINATE )\r
-    {\r
-        return XMLDateTime::INDETERMINATE;\r
-    }\r
-    else if ( (resultA != resultB) &&\r
-              strict                )\r
-    {\r
-        return XMLDateTime::INDETERMINATE;\r
-    }\r
-    else if ( (resultA != resultB) &&\r
-              !strict               )\r
-    {\r
-        if ( (resultA != XMLDateTime::EQUAL) &&\r
-             (resultB != XMLDateTime::EQUAL)  )\r
-        {\r
-            return XMLDateTime::INDETERMINATE;\r
-        }\r
-        else\r
-        {\r
-            return (resultA != XMLDateTime::EQUAL)? resultA : resultB;\r
-        }\r
-    }\r
-\r
-    return resultA;\r
-       \r
-}\r
-\r
-// ---------------------------------------------------------------------------\r
-//  static methods : for others\r
-// ---------------------------------------------------------------------------\r
-int DateTime::compare(const DateTime* const pDate1\r
-                       , const DateTime* const pDate2)\r
-{\r
-\r
-    if (pDate1->fValue[utc] == pDate2->fValue[utc])\r
-    {\r
-        return DateTime::compareOrder(pDate1, pDate2);\r
-    }\r
-\r
-    int c1, c2;\r
-\r
-    if ( pDate1->isNormalized())\r
-    {\r
-        c1 = compareResult(pDate1, pDate2, false, UTC_POS);\r
-        c2 = compareResult(pDate1, pDate2, false, UTC_NEG);\r
-        return getRetVal(c1, c2);\r
-    }\r
-    else if ( pDate2->isNormalized())\r
-    {\r
-        c1 = compareResult(pDate1, pDate2, true, UTC_POS);\r
-        c2 = compareResult(pDate1, pDate2, true, UTC_NEG);\r
-        return getRetVal(c1, c2);\r
-    }\r
-\r
-    return XMLDateTime::INDETERMINATE; \r
-}\r
-\r
-int DateTime::compareResult(const DateTime* const pDate1\r
-                             , const DateTime* const pDate2\r
-                             , bool  set2Left\r
-                             , int   utc_type)\r
-{\r
-    DateTime tmpDate = (set2Left ? *pDate1 : *pDate2);\r
-\r
-    tmpDate.fTimeZone[hh] = 14;\r
-    tmpDate.fTimeZone[mm] = 0;\r
-    tmpDate.fValue[utc] = utc_type;\r
-    tmpDate.normalize();\r
-\r
-    return (set2Left? DateTime::compareOrder(&tmpDate, pDate2) :\r
-                      DateTime::compareOrder(pDate1, &tmpDate));\r
-}\r
-\r
-int DateTime::compareOrder(const DateTime* const lValue\r
-                            , const DateTime* const rValue)\r
-                            //, MemoryManager* const memMgr)\r
-{\r
-    //\r
-    // If any of the them is not normalized() yet,\r
-    // we need to do something here.\r
-    //\r
-    DateTime lTemp = *lValue;\r
-    DateTime rTemp = *rValue;\r
-\r
-    lTemp.normalize();\r
-    rTemp.normalize();\r
-\r
-    for ( int i = 0 ; i < TOTAL_SIZE; i++ )\r
-    {\r
-        if ( lTemp.fValue[i] < rTemp.fValue[i] )\r
-        {\r
-            return XMLDateTime::LESS_THAN;\r
-        }\r
-        else if ( lTemp.fValue[i] > rTemp.fValue[i] )\r
-        {\r
-            return XMLDateTime::GREATER_THAN;\r
-        }\r
-    }\r
-\r
-    if ( lTemp.fHasTime)\r
-    {\r
-        if ( lTemp.fMiliSecond < rTemp.fMiliSecond )\r
-        {\r
-            return XMLDateTime::LESS_THAN;\r
-        }\r
-        else if ( lTemp.fMiliSecond > rTemp.fMiliSecond )\r
-        {\r
-            return XMLDateTime::GREATER_THAN;\r
-        }\r
-    }\r
-\r
-    return XMLDateTime::EQUAL;\r
-}\r
-\r
-// ---------------------------------------------------------------------------\r
-//  ctor and dtor\r
-// ---------------------------------------------------------------------------\r
-DateTime::~DateTime()\r
-{\r
-    delete[] fBuffer;\r
-}\r
-\r
-DateTime::DateTime()\r
-: fStart(0)\r
-, fEnd(0)\r
-, fBufferMaxLen(0)\r
-, fBuffer(0)\r
-, fMiliSecond(0)\r
-, fHasTime(false)\r
-{\r
-    reset();\r
-}\r
-\r
-DateTime::DateTime(const XMLCh* const aString)\r
-: fStart(0)\r
-, fEnd(0)\r
-, fBufferMaxLen(0)\r
-, fBuffer(0)\r
-, fMiliSecond(0)\r
-, fHasTime(false)\r
-{\r
-    setBuffer(aString);\r
-}\r
-\r
-DateTime::DateTime(time_t epoch)\r
-: fStart(0)\r
-, fEnd(0)\r
-, fBufferMaxLen(0)\r
-, fBuffer(0)\r
-, fMiliSecond(0)\r
-, fHasTime(false)\r
-{\r
-#ifndef HAVE_GMTIME_R\r
-    struct tm* ptime=gmtime(&epoch);\r
-#else\r
-    struct tm res;\r
-    struct tm* ptime=gmtime_r(&epoch,&res);\r
-#endif\r
-    char timebuf[32];\r
-    strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);\r
-    auto_ptr_XMLCh timeptr(timebuf);\r
-    setBuffer(timeptr.get());\r
-}\r
-\r
-// -----------------------------------------------------------------------\r
-// Copy ctor and Assignment operators\r
-// -----------------------------------------------------------------------\r
-\r
-DateTime::DateTime(const DateTime &toCopy)\r
-: fBufferMaxLen(0)\r
-, fBuffer(0)\r
-{\r
-    copy(toCopy);\r
-}\r
-\r
-DateTime& DateTime::operator=(const DateTime& rhs)\r
-{\r
-    if (this == &rhs)\r
-        return *this;\r
-\r
-    copy(rhs);\r
-    return *this;\r
-}\r
-\r
-// -----------------------------------------------------------------------\r
-// Implementation of Abstract Interface\r
-// -----------------------------------------------------------------------\r
-\r
-//\r
-// We may simply return the handle to fBuffer\r
-//\r
-const XMLCh*  DateTime::getRawData() const\r
-{\r
-    //assertBuffer();\r
-    return fBuffer;\r
-}\r
-\r
-\r
-const XMLCh*  DateTime::getFormattedString() const\r
-{\r
-    return getRawData();\r
-}\r
-\r
-int DateTime::getSign() const\r
-{\r
-    return 0;\r
-}\r
-\r
-time_t DateTime::getEpoch() const\r
-{\r
-    struct tm t;\r
-    t.tm_sec=getSecond();\r
-    t.tm_min=getMinute();\r
-    t.tm_hour=getHour();\r
-    t.tm_mday=getDay();\r
-    t.tm_mon=getMonth()-1;\r
-    t.tm_year=getYear()-1900;\r
-    t.tm_isdst=0;\r
-#if defined(HAVE_TIMEGM)\r
-    return timegm(&t);\r
-#else\r
-    // Windows, and hopefully most others...?\r
-    return mktime(&t) - timezone;\r
-#endif\r
-}\r
-\r
-// ---------------------------------------------------------------------------\r
-//  Parsers\r
-// ---------------------------------------------------------------------------\r
-\r
-//\r
-// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}[TimeZone]\r
-//\r
-void DateTime::parseDateTime()\r
-{\r
-    initParser();\r
-    getDate();\r
-\r
-    //fStart is supposed to point to 'T'\r
-    if (fBuffer[fStart++] != DATETIME_SEPARATOR)\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_gDay_invalid\r
-                , fBuffer);\r
-\r
-    getTime();\r
-    validateDateTime();\r
-    normalize();\r
-    fHasTime = true;\r
-}\r
-\r
-//\r
-// [-]{CCYY-MM-DD}[TimeZone]\r
-//\r
-void DateTime::parseDate()\r
-{\r
-    initParser();\r
-    getDate();\r
-    parseTimeZone();\r
-    validateDateTime();\r
-    normalize();\r
-}\r
-\r
-void DateTime::parseTime()\r
-{\r
-    initParser();\r
-\r
-    // time initialize to default values\r
-    fValue[CentYear]= YEAR_DEFAULT;\r
-    fValue[Month]   = MONTH_DEFAULT;\r
-    fValue[Day]     = DAY_DEFAULT;\r
-\r
-    getTime();\r
-\r
-    validateDateTime();\r
-    normalize();\r
-    fHasTime = true;\r
-}\r
-\r
-//\r
-// {---DD}[TimeZone]\r
-//  01234\r
-//\r
-void DateTime::parseDay()\r
-{\r
-    initParser();\r
-\r
-    if (fBuffer[0] != DATE_SEPARATOR ||\r
-        fBuffer[1] != DATE_SEPARATOR ||\r
-        fBuffer[2] != DATE_SEPARATOR  )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_gDay_invalid\r
-                , fBuffer);\r
-    }\r
-\r
-    //initialize values\r
-    fValue[CentYear] = YEAR_DEFAULT;\r
-    fValue[Month]    = MONTH_DEFAULT;\r
-    fValue[Day]      = parseInt(fStart+3, fStart+5);\r
-\r
-    if ( DAY_SIZE < fEnd )\r
-    {\r
-        int sign = findUTCSign(DAY_SIZE);\r
-        if ( sign < 0 )\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_gDay_invalid\r
-                    , fBuffer);\r
-        }\r
-        else\r
-        {\r
-            getTimeZone(sign);\r
-        }\r
-    }\r
-\r
-    validateDateTime();\r
-    normalize();\r
-}\r
-\r
-//\r
-// {--MM--}[TimeZone]\r
-// {--MM}[TimeZone]\r
-//  012345\r
-//\r
-void DateTime::parseMonth()\r
-{\r
-    initParser();\r
-\r
-    if (fBuffer[0] != DATE_SEPARATOR ||\r
-        fBuffer[1] != DATE_SEPARATOR  )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_gMth_invalid\r
-                , fBuffer);\r
-    }\r
-\r
-    //set constants\r
-    fValue[CentYear] = YEAR_DEFAULT;\r
-    fValue[Day]      = DAY_DEFAULT;\r
-    fValue[Month]    = parseInt(2, 4);\r
-\r
-    // REVISIT: allow both --MM and --MM-- now. \r
-    // need to remove the following lines to disallow --MM-- \r
-    // when the errata is officially in the rec. \r
-    fStart = 4;\r
-    if ( fEnd >= fStart+2 && fBuffer[fStart] == DATE_SEPARATOR && fBuffer[fStart+1] == DATE_SEPARATOR ) \r
-    { \r
-        fStart += 2; \r
-    } \r
-\r
-    //\r
-    // parse TimeZone if any\r
-    //\r
-    if ( fStart < fEnd )\r
-    {\r
-        int sign = findUTCSign(fStart);\r
-        if ( sign < 0 )\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_gMth_invalid\r
-                    , fBuffer);\r
-        }\r
-        else\r
-        {\r
-            getTimeZone(sign);\r
-        }\r
-    }\r
-\r
-    validateDateTime();\r
-    normalize();\r
-}\r
-\r
-//\r
-//[-]{CCYY}[TimeZone]\r
-// 0  1234\r
-//\r
-void DateTime::parseYear()\r
-{\r
-    initParser();\r
-\r
-    // skip the first '-' and search for timezone\r
-    //\r
-    int sign = findUTCSign((fBuffer[0] == chDash) ? 1 : 0);\r
-\r
-    if (sign == NOT_FOUND)\r
-    {\r
-        fValue[CentYear] = parseIntYear(fEnd);\r
-    }\r
-    else\r
-    {\r
-        fValue[CentYear] = parseIntYear(sign);\r
-        getTimeZone(sign);\r
-    }\r
-\r
-    //initialize values\r
-    fValue[Month] = MONTH_DEFAULT;\r
-    fValue[Day]   = DAY_DEFAULT;   //java is 1\r
-\r
-    validateDateTime();\r
-    normalize();\r
-}\r
-\r
-//\r
-//{--MM-DD}[TimeZone]\r
-// 0123456\r
-//\r
-void DateTime::parseMonthDay()\r
-{\r
-    initParser();\r
-\r
-    if (fBuffer[0] != DATE_SEPARATOR ||\r
-        fBuffer[1] != DATE_SEPARATOR ||\r
-        fBuffer[4] != DATE_SEPARATOR )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_gMthDay_invalid\r
-                , fBuffer);\r
-    }\r
-\r
-\r
-    //initialize\r
-    fValue[CentYear] = YEAR_DEFAULT;\r
-    fValue[Month]    = parseInt(2, 4); \r
-    fValue[Day]      = parseInt(5, 7);\r
-\r
-    if ( MONTHDAY_SIZE < fEnd )\r
-    {\r
-        int sign = findUTCSign(MONTHDAY_SIZE);\r
-        if ( sign<0 )\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_gMthDay_invalid\r
-                    , fBuffer);\r
-        }\r
-        else\r
-        {\r
-            getTimeZone(sign);\r
-        }\r
-    }\r
-\r
-    validateDateTime();\r
-    normalize();\r
-}\r
-\r
-void DateTime::parseYearMonth()\r
-{\r
-    initParser();\r
-\r
-    // get date\r
-    getYearMonth();\r
-    fValue[Day] = DAY_DEFAULT;\r
-    parseTimeZone();\r
-\r
-    validateDateTime();\r
-    normalize();\r
-}\r
-\r
-//\r
-//PnYn MnDTnH nMnS: -P1Y2M3DT10H30M\r
-//\r
-// [-]{'P'{[n'Y'][n'M'][n'D']['T'][n'H'][n'M'][n'S']}}\r
-//\r
-//  Note: the n above shall be >= 0\r
-//        if no time element found, 'T' shall be absent\r
-//\r
-void DateTime::parseDuration()\r
-{\r
-    initParser();\r
-\r
-    // must start with '-' or 'P'\r
-    //\r
-    XMLCh c = fBuffer[fStart++];\r
-    if ( (c != DURATION_STARTER) &&\r
-         (c != chDash)            )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_dur_Start_dashP\r
-                , fBuffer);\r
-    }\r
-\r
-    // 'P' must ALWAYS be present in either case\r
-    if ( (c == chDash) &&\r
-         (fBuffer[fStart++]!= DURATION_STARTER ))\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_dur_noP\r
-                , fBuffer);\r
-    }\r
-\r
-    // java code\r
-    //date[utc]=(c=='-')?'-':0;\r
-    //fValue[utc] = UTC_STD;\r
-    fValue[utc] = (fBuffer[0] == chDash? UTC_NEG : UTC_STD);\r
-\r
-    int negate = ( fBuffer[0] == chDash ? -1 : 1);\r
-\r
-    //\r
-    // No negative value is allowed after 'P'\r
-    //\r
-    // eg P-1234, invalid\r
-    //\r
-    if (indexOf(fStart, fEnd, chDash) != NOT_FOUND)\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_dur_DashNotFirst\r
-                , fBuffer);\r
-    }\r
-\r
-    //at least one number and designator must be seen after P\r
-    bool designator = false;\r
-\r
-    int endDate = indexOf(fStart, fEnd, DATETIME_SEPARATOR);\r
-    if ( endDate == NOT_FOUND )\r
-    {\r
-        endDate = fEnd;  // 'T' absent\r
-    }\r
-\r
-    //find 'Y'\r
-    int end = indexOf(fStart, endDate, DURATION_Y);\r
-    if ( end != NOT_FOUND )\r
-    {\r
-        //scan year\r
-        fValue[CentYear] = negate * parseInt(fStart, end);\r
-        fStart = end+1;\r
-        designator = true;\r
-    }\r
-\r
-    end = indexOf(fStart, endDate, DURATION_M);\r
-    if ( end != NOT_FOUND )\r
-    {\r
-        //scan month\r
-        fValue[Month] = negate * parseInt(fStart, end);\r
-        fStart = end+1;\r
-        designator = true;\r
-    }\r
-\r
-    end = indexOf(fStart, endDate, DURATION_D);\r
-    if ( end != NOT_FOUND )\r
-    {\r
-        //scan day\r
-        fValue[Day] = negate * parseInt(fStart,end);\r
-        fStart = end+1;\r
-        designator = true;\r
-    }\r
-\r
-    if ( (fEnd == endDate) &&   // 'T' absent\r
-         (fStart != fEnd)   )   // something after Day\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_dur_inv_b4T\r
-                , fBuffer);\r
-    }\r
-\r
-    if ( fEnd != endDate ) // 'T' present\r
-    {\r
-        //scan hours, minutes, seconds\r
-        //\r
-\r
-        // skip 'T' first\r
-        end = indexOf(++fStart, fEnd, DURATION_H);\r
-        if ( end != NOT_FOUND )\r
-        {\r
-            //scan hours\r
-            fValue[Hour] = negate * parseInt(fStart, end);\r
-            fStart = end+1;\r
-            designator = true;\r
-        }\r
-\r
-        end = indexOf(fStart, fEnd, DURATION_M);\r
-        if ( end != NOT_FOUND )\r
-        {\r
-            //scan min\r
-            fValue[Minute] = negate * parseInt(fStart, end);\r
-            fStart = end+1;\r
-            designator = true;\r
-        }\r
-\r
-        end = indexOf(fStart, fEnd, DURATION_S);\r
-        if ( end != NOT_FOUND )\r
-        {\r
-            //scan seconds\r
-            int mlsec = indexOf (fStart, end, MILISECOND_SEPARATOR);\r
-\r
-            /***\r
-             * Schema Errata: E2-23\r
-             * at least one digit must follow the decimal point if it appears. \r
-             * That is, the value of the seconds component must conform \r
-             * to the following pattern: [0-9]+(.[0-9]+)? \r
-             */\r
-            if ( mlsec != NOT_FOUND )\r
-            {\r
-                /***\r
-                 * make usure there is something after the '.' and before the end.\r
-                 */\r
-                if ( mlsec+1 == end )\r
-                {\r
-                    ThrowXML1(SchemaDateTimeException\r
-                            , XMLExcepts::DateTime_dur_inv_seconds\r
-                            , fBuffer);\r
-                }\r
-\r
-                fValue[Second]     = negate * parseInt(fStart, mlsec);\r
-                fMiliSecond        = negate * parseMiliSecond(mlsec+1, end);\r
-            }\r
-            else\r
-            {\r
-                fValue[Second] = negate * parseInt(fStart,end);\r
-            }\r
-\r
-            fStart = end+1;\r
-            designator = true;\r
-        }\r
-\r
-        // no additional data should appear after last item\r
-        // P1Y1M1DT is illigal value as well\r
-        if ( (fStart != fEnd) ||\r
-              fBuffer[--fStart] == DATETIME_SEPARATOR )\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_dur_NoTimeAfterT\r
-                    , fBuffer);\r
-        }\r
-    }\r
-\r
-    if ( !designator )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_dur_NoElementAtAll\r
-                , fBuffer);\r
-    }\r
-\r
-}\r
-\r
-// ---------------------------------------------------------------------------\r
-//  Scanners\r
-// ---------------------------------------------------------------------------\r
-\r
-//\r
-// [-]{CCYY-MM-DD}\r
-//\r
-// Note: CCYY could be more than 4 digits\r
-//       Assuming fStart point to the beginning of the Date Section\r
-//       fStart updated to point to the position right AFTER the second 'D'\r
-//       Since the lenght of CCYY might be variable, we can't check format upfront\r
-//\r
-void DateTime::getDate()\r
-{\r
-\r
-    // Ensure enough chars in buffer\r
-    if ( (fStart+YMD_MIN_SIZE) > fEnd)\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_date_incomplete\r
-                , fBuffer);\r
-\r
-    getYearMonth();    // Scan YearMonth and\r
-                       // fStart point to the next '-'\r
-\r
-    if (fBuffer[fStart++] != DATE_SEPARATOR)\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_date_invalid\r
-                , fBuffer);\r
-        //("CCYY-MM must be followed by '-' sign");\r
-    }\r
-\r
-    fValue[Day] = parseInt(fStart, fStart+2);\r
-    fStart += 2 ;  //fStart points right after the Day\r
-\r
-    return;\r
-}\r
-\r
-//\r
-// hh:mm:ss[.msssss]['Z']\r
-// hh:mm:ss[.msssss][['+'|'-']hh:mm]\r
-// 012345678\r
-//\r
-// Note: Assuming fStart point to the beginning of the Time Section\r
-//       fStart updated to point to the position right AFTER the second 's'\r
-//                                                  or ms if any\r
-//\r
-void DateTime::getTime()\r
-{\r
-\r
-    // Ensure enough chars in buffer\r
-    if ( (fStart+TIME_MIN_SIZE) > fEnd)\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_time_incomplete\r
-                , fBuffer);\r
-        //"Imcomplete Time Format"\r
-\r
-    // check (fixed) format first\r
-    if ((fBuffer[fStart + 2] != TIME_SEPARATOR) ||\r
-        (fBuffer[fStart + 5] != TIME_SEPARATOR)  )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_time_invalid\r
-                , fBuffer);\r
-        //("Error in parsing time" );\r
-    }\r
-\r
-    //\r
-    // get hours, minute and second\r
-    //\r
-    fValue[Hour]   = parseInt(fStart + 0, fStart + 2);\r
-    fValue[Minute] = parseInt(fStart + 3, fStart + 5);\r
-    fValue[Second] = parseInt(fStart + 6, fStart + 8);\r
-    fStart += 8;\r
-\r
-    // to see if any ms and/or utc part after that\r
-    if (fStart >= fEnd)\r
-        return;\r
-\r
-    //find UTC sign if any\r
-    int sign = findUTCSign(fStart);\r
-\r
-    //parse miliseconds\r
-    int milisec = (fBuffer[fStart] == MILISECOND_SEPARATOR)? fStart : NOT_FOUND;\r
-    if ( milisec != NOT_FOUND )\r
-    {\r
-        fStart++;   // skip the '.'\r
-        // make sure we have some thing between the '.' and fEnd\r
-        if (fStart >= fEnd)\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_ms_noDigit\r
-                    , fBuffer);\r
-            //("ms shall be present once '.' is present" );\r
-        }\r
-\r
-        if ( sign == NOT_FOUND )\r
-        {\r
-            fMiliSecond = parseMiliSecond(fStart, fEnd);  //get ms between '.' and fEnd\r
-            fStart = fEnd;\r
-        }\r
-        else\r
-        {\r
-            fMiliSecond = parseMiliSecond(fStart, sign);  //get ms between UTC sign and fEnd\r
-        }\r
-       }\r
-    else if(sign == 0 || sign != fStart)\r
-    {\r
-        // seconds has more than 2 digits\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_min_invalid\r
-                , fBuffer);\r
-    }\r
-\r
-    //parse UTC time zone (hh:mm)\r
-    if ( sign > 0 ) {\r
-        getTimeZone(sign);\r
-    }\r
-\r
-}\r
-\r
-//\r
-// [-]{CCYY-MM}\r
-//\r
-// Note: CCYY could be more than 4 digits\r
-//       fStart updated to point AFTER the second 'M' (probably meet the fEnd)\r
-//\r
-void DateTime::getYearMonth()\r
-{\r
-\r
-    // Ensure enough chars in buffer\r
-    if ( (fStart+YMONTH_MIN_SIZE) > fEnd)\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_ym_incomplete\r
-                , fBuffer);\r
-        //"Imcomplete YearMonth Format";\r
-\r
-    // skip the first leading '-'\r
-    int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;\r
-\r
-    //\r
-    // search for year separator '-'\r
-    //\r
-    int yearSeparator = indexOf(start, fEnd, DATE_SEPARATOR);\r
-    if ( yearSeparator == NOT_FOUND)\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_ym_invalid\r
-                , fBuffer);\r
-        //("Year separator is missing or misplaced");\r
-\r
-    fValue[CentYear] = parseIntYear(yearSeparator);\r
-    fStart = yearSeparator + 1;  //skip the '-' and point to the first M\r
-\r
-    //\r
-    //gonna check we have enough byte for month\r
-    //\r
-    if ((fStart + 2) > fEnd )\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_ym_noMonth\r
-                , fBuffer);\r
-        //"no month in buffer"\r
-\r
-    fValue[Month] = parseInt(fStart, yearSeparator + 3);\r
-    fStart += 2;  //fStart points right after the MONTH\r
-\r
-    return;\r
-}\r
-\r
-void DateTime::parseTimeZone()\r
-{\r
-    if ( fStart < fEnd )\r
-    {\r
-        int sign = findUTCSign(fStart);\r
-        if ( sign < 0 )\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_tz_noUTCsign\r
-                    , fBuffer);\r
-            //("Error in month parsing");\r
-        }\r
-        else\r
-        {\r
-            getTimeZone(sign);\r
-        }\r
-    }\r
-\r
-    return;\r
-}\r
-\r
-//\r
-// 'Z'\r
-// ['+'|'-']hh:mm\r
-//\r
-// Note: Assuming fStart points to the beginning of TimeZone section\r
-//       fStart updated to meet fEnd\r
-//\r
-void DateTime::getTimeZone(const int sign)\r
-{\r
-\r
-    if ( fBuffer[sign] == UTC_STD_CHAR )\r
-    {\r
-        if ((sign + 1) != fEnd )\r
-        {\r
-            ThrowXML1(SchemaDateTimeException\r
-                    , XMLExcepts::DateTime_tz_stuffAfterZ\r
-                    , fBuffer);\r
-            //"Error in parsing time zone");\r
-        }              \r
-\r
-        return;        \r
-    }\r
-\r
-    //\r
-    // otherwise, it has to be this format\r
-    // '[+|-]'hh:mm\r
-    //    1   23456 7\r
-    //   sign      fEnd\r
-    //\r
-    if ( ( ( sign + TIMEZONE_SIZE + 1) != fEnd )      ||\r
-         ( fBuffer[sign + 3] != TIMEZONE_SEPARATOR ) )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_tz_invalid\r
-                , fBuffer);\r
-        //("Error in parsing time zone");\r
-    }\r
-\r
-    fTimeZone[hh] = parseInt(sign+1, sign+3);          \r
-    fTimeZone[mm] = parseInt(sign+4, fEnd);\r
-                       \r
-    return;\r
-}\r
-\r
-// ---------------------------------------------------------------------------\r
-//  Validator and normalizer\r
-// ---------------------------------------------------------------------------\r
-\r
-/**\r
- * If timezone present - normalize dateTime  [E Adding durations to dateTimes]\r
- *\r
- * @param date   CCYY-MM-DDThh:mm:ss+03\r
- * @return CCYY-MM-DDThh:mm:ssZ\r
- */\r
-void DateTime::normalize()\r
-{\r
-\r
-    if ((fValue[utc] == UTC_UNKNOWN) ||\r
-        (fValue[utc] == UTC_STD)      )\r
-        return;\r
-\r
-    int negate = (fValue[utc] == UTC_POS)? -1: 1;\r
-\r
-    // add mins\r
-    int temp = fValue[Minute] + negate * fTimeZone[mm];\r
-    int carry = fQuotient(temp, 60);\r
-    fValue[Minute] = mod(temp, 60, carry);\r
-\r
-    //add hours\r
-    temp = fValue[Hour] + negate * fTimeZone[hh] + carry;\r
-    carry = fQuotient(temp, 24);\r
-    fValue[Hour] = mod(temp, 24, carry);\r
-\r
-    fValue[Day] += carry;\r
-\r
-    while (1)\r
-    {\r
-        temp = maxDayInMonthFor(fValue[CentYear], fValue[Month]);\r
-        if (fValue[Day] < 1)\r
-        {\r
-            fValue[Day] += maxDayInMonthFor(fValue[CentYear], fValue[Month] - 1);\r
-            carry = -1;\r
-        }\r
-        else if ( fValue[Day] > temp )\r
-        {\r
-            fValue[Day] -= temp;\r
-            carry = 1;\r
-        }\r
-        else\r
-        {\r
-            break;\r
-        }\r
-\r
-        temp = fValue[Month] + carry;\r
-        fValue[Month] = modulo(temp, 1, 13);\r
-        fValue[CentYear] += fQuotient(temp, 1, 13);\r
-    }\r
-\r
-    // set to normalized\r
-    fValue[utc] = UTC_STD;\r
-\r
-    return;\r
-}\r
-\r
-void DateTime::validateDateTime() const\r
-{\r
-\r
-    //REVISIT: should we throw an exception for not valid dates\r
-    //          or reporting an error message should be sufficient?\r
-    if ( fValue[CentYear] == 0 )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_year_zero\r
-                , fBuffer);\r
-        //"The year \"0000\" is an illegal year value");\r
-    }\r
-\r
-    if ( fValue[Month] < 1  ||\r
-         fValue[Month] > 12  )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_mth_invalid\r
-                , fBuffer);\r
-               //"The month must have values 1 to 12");\r
-    }\r
-\r
-    //validate days\r
-    if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) ||\r
-         fValue[Day] == 0 )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_day_invalid\r
-                , fBuffer);\r
-        //"The day must have values 1 to 31");\r
-    }\r
-\r
-    //validate hours\r
-    if ((fValue[Hour] < 0)  ||\r
-        (fValue[Hour] > 24) ||\r
-        ((fValue[Hour] == 24) && ((fValue[Minute] !=0) ||\r
-                                  (fValue[Second] !=0) ||\r
-                                  (fMiliSecond    !=0))))\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_hour_invalid\r
-                , fBuffer);\r
-        //("Hour must have values 0-23");\r
-    }\r
-\r
-    //validate minutes\r
-    if ( fValue[Minute] < 0 ||\r
-         fValue[Minute] > 59 )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_min_invalid\r
-                , fBuffer);\r
-        //"Minute must have values 0-59");\r
-    }\r
-\r
-    //validate seconds\r
-    if ( fValue[Second] < 0 ||\r
-         fValue[Second] > 60 )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_second_invalid\r
-                , fBuffer);\r
-        //"Second must have values 0-60");\r
-    }\r
-\r
-    //validate time-zone hours\r
-    if ( (abs(fTimeZone[hh]) > 14) ||\r
-         ((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_tz_hh_invalid\r
-                , fBuffer);\r
-        //"Time zone should have range -14..+14");\r
-    }\r
-\r
-    //validate time-zone minutes\r
-    if ( abs(fTimeZone[mm]) > 59 )\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_min_invalid\r
-                , fBuffer);\r
-        //("Minute must have values 0-59");\r
-    }\r
-       \r
-    return;\r
-}\r
-\r
-// -----------------------------------------------------------------------\r
-// locator and converter\r
-// -----------------------------------------------------------------------\r
-int DateTime::indexOf(const int start, const int end, const XMLCh ch) const\r
-{\r
-    for ( int i = start; i < end; i++ )\r
-        if ( fBuffer[i] == ch )\r
-            return i;\r
-\r
-    return NOT_FOUND;\r
-}\r
-\r
-int DateTime::findUTCSign (const int start)\r
-{\r
-    int  pos;\r
-    for ( int index = start; index < fEnd; index++ )\r
-    {\r
-        pos = XMLString::indexOf(UTC_SET, fBuffer[index]);\r
-        if ( pos != NOT_FOUND)\r
-        {\r
-            fValue[utc] = pos+1;   // refer to utcType, there is 1 diff\r
-            return index;\r
-        }\r
-    }\r
-\r
-    return NOT_FOUND;\r
-}\r
-\r
-//\r
-// Note:\r
-//    start: starting point in fBuffer\r
-//    end:   ending point in fBuffer (exclusive)\r
-//    fStart NOT updated\r
-//\r
-int DateTime::parseInt(const int start, const int end) const\r
-{\r
-    unsigned int retVal = 0;\r
-    for (int i=start; i < end; i++) {\r
-\r
-        if (fBuffer[i] < chDigit_0 || fBuffer[i] > chDigit_9)\r
-            ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars);\r
-\r
-        retVal = (retVal * 10) + (unsigned int) (fBuffer[i] - chDigit_0);\r
-    }\r
-\r
-    return (int) retVal;\r
-}\r
-\r
-//\r
-// Note:\r
-//    start: pointing to the first digit after the '.'\r
-//    end:   pointing to one position after the last digit\r
-//    fStart NOT updated\r
-//\r
-double DateTime::parseMiliSecond(const int start, const int end) const\r
-{\r
-\r
-    unsigned int  miliSecLen = (end-1) - (start-1) + 1; //to include the '.'\r
-    XMLCh* miliSecData = new XMLCh[miliSecLen + 1];\r
-    ArrayJanitor<XMLCh> janMili(miliSecData);\r
-    XMLString::copyNString(miliSecData, &(fBuffer[start-1]), miliSecLen);\r
-    *(miliSecData + miliSecLen) = chNull;\r
-\r
-    char *nptr = XMLString::transcode(miliSecData);\r
-    ArrayJanitor<char> jan(nptr);\r
-    size_t   strLen = strlen(nptr);\r
-    char *endptr = 0;\r
-    errno = 0;\r
-\r
-    //printf("milisec=<%s>\n", nptr);\r
-\r
-    double retVal = strtod(nptr, &endptr);\r
-\r
-    // check if all chars are valid char\r
-    if ( (endptr - nptr) != strLen)\r
-        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars);\r
-\r
-    // we don't check underflow occurs since\r
-    // nothing we can do about it.\r
-    return retVal;\r
-}\r
-\r
-//\r
-// [-]CCYY\r
-//\r
-// Note: start from fStart\r
-//       end (exclusive)\r
-//       fStart NOT updated\r
-//\r
-int DateTime::parseIntYear(const int end) const\r
-{\r
-    // skip the first leading '-'\r
-    int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;\r
-\r
-    int length = end - start;\r
-    if (length < 4)\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_year_tooShort\r
-                , fBuffer);\r
-        //"Year must have 'CCYY' format");\r
-    }\r
-    else if (length > 4 &&\r
-             fBuffer[start] == chDigit_0)\r
-    {\r
-        ThrowXML1(SchemaDateTimeException\r
-                , XMLExcepts::DateTime_year_leadingZero\r
-                , fBuffer);\r
-        //"Leading zeros are required if the year value would otherwise have fewer than four digits;\r
-        // otherwise they are forbidden");\r
-    }\r
-\r
-    bool negative = (fBuffer[0] == chDash);\r
-    int  yearVal = parseInt((negative ? 1 : 0), end);\r
-    return ( negative ? (-1) * yearVal : yearVal );\r
-}\r
-\r
-/***\r
- * E2-41\r
- *\r
- *  3.2.7.2 Canonical representation\r
- * \r
- *  Except for trailing fractional zero digits in the seconds representation, \r
- *  '24:00:00' time representations, and timezone (for timezoned values), \r
- *  the mapping from literals to values is one-to-one. Where there is more \r
- *  than one possible representation, the canonical representation is as follows: \r
- *  redundant trailing zero digits in fractional-second literals are prohibited. \r
- *  An hour representation of '24' is prohibited. Timezoned values are canonically\r
- *  represented by appending 'Z' to the nontimezoned representation. (All \r
- *  timezoned dateTime values are UTC.) \r
- *\r
- *  .'24:00:00' -> '00:00:00'\r
- *  .milisecond: trailing zeros removed\r
- *  .'Z'\r
- *\r
- ***/\r
-XMLCh* DateTime::getDateTimeCanonicalRepresentation() const\r
-{\r
-    XMLCh *miliStartPtr, *miliEndPtr;\r
-    searchMiliSeconds(miliStartPtr, miliEndPtr);\r
-    size_t miliSecondsLen = miliEndPtr - miliStartPtr;\r
-\r
-    XMLCh* retBuf = new XMLCh[21 + miliSecondsLen + 2];\r
-    XMLCh* retPtr = retBuf;\r
-\r
-    // (-?) cc+yy-mm-dd'T'hh:mm:ss'Z'    ('.'s+)?\r
-    //      2+  8       1      8   1\r
-    //\r
-    int additionalLen = fillYearString(retPtr, CentYear);\r
-    if(additionalLen != 0)\r
-    {\r
-        // very bad luck; have to resize the buffer...\r
-        XMLCh *tmpBuf = new XMLCh[additionalLen+21+miliSecondsLen +2];\r
-        XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen);\r
-        retPtr = tmpBuf+(retPtr-retBuf);\r
-        delete[] retBuf;\r
-        retBuf = tmpBuf;\r
-    }\r
-    *retPtr++ = DATE_SEPARATOR;\r
-    fillString(retPtr, Month, 2);\r
-    *retPtr++ = DATE_SEPARATOR;\r
-    fillString(retPtr, Day, 2);\r
-    *retPtr++ = DATETIME_SEPARATOR;\r
-\r
-    fillString(retPtr, Hour, 2);\r
-    if (fValue[Hour] == 24)\r
-    {\r
-        *(retPtr - 2) = chDigit_0;\r
-        *(retPtr - 1) = chDigit_0;\r
-    }\r
-    *retPtr++ = TIME_SEPARATOR;\r
-    fillString(retPtr, Minute, 2);\r
-    *retPtr++ = TIME_SEPARATOR;\r
-    fillString(retPtr, Second, 2);\r
-\r
-    if (miliSecondsLen)\r
-    {\r
-        *retPtr++ = chPeriod;\r
-        XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);\r
-        retPtr += miliSecondsLen;\r
-    }\r
-\r
-    *retPtr++ = UTC_STD_CHAR;\r
-    *retPtr = chNull;\r
-\r
-    return retBuf;\r
-}\r
-\r
-/***\r
- * 3.2.8 time\r
- *\r
- *  . either the time zone must be omitted or, \r
- *    if present, the time zone must be Coordinated Universal Time (UTC) indicated by a "Z".   \r
- *\r
- *  . Additionally, the canonical representation for midnight is 00:00:00.\r
- *\r
-***/\r
-XMLCh* DateTime::getTimeCanonicalRepresentation() const\r
-{\r
-    XMLCh *miliStartPtr, *miliEndPtr;\r
-    searchMiliSeconds(miliStartPtr, miliEndPtr);\r
-    size_t miliSecondsLen = miliEndPtr - miliStartPtr;\r
-\r
-    XMLCh* retBuf = new XMLCh[10 + miliSecondsLen + 2];\r
-    XMLCh* retPtr = retBuf;\r
-\r
-    // 'hh:mm:ss'Z'    ('.'s+)?\r
-    //      8    1\r
-    //\r
-\r
-    fillString(retPtr, Hour, 2);\r
-    if (fValue[Hour] == 24)\r
-    {\r
-        *(retPtr - 2) = chDigit_0;\r
-        *(retPtr - 1) = chDigit_0;\r
-    }\r
-    *retPtr++ = TIME_SEPARATOR;\r
-    fillString(retPtr, Minute, 2);\r
-    *retPtr++ = TIME_SEPARATOR;\r
-    fillString(retPtr, Second, 2);\r
-\r
-    if (miliSecondsLen)\r
-    {\r
-        *retPtr++ = chPeriod;\r
-        XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);\r
-        retPtr += miliSecondsLen;\r
-    }\r
-\r
-    *retPtr++ = UTC_STD_CHAR;\r
-    *retPtr = chNull;\r
-\r
-    return retBuf;\r
-}\r
-\r
-void DateTime::fillString(XMLCh*& ptr, valueIndex ind, int expLen) const\r
-{\r
-    XMLCh strBuffer[16];\r
-    assert(expLen < 16);\r
-    XMLString::binToText(fValue[ind], strBuffer, expLen, 10);\r
-    int   actualLen = XMLString::stringLen(strBuffer);\r
-    int   i;\r
-    //append leading zeros\r
-    for (i = 0; i < expLen - actualLen; i++)\r
-    {\r
-        *ptr++ = chDigit_0;\r
-    }\r
-\r
-    for (i = 0; i < actualLen; i++)\r
-    {\r
-        *ptr++ = strBuffer[i];\r
-    }\r
-\r
-}\r
-\r
-int DateTime::fillYearString(XMLCh*& ptr, valueIndex ind) const\r
-{\r
-    XMLCh strBuffer[16];\r
-    // let's hope we get no years of 15 digits...\r
-    XMLString::binToText(fValue[ind], strBuffer, 15, 10);\r
-    int   actualLen = XMLString::stringLen(strBuffer);\r
-    // don't forget that years can be negative...\r
-    int negativeYear = 0;\r
-    if(strBuffer[0] == chDash)\r
-    {\r
-        *ptr++ = strBuffer[0];\r
-        negativeYear = 1;\r
-    }\r
-    int   i;\r
-    //append leading zeros\r
-    for (i = 0; i < 4 - actualLen+negativeYear; i++)\r
-    {\r
-        *ptr++ = chDigit_0;\r
-    }\r
-\r
-    for (i = negativeYear; i < actualLen; i++)\r
-    {\r
-        *ptr++ = strBuffer[i];\r
-    }\r
-    if(actualLen > 4)\r
-        return actualLen-4;\r
-    return 0;\r
-}\r
-\r
-/***\r
- *\r
- *   .check if the rawData has the mili second component\r
- *   .capture the substring\r
- *\r
- ***/\r
-void DateTime::searchMiliSeconds(XMLCh*& miliStartPtr, XMLCh*& miliEndPtr) const\r
-{\r
-    miliStartPtr = miliEndPtr = 0;\r
-\r
-    int milisec = XMLString::indexOf(fBuffer, MILISECOND_SEPARATOR);\r
-    if (milisec == -1)\r
-        return;\r
-\r
-    miliStartPtr = fBuffer + milisec + 1;\r
-    miliEndPtr   = miliStartPtr;\r
-    while (*miliEndPtr)\r
-    {\r
-        if ((*miliEndPtr < chDigit_0) || (*miliEndPtr > chDigit_9))\r
-            break;\r
-\r
-        miliEndPtr++;\r
-    }\r
-\r
-    //remove trailing zeros\r
-    while( *(miliEndPtr - 1) == chDigit_0)\r
-        miliEndPtr--;\r
-\r
-    return;\r
-}\r
-\r
+/*
+ *  Copyright 2001-2006 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * DateTime.cpp
+ * 
+ * Manipulation of XML date/time data. 
+ */
+
+/*
+ * This is mostly copied from Xerces-C, but they don't seem inclined to produce a usable
+ * class, so I had to incorporate my own version of it for now. I can't inherit it
+ * since the fields I need are private.
+ */
+
+#include "internal.h"
+#include "util/DateTime.h"
+
+#ifndef WIN32
+# include <errno.h>
+#endif
+
+#include <ctime>
+#include <xercesc/util/Janitor.hpp>
+#include <xercesc/util/NumberFormatException.hpp>
+
+using namespace xmltooling;
+using namespace std;
+
+//
+// constants used to process raw data (fBuffer)
+//
+// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}['Z']
+//                                [{+|-}hh:mm']
+//
+
+static const XMLCh DURATION_STARTER     = chLatin_P;              // 'P'
+static const XMLCh DURATION_Y           = chLatin_Y;              // 'Y'
+static const XMLCh DURATION_M           = chLatin_M;              // 'M'
+static const XMLCh DURATION_D           = chLatin_D;              // 'D'
+static const XMLCh DURATION_H           = chLatin_H;              // 'H'
+static const XMLCh DURATION_S           = chLatin_S;              // 'S'
+
+static const XMLCh DATE_SEPARATOR       = chDash;                 // '-'
+static const XMLCh TIME_SEPARATOR       = chColon;                // ':'
+static const XMLCh TIMEZONE_SEPARATOR   = chColon;                // ':'
+static const XMLCh DATETIME_SEPARATOR   = chLatin_T;              // 'T'
+static const XMLCh MILISECOND_SEPARATOR = chPeriod;               // '.'
+
+static const XMLCh UTC_STD_CHAR         = chLatin_Z;              // 'Z'
+static const XMLCh UTC_POS_CHAR         = chPlus;                 // '+'
+static const XMLCh UTC_NEG_CHAR         = chDash;                 // '-'
+
+static const XMLCh UTC_SET[]            = {UTC_STD_CHAR           //"Z+-"
+                                         , UTC_POS_CHAR
+                                         , UTC_NEG_CHAR
+                                         , chNull};
+
+static const int YMD_MIN_SIZE    = 10;   // CCYY-MM-DD
+static const int YMONTH_MIN_SIZE = 7;    // CCYY_MM
+static const int TIME_MIN_SIZE   = 8;    // hh:mm:ss
+static const int TIMEZONE_SIZE   = 5;    // hh:mm
+static const int DAY_SIZE        = 5;    // ---DD
+//static const int MONTH_SIZE      = 6;    // --MM--
+static const int MONTHDAY_SIZE   = 7;    // --MM-DD
+static const int NOT_FOUND       = -1;
+
+//define constants to be used in assigning default values for
+//all date/time excluding duration
+static const int YEAR_DEFAULT  = 2000;
+static const int MONTH_DEFAULT = 01;
+static const int DAY_DEFAULT   = 15;
+
+// order-relation on duration is a partial order. The dates below are used to
+// for comparison of 2 durations, based on the fact that
+// duration x and y is x<=y iff s+x<=s+y
+// see 3.2.6 duration W3C schema datatype specs
+//
+// the dates are in format: {CCYY,MM,DD, H, S, M, MS, timezone}
+const int DateTime::DATETIMES[][TOTAL_SIZE] =
+{
+    {1696, 9, 1, 0, 0, 0, 0, UTC_STD},
+       {1697, 2, 1, 0, 0, 0, 0, UTC_STD},
+       {1903, 3, 1, 0, 0, 0, 0, UTC_STD},
+       {1903, 7, 1, 0, 0, 0, 0, UTC_STD}
+};
+
+// ---------------------------------------------------------------------------
+//  local methods
+// ---------------------------------------------------------------------------
+static inline int fQuotient(int a, int b)
+{
+    div_t div_result = div(a, b);
+    return div_result.quot;
+}
+
+static inline int fQuotient(int temp, int low, int high)
+{
+    return fQuotient(temp - low, high - low);
+}
+
+static inline int mod(int a, int b, int quotient)
+{
+       return (a - quotient*b) ;
+}
+
+static inline int modulo (int temp, int low, int high)
+{
+    //modulo(a - low, high - low) + low
+    int a = temp - low;
+    int b = high - low;
+    return (mod (a, b, fQuotient(a, b)) + low) ;
+}
+
+static inline bool isLeapYear(int year)
+{
+    return((year%4 == 0) && ((year%100 != 0) || (year%400 == 0)));
+}
+
+static int maxDayInMonthFor(int year, int month)
+{
+
+    if ( month == 4 || month == 6 || month == 9 || month == 11 )
+    {
+        return 30;
+    }
+    else if ( month==2 )
+    {
+        if ( isLeapYear(year) )
+            return 29;
+        else
+            return 28;
+    }
+    else
+    {
+        return 31;
+    }
+
+}
+
+// ---------------------------------------------------------------------------
+//  static methods : for duration
+// ---------------------------------------------------------------------------
+/**
+ * Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
+ *
+ * 3.2.6.2 Order relation on duration
+ *
+ *     In general, the order-relation on duration is a partial order since there is no
+ *  determinate relationship between certain durations such as one month (P1M) and 30 days (P30D).
+ *  The order-relation of two duration values x and y is x < y iff s+x < s+y for each qualified
+ *  dateTime s in the list below.
+ *
+ *     These values for s cause the greatest deviations in the addition of dateTimes and durations
+ *
+ **/
+int DateTime::compare(const DateTime* const pDate1
+                       , const DateTime* const pDate2
+                       , bool  strict)
+{
+    //REVISIT: this is unoptimazed vs of comparing 2 durations
+    //         Algorithm is described in 3.2.6.2 W3C Schema Datatype specs
+    //
+
+    int resultA, resultB = XMLDateTime::INDETERMINATE;
+
+    //try and see if the objects are equal
+    if ( (resultA = compareOrder(pDate1, pDate2)) == XMLDateTime::EQUAL)
+        return XMLDateTime::EQUAL;
+
+    //long comparison algorithm is required
+    DateTime tempA, *pTempA = &tempA;
+    DateTime tempB, *pTempB = &tempB;
+
+    addDuration(pTempA, pDate1, 0);
+    addDuration(pTempB, pDate2, 0);
+    resultA = compareOrder(pTempA, pTempB);
+    if ( resultA == XMLDateTime::INDETERMINATE )
+        return XMLDateTime::INDETERMINATE;
+
+    addDuration(pTempA, pDate1, 1);
+    addDuration(pTempB, pDate2, 1);
+    resultB = compareOrder(pTempA, pTempB);
+    resultA = compareResult(resultA, resultB, strict);
+    if ( resultA == XMLDateTime::INDETERMINATE )
+        return XMLDateTime::INDETERMINATE;
+
+    addDuration(pTempA, pDate1, 2);
+    addDuration(pTempB, pDate2, 2);
+    resultB = compareOrder(pTempA, pTempB);
+    resultA = compareResult(resultA, resultB, strict);
+    if ( resultA == XMLDateTime::INDETERMINATE )
+        return XMLDateTime::INDETERMINATE;
+
+    addDuration(pTempA, pDate1, 3);
+    addDuration(pTempB, pDate2, 3);
+    resultB = compareOrder(pTempA, pTempB);
+    resultA = compareResult(resultA, resultB, strict);
+
+    return resultA;
+
+}
+
+//
+// Form a new DateTime with duration and baseDate array
+// Note: C++        Java
+//       fNewDate   duration
+//       fDuration  date
+//
+
+void DateTime::addDuration(DateTime*             fNewDate
+                            , const DateTime* const fDuration
+                            , int index)
+
+{
+
+    //REVISIT: some code could be shared between normalize() and this method,
+    //         however is it worth moving it? The structures are different...
+    //
+
+    fNewDate->reset();
+    //add months (may be modified additionaly below)
+    int temp = DATETIMES[index][Month] + fDuration->fValue[Month];
+    fNewDate->fValue[Month] = modulo(temp, 1, 13);
+    int carry = fQuotient(temp, 1, 13);
+
+    //add years (may be modified additionaly below)
+    fNewDate->fValue[CentYear] =
+        DATETIMES[index][CentYear] + fDuration->fValue[CentYear] + carry;
+
+    //add seconds
+    temp = DATETIMES[index][Second] + fDuration->fValue[Second];
+    carry = fQuotient (temp, 60);
+    fNewDate->fValue[Second] =  mod(temp, 60, carry);
+               
+    //add minutes
+    temp = DATETIMES[index][Minute] + fDuration->fValue[Minute] + carry;
+    carry = fQuotient(temp, 60);
+    fNewDate->fValue[Minute] = mod(temp, 60, carry);
+
+    //add hours
+    temp = DATETIMES[index][Hour] + fDuration->fValue[Hour] + carry;
+    carry = fQuotient(temp, 24);
+    fNewDate->fValue[Hour] = mod(temp, 24, carry);
+               
+    fNewDate->fValue[Day] =
+        DATETIMES[index][Day] + fDuration->fValue[Day] + carry;
+
+    while ( true )
+    {
+        temp = maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]);
+        if ( fNewDate->fValue[Day] < 1 )
+        { //original fNewDate was negative
+            fNewDate->fValue[Day] +=
+                maxDayInMonthFor(fNewDate->fValue[CentYear], fNewDate->fValue[Month]-1);
+            carry = -1;
+        }
+        else if ( fNewDate->fValue[Day] > temp )
+        {
+            fNewDate->fValue[Day] -= temp;
+            carry = 1;
+        }
+        else
+        {
+            break;
+        }
+
+        temp = fNewDate->fValue[Month] + carry;
+        fNewDate->fValue[Month] = modulo(temp, 1, 13);
+        fNewDate->fValue[CentYear] += fQuotient(temp, 1, 13);
+    }
+
+    //fNewDate->fValue[utc] = UTC_STD_CHAR;
+    fNewDate->fValue[utc] = UTC_STD;
+}
+
+int DateTime::compareResult(int resultA
+                             , int resultB
+                             , bool strict)
+{
+
+    if ( resultB == XMLDateTime::INDETERMINATE )
+    {
+        return XMLDateTime::INDETERMINATE;
+    }
+    else if ( (resultA != resultB) &&
+              strict                )
+    {
+        return XMLDateTime::INDETERMINATE;
+    }
+    else if ( (resultA != resultB) &&
+              !strict               )
+    {
+        if ( (resultA != XMLDateTime::EQUAL) &&
+             (resultB != XMLDateTime::EQUAL)  )
+        {
+            return XMLDateTime::INDETERMINATE;
+        }
+        else
+        {
+            return (resultA != XMLDateTime::EQUAL)? resultA : resultB;
+        }
+    }
+
+    return resultA;
+       
+}
+
+// ---------------------------------------------------------------------------
+//  static methods : for others
+// ---------------------------------------------------------------------------
+int DateTime::compare(const DateTime* const pDate1
+                       , const DateTime* const pDate2)
+{
+
+    if (pDate1->fValue[utc] == pDate2->fValue[utc])
+    {
+        return DateTime::compareOrder(pDate1, pDate2);
+    }
+
+    int c1, c2;
+
+    if ( pDate1->isNormalized())
+    {
+        c1 = compareResult(pDate1, pDate2, false, UTC_POS);
+        c2 = compareResult(pDate1, pDate2, false, UTC_NEG);
+        return getRetVal(c1, c2);
+    }
+    else if ( pDate2->isNormalized())
+    {
+        c1 = compareResult(pDate1, pDate2, true, UTC_POS);
+        c2 = compareResult(pDate1, pDate2, true, UTC_NEG);
+        return getRetVal(c1, c2);
+    }
+
+    return XMLDateTime::INDETERMINATE; 
+}
+
+int DateTime::compareResult(const DateTime* const pDate1
+                             , const DateTime* const pDate2
+                             , bool  set2Left
+                             , int   utc_type)
+{
+    DateTime tmpDate = (set2Left ? *pDate1 : *pDate2);
+
+    tmpDate.fTimeZone[hh] = 14;
+    tmpDate.fTimeZone[mm] = 0;
+    tmpDate.fValue[utc] = utc_type;
+    tmpDate.normalize();
+
+    return (set2Left? DateTime::compareOrder(&tmpDate, pDate2) :
+                      DateTime::compareOrder(pDate1, &tmpDate));
+}
+
+int DateTime::compareOrder(const DateTime* const lValue
+                            , const DateTime* const rValue)
+                            //, MemoryManager* const memMgr)
+{
+    //
+    // If any of the them is not normalized() yet,
+    // we need to do something here.
+    //
+    DateTime lTemp = *lValue;
+    DateTime rTemp = *rValue;
+
+    lTemp.normalize();
+    rTemp.normalize();
+
+    for ( int i = 0 ; i < TOTAL_SIZE; i++ )
+    {
+        if ( lTemp.fValue[i] < rTemp.fValue[i] )
+        {
+            return XMLDateTime::LESS_THAN;
+        }
+        else if ( lTemp.fValue[i] > rTemp.fValue[i] )
+        {
+            return XMLDateTime::GREATER_THAN;
+        }
+    }
+
+    if ( lTemp.fHasTime)
+    {
+        if ( lTemp.fMiliSecond < rTemp.fMiliSecond )
+        {
+            return XMLDateTime::LESS_THAN;
+        }
+        else if ( lTemp.fMiliSecond > rTemp.fMiliSecond )
+        {
+            return XMLDateTime::GREATER_THAN;
+        }
+    }
+
+    return XMLDateTime::EQUAL;
+}
+
+// ---------------------------------------------------------------------------
+//  ctor and dtor
+// ---------------------------------------------------------------------------
+DateTime::~DateTime()
+{
+    delete[] fBuffer;
+}
+
+DateTime::DateTime()
+: fStart(0)
+, fEnd(0)
+, fBufferMaxLen(0)
+, fBuffer(0)
+, fMiliSecond(0)
+, fHasTime(false)
+{
+    reset();
+}
+
+DateTime::DateTime(const XMLCh* const aString)
+: fStart(0)
+, fEnd(0)
+, fBufferMaxLen(0)
+, fBuffer(0)
+, fMiliSecond(0)
+, fHasTime(false)
+{
+    setBuffer(aString);
+}
+
+DateTime::DateTime(time_t epoch)
+: fStart(0)
+, fEnd(0)
+, fBufferMaxLen(0)
+, fBuffer(0)
+, fMiliSecond(0)
+, fHasTime(false)
+{
+#ifndef HAVE_GMTIME_R
+    struct tm* ptime=gmtime(&epoch);
+#else
+    struct tm res;
+    struct tm* ptime=gmtime_r(&epoch,&res);
+#endif
+    char timebuf[32];
+    strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
+    auto_ptr_XMLCh timeptr(timebuf);
+    setBuffer(timeptr.get());
+}
+
+// -----------------------------------------------------------------------
+// Copy ctor and Assignment operators
+// -----------------------------------------------------------------------
+
+DateTime::DateTime(const DateTime &toCopy)
+: fBufferMaxLen(0)
+, fBuffer(0)
+{
+    copy(toCopy);
+}
+
+DateTime& DateTime::operator=(const DateTime& rhs)
+{
+    if (this == &rhs)
+        return *this;
+
+    copy(rhs);
+    return *this;
+}
+
+// -----------------------------------------------------------------------
+// Implementation of Abstract Interface
+// -----------------------------------------------------------------------
+
+//
+// We may simply return the handle to fBuffer
+//
+const XMLCh*  DateTime::getRawData() const
+{
+    //assertBuffer();
+    return fBuffer;
+}
+
+
+const XMLCh*  DateTime::getFormattedString() const
+{
+    return getRawData();
+}
+
+int DateTime::getSign() const
+{
+    return 0;
+}
+
+time_t DateTime::getEpoch() const
+{
+    struct tm t;
+    t.tm_sec=getSecond();
+    t.tm_min=getMinute();
+    t.tm_hour=getHour();
+    t.tm_mday=getDay();
+    t.tm_mon=getMonth()-1;
+    t.tm_year=getYear()-1900;
+    t.tm_isdst=0;
+#if defined(HAVE_TIMEGM)
+    return timegm(&t);
+#else
+    // Windows, and hopefully most others...?
+    return mktime(&t) - timezone;
+#endif
+}
+
+// ---------------------------------------------------------------------------
+//  Parsers
+// ---------------------------------------------------------------------------
+
+//
+// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}[TimeZone]
+//
+void DateTime::parseDateTime()
+{
+    initParser();
+    getDate();
+
+    //fStart is supposed to point to 'T'
+    if (fBuffer[fStart++] != DATETIME_SEPARATOR)
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_gDay_invalid
+                , fBuffer);
+
+    getTime();
+    validateDateTime();
+    normalize();
+    fHasTime = true;
+}
+
+//
+// [-]{CCYY-MM-DD}[TimeZone]
+//
+void DateTime::parseDate()
+{
+    initParser();
+    getDate();
+    parseTimeZone();
+    validateDateTime();
+    normalize();
+}
+
+void DateTime::parseTime()
+{
+    initParser();
+
+    // time initialize to default values
+    fValue[CentYear]= YEAR_DEFAULT;
+    fValue[Month]   = MONTH_DEFAULT;
+    fValue[Day]     = DAY_DEFAULT;
+
+    getTime();
+
+    validateDateTime();
+    normalize();
+    fHasTime = true;
+}
+
+//
+// {---DD}[TimeZone]
+//  01234
+//
+void DateTime::parseDay()
+{
+    initParser();
+
+    if (fBuffer[0] != DATE_SEPARATOR ||
+        fBuffer[1] != DATE_SEPARATOR ||
+        fBuffer[2] != DATE_SEPARATOR  )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_gDay_invalid
+                , fBuffer);
+    }
+
+    //initialize values
+    fValue[CentYear] = YEAR_DEFAULT;
+    fValue[Month]    = MONTH_DEFAULT;
+    fValue[Day]      = parseInt(fStart+3, fStart+5);
+
+    if ( DAY_SIZE < fEnd )
+    {
+        int sign = findUTCSign(DAY_SIZE);
+        if ( sign < 0 )
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_gDay_invalid
+                    , fBuffer);
+        }
+        else
+        {
+            getTimeZone(sign);
+        }
+    }
+
+    validateDateTime();
+    normalize();
+}
+
+//
+// {--MM--}[TimeZone]
+// {--MM}[TimeZone]
+//  012345
+//
+void DateTime::parseMonth()
+{
+    initParser();
+
+    if (fBuffer[0] != DATE_SEPARATOR ||
+        fBuffer[1] != DATE_SEPARATOR  )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_gMth_invalid
+                , fBuffer);
+    }
+
+    //set constants
+    fValue[CentYear] = YEAR_DEFAULT;
+    fValue[Day]      = DAY_DEFAULT;
+    fValue[Month]    = parseInt(2, 4);
+
+    // REVISIT: allow both --MM and --MM-- now. 
+    // need to remove the following lines to disallow --MM-- 
+    // when the errata is officially in the rec. 
+    fStart = 4;
+    if ( fEnd >= fStart+2 && fBuffer[fStart] == DATE_SEPARATOR && fBuffer[fStart+1] == DATE_SEPARATOR ) 
+    { 
+        fStart += 2; 
+    } 
+
+    //
+    // parse TimeZone if any
+    //
+    if ( fStart < fEnd )
+    {
+        int sign = findUTCSign(fStart);
+        if ( sign < 0 )
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_gMth_invalid
+                    , fBuffer);
+        }
+        else
+        {
+            getTimeZone(sign);
+        }
+    }
+
+    validateDateTime();
+    normalize();
+}
+
+//
+//[-]{CCYY}[TimeZone]
+// 0  1234
+//
+void DateTime::parseYear()
+{
+    initParser();
+
+    // skip the first '-' and search for timezone
+    //
+    int sign = findUTCSign((fBuffer[0] == chDash) ? 1 : 0);
+
+    if (sign == NOT_FOUND)
+    {
+        fValue[CentYear] = parseIntYear(fEnd);
+    }
+    else
+    {
+        fValue[CentYear] = parseIntYear(sign);
+        getTimeZone(sign);
+    }
+
+    //initialize values
+    fValue[Month] = MONTH_DEFAULT;
+    fValue[Day]   = DAY_DEFAULT;   //java is 1
+
+    validateDateTime();
+    normalize();
+}
+
+//
+//{--MM-DD}[TimeZone]
+// 0123456
+//
+void DateTime::parseMonthDay()
+{
+    initParser();
+
+    if (fBuffer[0] != DATE_SEPARATOR ||
+        fBuffer[1] != DATE_SEPARATOR ||
+        fBuffer[4] != DATE_SEPARATOR )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_gMthDay_invalid
+                , fBuffer);
+    }
+
+
+    //initialize
+    fValue[CentYear] = YEAR_DEFAULT;
+    fValue[Month]    = parseInt(2, 4); 
+    fValue[Day]      = parseInt(5, 7);
+
+    if ( MONTHDAY_SIZE < fEnd )
+    {
+        int sign = findUTCSign(MONTHDAY_SIZE);
+        if ( sign<0 )
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_gMthDay_invalid
+                    , fBuffer);
+        }
+        else
+        {
+            getTimeZone(sign);
+        }
+    }
+
+    validateDateTime();
+    normalize();
+}
+
+void DateTime::parseYearMonth()
+{
+    initParser();
+
+    // get date
+    getYearMonth();
+    fValue[Day] = DAY_DEFAULT;
+    parseTimeZone();
+
+    validateDateTime();
+    normalize();
+}
+
+//
+//PnYn MnDTnH nMnS: -P1Y2M3DT10H30M
+//
+// [-]{'P'{[n'Y'][n'M'][n'D']['T'][n'H'][n'M'][n'S']}}
+//
+//  Note: the n above shall be >= 0
+//        if no time element found, 'T' shall be absent
+//
+void DateTime::parseDuration()
+{
+    initParser();
+
+    // must start with '-' or 'P'
+    //
+    XMLCh c = fBuffer[fStart++];
+    if ( (c != DURATION_STARTER) &&
+         (c != chDash)            )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_dur_Start_dashP
+                , fBuffer);
+    }
+
+    // 'P' must ALWAYS be present in either case
+    if ( (c == chDash) &&
+         (fBuffer[fStart++]!= DURATION_STARTER ))
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_dur_noP
+                , fBuffer);
+    }
+
+    // java code
+    //date[utc]=(c=='-')?'-':0;
+    //fValue[utc] = UTC_STD;
+    fValue[utc] = (fBuffer[0] == chDash? UTC_NEG : UTC_STD);
+
+    int negate = ( fBuffer[0] == chDash ? -1 : 1);
+
+    //
+    // No negative value is allowed after 'P'
+    //
+    // eg P-1234, invalid
+    //
+    if (indexOf(fStart, fEnd, chDash) != NOT_FOUND)
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_dur_DashNotFirst
+                , fBuffer);
+    }
+
+    //at least one number and designator must be seen after P
+    bool designator = false;
+
+    int endDate = indexOf(fStart, fEnd, DATETIME_SEPARATOR);
+    if ( endDate == NOT_FOUND )
+    {
+        endDate = fEnd;  // 'T' absent
+    }
+
+    //find 'Y'
+    int end = indexOf(fStart, endDate, DURATION_Y);
+    if ( end != NOT_FOUND )
+    {
+        //scan year
+        fValue[CentYear] = negate * parseInt(fStart, end);
+        fStart = end+1;
+        designator = true;
+    }
+
+    end = indexOf(fStart, endDate, DURATION_M);
+    if ( end != NOT_FOUND )
+    {
+        //scan month
+        fValue[Month] = negate * parseInt(fStart, end);
+        fStart = end+1;
+        designator = true;
+    }
+
+    end = indexOf(fStart, endDate, DURATION_D);
+    if ( end != NOT_FOUND )
+    {
+        //scan day
+        fValue[Day] = negate * parseInt(fStart,end);
+        fStart = end+1;
+        designator = true;
+    }
+
+    if ( (fEnd == endDate) &&   // 'T' absent
+         (fStart != fEnd)   )   // something after Day
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_dur_inv_b4T
+                , fBuffer);
+    }
+
+    if ( fEnd != endDate ) // 'T' present
+    {
+        //scan hours, minutes, seconds
+        //
+
+        // skip 'T' first
+        end = indexOf(++fStart, fEnd, DURATION_H);
+        if ( end != NOT_FOUND )
+        {
+            //scan hours
+            fValue[Hour] = negate * parseInt(fStart, end);
+            fStart = end+1;
+            designator = true;
+        }
+
+        end = indexOf(fStart, fEnd, DURATION_M);
+        if ( end != NOT_FOUND )
+        {
+            //scan min
+            fValue[Minute] = negate * parseInt(fStart, end);
+            fStart = end+1;
+            designator = true;
+        }
+
+        end = indexOf(fStart, fEnd, DURATION_S);
+        if ( end != NOT_FOUND )
+        {
+            //scan seconds
+            int mlsec = indexOf (fStart, end, MILISECOND_SEPARATOR);
+
+            /***
+             * Schema Errata: E2-23
+             * at least one digit must follow the decimal point if it appears. 
+             * That is, the value of the seconds component must conform 
+             * to the following pattern: [0-9]+(.[0-9]+)? 
+             */
+            if ( mlsec != NOT_FOUND )
+            {
+                /***
+                 * make usure there is something after the '.' and before the end.
+                 */
+                if ( mlsec+1 == end )
+                {
+                    ThrowXML1(SchemaDateTimeException
+                            , XMLExcepts::DateTime_dur_inv_seconds
+                            , fBuffer);
+                }
+
+                fValue[Second]     = negate * parseInt(fStart, mlsec);
+                fMiliSecond        = negate * parseMiliSecond(mlsec+1, end);
+            }
+            else
+            {
+                fValue[Second] = negate * parseInt(fStart,end);
+            }
+
+            fStart = end+1;
+            designator = true;
+        }
+
+        // no additional data should appear after last item
+        // P1Y1M1DT is illigal value as well
+        if ( (fStart != fEnd) ||
+              fBuffer[--fStart] == DATETIME_SEPARATOR )
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_dur_NoTimeAfterT
+                    , fBuffer);
+        }
+    }
+
+    if ( !designator )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_dur_NoElementAtAll
+                , fBuffer);
+    }
+
+}
+
+// ---------------------------------------------------------------------------
+//  Scanners
+// ---------------------------------------------------------------------------
+
+//
+// [-]{CCYY-MM-DD}
+//
+// Note: CCYY could be more than 4 digits
+//       Assuming fStart point to the beginning of the Date Section
+//       fStart updated to point to the position right AFTER the second 'D'
+//       Since the lenght of CCYY might be variable, we can't check format upfront
+//
+void DateTime::getDate()
+{
+
+    // Ensure enough chars in buffer
+    if ( (fStart+YMD_MIN_SIZE) > fEnd)
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_date_incomplete
+                , fBuffer);
+
+    getYearMonth();    // Scan YearMonth and
+                       // fStart point to the next '-'
+
+    if (fBuffer[fStart++] != DATE_SEPARATOR)
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_date_invalid
+                , fBuffer);
+        //("CCYY-MM must be followed by '-' sign");
+    }
+
+    fValue[Day] = parseInt(fStart, fStart+2);
+    fStart += 2 ;  //fStart points right after the Day
+
+    return;
+}
+
+//
+// hh:mm:ss[.msssss]['Z']
+// hh:mm:ss[.msssss][['+'|'-']hh:mm]
+// 012345678
+//
+// Note: Assuming fStart point to the beginning of the Time Section
+//       fStart updated to point to the position right AFTER the second 's'
+//                                                  or ms if any
+//
+void DateTime::getTime()
+{
+
+    // Ensure enough chars in buffer
+    if ( (fStart+TIME_MIN_SIZE) > fEnd)
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_time_incomplete
+                , fBuffer);
+        //"Imcomplete Time Format"
+
+    // check (fixed) format first
+    if ((fBuffer[fStart + 2] != TIME_SEPARATOR) ||
+        (fBuffer[fStart + 5] != TIME_SEPARATOR)  )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_time_invalid
+                , fBuffer);
+        //("Error in parsing time" );
+    }
+
+    //
+    // get hours, minute and second
+    //
+    fValue[Hour]   = parseInt(fStart + 0, fStart + 2);
+    fValue[Minute] = parseInt(fStart + 3, fStart + 5);
+    fValue[Second] = parseInt(fStart + 6, fStart + 8);
+    fStart += 8;
+
+    // to see if any ms and/or utc part after that
+    if (fStart >= fEnd)
+        return;
+
+    //find UTC sign if any
+    int sign = findUTCSign(fStart);
+
+    //parse miliseconds
+    int milisec = (fBuffer[fStart] == MILISECOND_SEPARATOR)? fStart : NOT_FOUND;
+    if ( milisec != NOT_FOUND )
+    {
+        fStart++;   // skip the '.'
+        // make sure we have some thing between the '.' and fEnd
+        if (fStart >= fEnd)
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_ms_noDigit
+                    , fBuffer);
+            //("ms shall be present once '.' is present" );
+        }
+
+        if ( sign == NOT_FOUND )
+        {
+            fMiliSecond = parseMiliSecond(fStart, fEnd);  //get ms between '.' and fEnd
+            fStart = fEnd;
+        }
+        else
+        {
+            fMiliSecond = parseMiliSecond(fStart, sign);  //get ms between UTC sign and fEnd
+        }
+       }
+    else if(sign == 0 || sign != fStart)
+    {
+        // seconds has more than 2 digits
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_min_invalid
+                , fBuffer);
+    }
+
+    //parse UTC time zone (hh:mm)
+    if ( sign > 0 ) {
+        getTimeZone(sign);
+    }
+
+}
+
+//
+// [-]{CCYY-MM}
+//
+// Note: CCYY could be more than 4 digits
+//       fStart updated to point AFTER the second 'M' (probably meet the fEnd)
+//
+void DateTime::getYearMonth()
+{
+
+    // Ensure enough chars in buffer
+    if ( (fStart+YMONTH_MIN_SIZE) > fEnd)
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_ym_incomplete
+                , fBuffer);
+        //"Imcomplete YearMonth Format";
+
+    // skip the first leading '-'
+    int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;
+
+    //
+    // search for year separator '-'
+    //
+    int yearSeparator = indexOf(start, fEnd, DATE_SEPARATOR);
+    if ( yearSeparator == NOT_FOUND)
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_ym_invalid
+                , fBuffer);
+        //("Year separator is missing or misplaced");
+
+    fValue[CentYear] = parseIntYear(yearSeparator);
+    fStart = yearSeparator + 1;  //skip the '-' and point to the first M
+
+    //
+    //gonna check we have enough byte for month
+    //
+    if ((fStart + 2) > fEnd )
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_ym_noMonth
+                , fBuffer);
+        //"no month in buffer"
+
+    fValue[Month] = parseInt(fStart, yearSeparator + 3);
+    fStart += 2;  //fStart points right after the MONTH
+
+    return;
+}
+
+void DateTime::parseTimeZone()
+{
+    if ( fStart < fEnd )
+    {
+        int sign = findUTCSign(fStart);
+        if ( sign < 0 )
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_tz_noUTCsign
+                    , fBuffer);
+            //("Error in month parsing");
+        }
+        else
+        {
+            getTimeZone(sign);
+        }
+    }
+
+    return;
+}
+
+//
+// 'Z'
+// ['+'|'-']hh:mm
+//
+// Note: Assuming fStart points to the beginning of TimeZone section
+//       fStart updated to meet fEnd
+//
+void DateTime::getTimeZone(const int sign)
+{
+
+    if ( fBuffer[sign] == UTC_STD_CHAR )
+    {
+        if ((sign + 1) != fEnd )
+        {
+            ThrowXML1(SchemaDateTimeException
+                    , XMLExcepts::DateTime_tz_stuffAfterZ
+                    , fBuffer);
+            //"Error in parsing time zone");
+        }              
+
+        return;        
+    }
+
+    //
+    // otherwise, it has to be this format
+    // '[+|-]'hh:mm
+    //    1   23456 7
+    //   sign      fEnd
+    //
+    if ( ( ( sign + TIMEZONE_SIZE + 1) != fEnd )      ||
+         ( fBuffer[sign + 3] != TIMEZONE_SEPARATOR ) )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_tz_invalid
+                , fBuffer);
+        //("Error in parsing time zone");
+    }
+
+    fTimeZone[hh] = parseInt(sign+1, sign+3);          
+    fTimeZone[mm] = parseInt(sign+4, fEnd);
+                       
+    return;
+}
+
+// ---------------------------------------------------------------------------
+//  Validator and normalizer
+// ---------------------------------------------------------------------------
+
+/**
+ * If timezone present - normalize dateTime  [E Adding durations to dateTimes]
+ *
+ * @param date   CCYY-MM-DDThh:mm:ss+03
+ * @return CCYY-MM-DDThh:mm:ssZ
+ */
+void DateTime::normalize()
+{
+
+    if ((fValue[utc] == UTC_UNKNOWN) ||
+        (fValue[utc] == UTC_STD)      )
+        return;
+
+    int negate = (fValue[utc] == UTC_POS)? -1: 1;
+
+    // add mins
+    int temp = fValue[Minute] + negate * fTimeZone[mm];
+    int carry = fQuotient(temp, 60);
+    fValue[Minute] = mod(temp, 60, carry);
+
+    //add hours
+    temp = fValue[Hour] + negate * fTimeZone[hh] + carry;
+    carry = fQuotient(temp, 24);
+    fValue[Hour] = mod(temp, 24, carry);
+
+    fValue[Day] += carry;
+
+    while (1)
+    {
+        temp = maxDayInMonthFor(fValue[CentYear], fValue[Month]);
+        if (fValue[Day] < 1)
+        {
+            fValue[Day] += maxDayInMonthFor(fValue[CentYear], fValue[Month] - 1);
+            carry = -1;
+        }
+        else if ( fValue[Day] > temp )
+        {
+            fValue[Day] -= temp;
+            carry = 1;
+        }
+        else
+        {
+            break;
+        }
+
+        temp = fValue[Month] + carry;
+        fValue[Month] = modulo(temp, 1, 13);
+        fValue[CentYear] += fQuotient(temp, 1, 13);
+    }
+
+    // set to normalized
+    fValue[utc] = UTC_STD;
+
+    return;
+}
+
+void DateTime::validateDateTime() const
+{
+
+    //REVISIT: should we throw an exception for not valid dates
+    //          or reporting an error message should be sufficient?
+    if ( fValue[CentYear] == 0 )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_year_zero
+                , fBuffer);
+        //"The year \"0000\" is an illegal year value");
+    }
+
+    if ( fValue[Month] < 1  ||
+         fValue[Month] > 12  )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_mth_invalid
+                , fBuffer);
+               //"The month must have values 1 to 12");
+    }
+
+    //validate days
+    if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) ||
+         fValue[Day] == 0 )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_day_invalid
+                , fBuffer);
+        //"The day must have values 1 to 31");
+    }
+
+    //validate hours
+    if ((fValue[Hour] < 0)  ||
+        (fValue[Hour] > 24) ||
+        ((fValue[Hour] == 24) && ((fValue[Minute] !=0) ||
+                                  (fValue[Second] !=0) ||
+                                  (fMiliSecond    !=0))))
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_hour_invalid
+                , fBuffer);
+        //("Hour must have values 0-23");
+    }
+
+    //validate minutes
+    if ( fValue[Minute] < 0 ||
+         fValue[Minute] > 59 )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_min_invalid
+                , fBuffer);
+        //"Minute must have values 0-59");
+    }
+
+    //validate seconds
+    if ( fValue[Second] < 0 ||
+         fValue[Second] > 60 )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_second_invalid
+                , fBuffer);
+        //"Second must have values 0-60");
+    }
+
+    //validate time-zone hours
+    if ( (abs(fTimeZone[hh]) > 14) ||
+         ((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_tz_hh_invalid
+                , fBuffer);
+        //"Time zone should have range -14..+14");
+    }
+
+    //validate time-zone minutes
+    if ( abs(fTimeZone[mm]) > 59 )
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_min_invalid
+                , fBuffer);
+        //("Minute must have values 0-59");
+    }
+       
+    return;
+}
+
+// -----------------------------------------------------------------------
+// locator and converter
+// -----------------------------------------------------------------------
+int DateTime::indexOf(const int start, const int end, const XMLCh ch) const
+{
+    for ( int i = start; i < end; i++ )
+        if ( fBuffer[i] == ch )
+            return i;
+
+    return NOT_FOUND;
+}
+
+int DateTime::findUTCSign (const int start)
+{
+    int  pos;
+    for ( int index = start; index < fEnd; index++ )
+    {
+        pos = XMLString::indexOf(UTC_SET, fBuffer[index]);
+        if ( pos != NOT_FOUND)
+        {
+            fValue[utc] = pos+1;   // refer to utcType, there is 1 diff
+            return index;
+        }
+    }
+
+    return NOT_FOUND;
+}
+
+//
+// Note:
+//    start: starting point in fBuffer
+//    end:   ending point in fBuffer (exclusive)
+//    fStart NOT updated
+//
+int DateTime::parseInt(const int start, const int end) const
+{
+    unsigned int retVal = 0;
+    for (int i=start; i < end; i++) {
+
+        if (fBuffer[i] < chDigit_0 || fBuffer[i] > chDigit_9)
+            ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars);
+
+        retVal = (retVal * 10) + (unsigned int) (fBuffer[i] - chDigit_0);
+    }
+
+    return (int) retVal;
+}
+
+//
+// Note:
+//    start: pointing to the first digit after the '.'
+//    end:   pointing to one position after the last digit
+//    fStart NOT updated
+//
+double DateTime::parseMiliSecond(const int start, const int end) const
+{
+
+    unsigned int  miliSecLen = (end-1) - (start-1) + 1; //to include the '.'
+    XMLCh* miliSecData = new XMLCh[miliSecLen + 1];
+    ArrayJanitor<XMLCh> janMili(miliSecData);
+    XMLString::copyNString(miliSecData, &(fBuffer[start-1]), miliSecLen);
+    *(miliSecData + miliSecLen) = chNull;
+
+    char *nptr = XMLString::transcode(miliSecData);
+    ArrayJanitor<char> jan(nptr);
+    size_t   strLen = strlen(nptr);
+    char *endptr = 0;
+    errno = 0;
+
+    //printf("milisec=<%s>\n", nptr);
+
+    double retVal = strtod(nptr, &endptr);
+
+    // check if all chars are valid char
+    if ( (endptr - nptr) != strLen)
+        ThrowXML(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars);
+
+    // we don't check underflow occurs since
+    // nothing we can do about it.
+    return retVal;
+}
+
+//
+// [-]CCYY
+//
+// Note: start from fStart
+//       end (exclusive)
+//       fStart NOT updated
+//
+int DateTime::parseIntYear(const int end) const
+{
+    // skip the first leading '-'
+    int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;
+
+    int length = end - start;
+    if (length < 4)
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_year_tooShort
+                , fBuffer);
+        //"Year must have 'CCYY' format");
+    }
+    else if (length > 4 &&
+             fBuffer[start] == chDigit_0)
+    {
+        ThrowXML1(SchemaDateTimeException
+                , XMLExcepts::DateTime_year_leadingZero
+                , fBuffer);
+        //"Leading zeros are required if the year value would otherwise have fewer than four digits;
+        // otherwise they are forbidden");
+    }
+
+    bool negative = (fBuffer[0] == chDash);
+    int  yearVal = parseInt((negative ? 1 : 0), end);
+    return ( negative ? (-1) * yearVal : yearVal );
+}
+
+/***
+ * E2-41
+ *
+ *  3.2.7.2 Canonical representation
+ * 
+ *  Except for trailing fractional zero digits in the seconds representation, 
+ *  '24:00:00' time representations, and timezone (for timezoned values), 
+ *  the mapping from literals to values is one-to-one. Where there is more 
+ *  than one possible representation, the canonical representation is as follows: 
+ *  redundant trailing zero digits in fractional-second literals are prohibited. 
+ *  An hour representation of '24' is prohibited. Timezoned values are canonically
+ *  represented by appending 'Z' to the nontimezoned representation. (All 
+ *  timezoned dateTime values are UTC.) 
+ *
+ *  .'24:00:00' -> '00:00:00'
+ *  .milisecond: trailing zeros removed
+ *  .'Z'
+ *
+ ***/
+XMLCh* DateTime::getDateTimeCanonicalRepresentation() const
+{
+    XMLCh *miliStartPtr, *miliEndPtr;
+    searchMiliSeconds(miliStartPtr, miliEndPtr);
+    size_t miliSecondsLen = miliEndPtr - miliStartPtr;
+
+    XMLCh* retBuf = new XMLCh[21 + miliSecondsLen + 2];
+    XMLCh* retPtr = retBuf;
+
+    // (-?) cc+yy-mm-dd'T'hh:mm:ss'Z'    ('.'s+)?
+    //      2+  8       1      8   1
+    //
+    int additionalLen = fillYearString(retPtr, CentYear);
+    if(additionalLen != 0)
+    {
+        // very bad luck; have to resize the buffer...
+        XMLCh *tmpBuf = new XMLCh[additionalLen+21+miliSecondsLen +2];
+        XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen);
+        retPtr = tmpBuf+(retPtr-retBuf);
+        delete[] retBuf;
+        retBuf = tmpBuf;
+    }
+    *retPtr++ = DATE_SEPARATOR;
+    fillString(retPtr, Month, 2);
+    *retPtr++ = DATE_SEPARATOR;
+    fillString(retPtr, Day, 2);
+    *retPtr++ = DATETIME_SEPARATOR;
+
+    fillString(retPtr, Hour, 2);
+    if (fValue[Hour] == 24)
+    {
+        *(retPtr - 2) = chDigit_0;
+        *(retPtr - 1) = chDigit_0;
+    }
+    *retPtr++ = TIME_SEPARATOR;
+    fillString(retPtr, Minute, 2);
+    *retPtr++ = TIME_SEPARATOR;
+    fillString(retPtr, Second, 2);
+
+    if (miliSecondsLen)
+    {
+        *retPtr++ = chPeriod;
+        XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);
+        retPtr += miliSecondsLen;
+    }
+
+    *retPtr++ = UTC_STD_CHAR;
+    *retPtr = chNull;
+
+    return retBuf;
+}
+
+/***
+ * 3.2.8 time
+ *
+ *  . either the time zone must be omitted or, 
+ *    if present, the time zone must be Coordinated Universal Time (UTC) indicated by a "Z".   
+ *
+ *  . Additionally, the canonical representation for midnight is 00:00:00.
+ *
+***/
+XMLCh* DateTime::getTimeCanonicalRepresentation() const
+{
+    XMLCh *miliStartPtr, *miliEndPtr;
+    searchMiliSeconds(miliStartPtr, miliEndPtr);
+    size_t miliSecondsLen = miliEndPtr - miliStartPtr;
+
+    XMLCh* retBuf = new XMLCh[10 + miliSecondsLen + 2];
+    XMLCh* retPtr = retBuf;
+
+    // 'hh:mm:ss'Z'    ('.'s+)?
+    //      8    1
+    //
+
+    fillString(retPtr, Hour, 2);
+    if (fValue[Hour] == 24)
+    {
+        *(retPtr - 2) = chDigit_0;
+        *(retPtr - 1) = chDigit_0;
+    }
+    *retPtr++ = TIME_SEPARATOR;
+    fillString(retPtr, Minute, 2);
+    *retPtr++ = TIME_SEPARATOR;
+    fillString(retPtr, Second, 2);
+
+    if (miliSecondsLen)
+    {
+        *retPtr++ = chPeriod;
+        XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);
+        retPtr += miliSecondsLen;
+    }
+
+    *retPtr++ = UTC_STD_CHAR;
+    *retPtr = chNull;
+
+    return retBuf;
+}
+
+void DateTime::fillString(XMLCh*& ptr, valueIndex ind, int expLen) const
+{
+    XMLCh strBuffer[16];
+    assert(expLen < 16);
+    XMLString::binToText(fValue[ind], strBuffer, expLen, 10);
+    int   actualLen = XMLString::stringLen(strBuffer);
+    int   i;
+    //append leading zeros
+    for (i = 0; i < expLen - actualLen; i++)
+    {
+        *ptr++ = chDigit_0;
+    }
+
+    for (i = 0; i < actualLen; i++)
+    {
+        *ptr++ = strBuffer[i];
+    }
+
+}
+
+int DateTime::fillYearString(XMLCh*& ptr, valueIndex ind) const
+{
+    XMLCh strBuffer[16];
+    // let's hope we get no years of 15 digits...
+    XMLString::binToText(fValue[ind], strBuffer, 15, 10);
+    int   actualLen = XMLString::stringLen(strBuffer);
+    // don't forget that years can be negative...
+    int negativeYear = 0;
+    if(strBuffer[0] == chDash)
+    {
+        *ptr++ = strBuffer[0];
+        negativeYear = 1;
+    }
+    int   i;
+    //append leading zeros
+    for (i = 0; i < 4 - actualLen+negativeYear; i++)
+    {
+        *ptr++ = chDigit_0;
+    }
+
+    for (i = negativeYear; i < actualLen; i++)
+    {
+        *ptr++ = strBuffer[i];
+    }
+    if(actualLen > 4)
+        return actualLen-4;
+    return 0;
+}
+
+/***
+ *
+ *   .check if the rawData has the mili second component
+ *   .capture the substring
+ *
+ ***/
+void DateTime::searchMiliSeconds(XMLCh*& miliStartPtr, XMLCh*& miliEndPtr) const
+{
+    miliStartPtr = miliEndPtr = 0;
+
+    int milisec = XMLString::indexOf(fBuffer, MILISECOND_SEPARATOR);
+    if (milisec == -1)
+        return;
+
+    miliStartPtr = fBuffer + milisec + 1;
+    miliEndPtr   = miliStartPtr;
+    while (*miliEndPtr)
+    {
+        if ((*miliEndPtr < chDigit_0) || (*miliEndPtr > chDigit_9))
+            break;
+
+        miliEndPtr++;
+    }
+
+    //remove trailing zeros
+    while( *(miliEndPtr - 1) == chDigit_0)
+        miliEndPtr--;
+
+    return;
+}
+