+/*\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