-/*\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
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you 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 <sstream>
+#include <assert.h>
+#include <xercesc/util/Janitor.hpp>
+
+using namespace xmltooling;
+using namespace xercesc;
+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, bool duration)
+: fStart(0)
+, fEnd(0)
+, fBufferMaxLen(0)
+, fBuffer(0)
+, fMiliSecond(0)
+, fHasTime(false)
+{
+ if (duration) {
+ ostringstream s;
+ if (epoch < 0) {
+ s << "-";
+ epoch = -epoch;
+ }
+ time_t days = epoch / 86400;
+ epoch %= 86400;
+ time_t hours = epoch / 3600;
+ epoch %= 3600;
+ time_t minutes = epoch / 60;
+ epoch %= 60;
+ s << "P" << days << "DT" << hours << "H" << minutes << "M" << epoch << "S";
+ auto_ptr_XMLCh timeptr(s.str().c_str());
+ setBuffer(timeptr.get());
+ }
+ else {
+#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 fValue[utc];
+}
+
+time_t DateTime::getEpoch(bool duration) const
+{
+ if (duration) {
+ time_t epoch = getSecond() + (60 * getMinute()) + (3600 * getHour()) + (86400 * getDay());
+ if (getMonth())
+ epoch += (((365 * 4) + 1)/48 * 86400);
+ if (getYear())
+ epoch += 365.25 * 86400;
+ return getSign()!=UTC_NEG ? epoch : -epoch;
+ }
+ else {
+ 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)
+ throw XMLParserException("Invalid separator between date and time.");
+
+ 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 )
+ {
+ throw XMLParserException("Invalid character in date.");
+ }
+
+ //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 )
+ {
+ throw XMLParserException("Invalid character in date.");
+ }
+ else
+ {
+ getTimeZone(sign);
+ }
+ }
+
+ validateDateTime();
+ normalize();
+}
+
+//
+// {--MM--}[TimeZone]
+// {--MM}[TimeZone]
+// 012345
+//
+void DateTime::parseMonth()
+{
+ initParser();
+
+ if (fBuffer[0] != DATE_SEPARATOR ||
+ fBuffer[1] != DATE_SEPARATOR )
+ {
+ throw XMLParserException("Invalid character in date.");
+ }
+
+ //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 )
+ {
+ throw XMLParserException("Invalid character in date.");
+ }
+ 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 )
+ {
+ throw XMLParserException("Invalid character in date.");
+ }
+
+
+ //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 )
+ {
+ throw XMLParserException("Invalid character in date.");
+ }
+ 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) )
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+
+ // 'P' must ALWAYS be present in either case
+ if ( (c == chDash) &&
+ (fBuffer[fStart++]!= DURATION_STARTER ))
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+
+ // 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)
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+
+ //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
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+
+ 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 )
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+
+ 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 )
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+ }
+
+ if ( !designator )
+ {
+ throw XMLParserException("Invalid character in time.");
+ }
+
+}
+
+// ---------------------------------------------------------------------------
+// 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)
+ throw XMLParserException("Date/time string not complete.");
+
+ getYearMonth(); // Scan YearMonth and
+ // fStart point to the next '-'
+
+ if (fBuffer[fStart++] != DATE_SEPARATOR)
+ {
+ throw XMLParserException("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)
+ throw XMLParserException("Incomplete Time Format.");
+
+ // check (fixed) format first
+ if ((fBuffer[fStart + 2] != TIME_SEPARATOR) ||
+ (fBuffer[fStart + 5] != TIME_SEPARATOR) )
+ {
+ throw XMLParserException("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)
+ {
+ throw XMLParserException("ms should 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)
+ {
+ throw XMLParserException("Seconds has more than 2 digits.");
+ }
+
+ //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)
+ throw XMLParserException("Incomplete 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)
+ throw XMLParserException("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 )
+ throw XMLParserException("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 )
+ {
+ throw XMLParserException("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 )
+ {
+ throw XMLParserException("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 ) )
+ {
+ throw XMLParserException("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 )
+ {
+ throw XMLParserException("The year \"0000\" is an illegal year value");
+ }
+
+ if ( fValue[Month] < 1 ||
+ fValue[Month] > 12 )
+ {
+ throw XMLParserException("The month must have values 1 to 12");
+ }
+
+ //validate days
+ if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) ||
+ fValue[Day] == 0 )
+ {
+ throw XMLParserException("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))))
+ {
+ throw XMLParserException("Hour must have values 0-23");
+ }
+
+ //validate minutes
+ if ( fValue[Minute] < 0 ||
+ fValue[Minute] > 59 )
+ {
+ throw XMLParserException("Minute must have values 0-59");
+ }
+
+ //validate seconds
+ if ( fValue[Second] < 0 ||
+ fValue[Second] > 60 )
+ {
+ throw XMLParserException("Second must have values 0-60");
+ }
+
+ //validate time-zone hours
+ if ( (abs(fTimeZone[hh]) > 14) ||
+ ((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) )
+ {
+ throw XMLParserException("Time zone should have range -14..+14");
+ }
+
+ //validate time-zone minutes
+ if ( abs(fTimeZone[mm]) > 59 )
+ {
+ throw XMLParserException("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)
+ throw XMLParserException("Invalid non-numeric characters.");
+
+ 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)
+ throw XMLParserException("Invalid non-numeric characters.");
+
+ // 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)
+ {
+ throw XMLParserException("Year must have 'CCYY' format");
+ }
+ else if (length > 4 &&
+ fBuffer[start] == chDigit_0)
+ {
+ throw XMLParserException("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 = (int) 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 = (int) 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;
+}
+
+void DateTime::setBuffer(const XMLCh* const aString)
+{
+ reset();
+ fEnd = (int) xercesc::XMLString::stringLen(aString);
+ if (fEnd > 0) {
+ if (fEnd > fBufferMaxLen) {
+ delete[] fBuffer;
+ fBufferMaxLen = fEnd + 8;
+ fBuffer = new XMLCh[fBufferMaxLen+1];
+ }
+ memcpy(fBuffer, aString, (fEnd+1) * sizeof(XMLCh));
+ }
+}
+
+void DateTime::reset()
+{
+ for ( int i=0; i < xercesc::XMLDateTime::TOTAL_SIZE; i++ )
+ fValue[i] = 0;
+
+ fMiliSecond = 0;
+ fHasTime = false;
+ fTimeZone[hh] = fTimeZone[mm] = 0;
+ fStart = fEnd = 0;
+
+ if (fBuffer)
+ *fBuffer = 0;
+}
+
+void DateTime::copy(const DateTime& rhs)
+{
+ for ( int i = 0; i < xercesc::XMLDateTime::TOTAL_SIZE; i++ )
+ fValue[i] = rhs.fValue[i];
+
+ fMiliSecond = rhs.fMiliSecond;
+ fHasTime = rhs.fHasTime;
+ fTimeZone[hh] = rhs.fTimeZone[hh];
+ fTimeZone[mm] = rhs.fTimeZone[mm];
+ fStart = rhs.fStart;
+ fEnd = rhs.fEnd;
+
+ if (fEnd > 0) {
+ if (fEnd > fBufferMaxLen) {
+ delete[] fBuffer;
+ fBufferMaxLen = rhs.fBufferMaxLen;
+ fBuffer = new XMLCh[fBufferMaxLen+1];
+ }
+ memcpy(fBuffer, rhs.fBuffer, (fEnd+1) * sizeof(XMLCh));
+ }
+}
+
+void DateTime::initParser()
+{
+ fStart = 0; // to ensure scan from the very first beginning
+ // in case the pointer is updated accidentally by someone else.
+}
+
+bool DateTime::isNormalized() const
+{
+ return (fValue[xercesc::XMLDateTime::utc] == xercesc::XMLDateTime::UTC_STD ? true : false);
+}
+
+int DateTime::getRetVal(int c1, int c2)
+{
+ if ((c1 == xercesc::XMLDateTime::LESS_THAN && c2 == xercesc::XMLDateTime::GREATER_THAN) ||
+ (c1 == xercesc::XMLDateTime::GREATER_THAN && c2 == xercesc::XMLDateTime::LESS_THAN))
+ return xercesc::XMLDateTime::INDETERMINATE;
+
+ return (c1 != xercesc::XMLDateTime::INDETERMINATE) ? c1 : c2;
+}