blob: 34c55577de0b9f39b215dabb7035232368eb4631 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.
*/
/*
* $Id: XMLDateTime.cpp 568078 2007-08-21 11:43:25Z amassari $
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <xercesc/util/XMLDateTime.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/NumberFormatException.hpp>
XERCES_CPP_NAMESPACE_BEGIN
//
// 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}
static const int DATETIMES[][XMLDateTime::TOTAL_SIZE] =
{
{1696, 9, 1, 0, 0, 0, 0, XMLDateTime::UTC_STD},
{1697, 2, 1, 0, 0, 0, 0, XMLDateTime::UTC_STD},
{1903, 3, 1, 0, 0, 0, 0, XMLDateTime::UTC_STD},
{1903, 7, 1, 0, 0, 0, 0, XMLDateTime::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 XMLDateTime::compare(const XMLDateTime* const pDate1
, const XMLDateTime* 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 = INDETERMINATE;
//try and see if the objects are equal
if ( (resultA = compareOrder(pDate1, pDate2)) == EQUAL)
return EQUAL;
//long comparison algorithm is required
XMLDateTime tempA(XMLPlatformUtils::fgMemoryManager), *pTempA = &tempA;
XMLDateTime tempB(XMLPlatformUtils::fgMemoryManager), *pTempB = &tempB;
addDuration(pTempA, pDate1, 0);
addDuration(pTempB, pDate2, 0);
resultA = compareOrder(pTempA, pTempB);
if ( resultA == INDETERMINATE )
return INDETERMINATE;
addDuration(pTempA, pDate1, 1);
addDuration(pTempB, pDate2, 1);
resultB = compareOrder(pTempA, pTempB);
resultA = compareResult(resultA, resultB, strict);
if ( resultA == INDETERMINATE )
return INDETERMINATE;
addDuration(pTempA, pDate1, 2);
addDuration(pTempB, pDate2, 2);
resultB = compareOrder(pTempA, pTempB);
resultA = compareResult(resultA, resultB, strict);
if ( resultA == INDETERMINATE )
return INDETERMINATE;
addDuration(pTempA, pDate1, 3);
addDuration(pTempB, pDate2, 3);
resultB = compareOrder(pTempA, pTempB);
resultA = compareResult(resultA, resultB, strict);
return resultA;
}
//
// Form a new XMLDateTime with duration and baseDate array
// Note: C++ Java
// fNewDate duration
// fDuration date
//
void XMLDateTime::addDuration(XMLDateTime* fNewDate
, const XMLDateTime* 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);
if (fNewDate->fValue[Month] <= 0) {
fNewDate->fValue[Month]+= 12;
carry--;
}
//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);
if (fNewDate->fValue[Second] < 0) {
fNewDate->fValue[Second]+= 60;
carry--;
}
//add minutes
temp = DATETIMES[index][Minute] + fDuration->fValue[Minute] + carry;
carry = fQuotient(temp, 60);
fNewDate->fValue[Minute] = mod(temp, 60, carry);
if (fNewDate->fValue[Minute] < 0) {
fNewDate->fValue[Minute]+= 60;
carry--;
}
//add hours
temp = DATETIMES[index][Hour] + fDuration->fValue[Hour] + carry;
carry = fQuotient(temp, 24);
fNewDate->fValue[Hour] = mod(temp, 24, carry);
if (fNewDate->fValue[Hour] < 0) {
fNewDate->fValue[Hour]+= 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);
if (fNewDate->fValue[Month] <= 0) {
fNewDate->fValue[Month]+= 12;
fNewDate->fValue[CentYear]--;
}
fNewDate->fValue[CentYear] += fQuotient(temp, 1, 13);
}
//fNewDate->fValue[utc] = UTC_STD_CHAR;
fNewDate->fValue[utc] = UTC_STD;
}
int XMLDateTime::compareResult(int resultA
, int resultB
, bool strict)
{
if ( resultB == INDETERMINATE )
{
return INDETERMINATE;
}
else if ( (resultA != resultB) &&
strict )
{
return INDETERMINATE;
}
else if ( (resultA != resultB) &&
!strict )
{
if ( (resultA != EQUAL) &&
(resultB != EQUAL) )
{
return INDETERMINATE;
}
else
{
return (resultA != EQUAL)? resultA : resultB;
}
}
return resultA;
}
// ---------------------------------------------------------------------------
// static methods : for others
// ---------------------------------------------------------------------------
int XMLDateTime::compare(const XMLDateTime* const pDate1
, const XMLDateTime* const pDate2)
{
if (pDate1->fValue[utc] == pDate2->fValue[utc])
{
return XMLDateTime::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 INDETERMINATE;
}
int XMLDateTime::compareResult(const XMLDateTime* const pDate1
, const XMLDateTime* const pDate2
, bool set2Left
, int utc_type)
{
XMLDateTime tmpDate = (set2Left ? *pDate1 : *pDate2);
tmpDate.fTimeZone[hh] = 14;
tmpDate.fTimeZone[mm] = 0;
tmpDate.fValue[utc] = utc_type;
tmpDate.normalize();
return (set2Left? XMLDateTime::compareOrder(&tmpDate, pDate2) :
XMLDateTime::compareOrder(pDate1, &tmpDate));
}
int XMLDateTime::compareOrder(const XMLDateTime* const lValue
, const XMLDateTime* const rValue)
//, MemoryManager* const memMgr)
{
//
// If any of the them is not normalized() yet,
// we need to do something here.
//
XMLDateTime lTemp = *lValue;
XMLDateTime rTemp = *rValue;
lTemp.normalize();
rTemp.normalize();
for ( int i = 0 ; i < TOTAL_SIZE; i++ )
{
if ( lTemp.fValue[i] < rTemp.fValue[i] )
{
return LESS_THAN;
}
else if ( lTemp.fValue[i] > rTemp.fValue[i] )
{
return GREATER_THAN;
}
}
if ( lTemp.fHasTime)
{
if ( lTemp.fMiliSecond < rTemp.fMiliSecond )
{
return LESS_THAN;
}
else if ( lTemp.fMiliSecond > rTemp.fMiliSecond )
{
return GREATER_THAN;
}
}
return EQUAL;
}
// ---------------------------------------------------------------------------
// ctor and dtor
// ---------------------------------------------------------------------------
XMLDateTime::~XMLDateTime()
{
if (fBuffer)
fMemoryManager->deallocate(fBuffer);//delete[] fBuffer;
}
XMLDateTime::XMLDateTime(MemoryManager* const manager)
: fStart(0)
, fEnd(0)
, fBufferMaxLen(0)
, fMiliSecond(0)
, fHasTime(false)
, fBuffer(0)
, fMemoryManager(manager)
{
reset();
}
XMLDateTime::XMLDateTime(const XMLCh* const aString,
MemoryManager* const manager)
: fStart(0)
, fEnd(0)
, fBufferMaxLen(0)
, fMiliSecond(0)
, fHasTime(false)
, fBuffer(0)
, fMemoryManager(manager)
{
setBuffer(aString);
}
// -----------------------------------------------------------------------
// Copy ctor and Assignment operators
// -----------------------------------------------------------------------
XMLDateTime::XMLDateTime(const XMLDateTime &toCopy)
: XMLNumber(toCopy)
, fBufferMaxLen(0)
, fBuffer(0)
, fMemoryManager(toCopy.fMemoryManager)
{
copy(toCopy);
}
XMLDateTime& XMLDateTime::operator=(const XMLDateTime& rhs)
{
if (this == &rhs)
return *this;
copy(rhs);
return *this;
}
// -----------------------------------------------------------------------
// Implementation of Abstract Interface
// -----------------------------------------------------------------------
//
// We may simply return the handle to fBuffer, but
// for the sake of consistency, we return a duplicated copy
// and the caller is responsible for the release of the buffer
// just like any other things in the XMLNumber family.
//
XMLCh* XMLDateTime::toString() const
{
assertBuffer();
// Return data using global operator new
XMLCh* retBuf = XMLString::replicate(fBuffer);
return retBuf;
}
//
// We may simply return the handle to fBuffer
//
XMLCh* XMLDateTime::getRawData() const
{
assertBuffer();
return fBuffer;
}
const XMLCh* XMLDateTime::getFormattedString() const
{
return getRawData();
}
int XMLDateTime::getSign() const
{
return 0;
}
// ---------------------------------------------------------------------------
// Parsers
// ---------------------------------------------------------------------------
//
// [-]{CCYY-MM-DD}'T'{HH:MM:SS.MS}[TimeZone]
//
void XMLDateTime::parseDateTime()
{
initParser();
getDate();
//fStart is supposed to point to 'T'
if (fBuffer[fStart++] != DATETIME_SEPARATOR)
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dt_missingT
, fBuffer
, fMemoryManager);
getTime();
validateDateTime();
normalize();
fHasTime = true;
}
//
// [-]{CCYY-MM-DD}[TimeZone]
//
void XMLDateTime::parseDate()
{
initParser();
getDate();
parseTimeZone();
validateDateTime();
normalize();
}
void XMLDateTime::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 XMLDateTime::parseDay()
{
initParser();
if (fBuffer[0] != DATE_SEPARATOR ||
fBuffer[1] != DATE_SEPARATOR ||
fBuffer[2] != DATE_SEPARATOR )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_gDay_invalid
, fBuffer
, fMemoryManager);
}
//initialize values
fValue[CentYear] = YEAR_DEFAULT;
fValue[Month] = MONTH_DEFAULT;
fValue[Day] = parseInt(fStart+3, fStart+5);
if ( DAY_SIZE < fEnd )
{
int pos = XMLString::indexOf(UTC_SET, fBuffer[DAY_SIZE]);
if (pos == -1 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_gDay_invalid
, fBuffer
, fMemoryManager);
}
else
{
fValue[utc] = pos+1;
getTimeZone(DAY_SIZE);
}
}
validateDateTime();
normalize();
}
//
// {--MM--}[TimeZone]
// {--MM}[TimeZone]
// 012345
//
void XMLDateTime::parseMonth()
{
initParser();
if (fBuffer[0] != DATE_SEPARATOR ||
fBuffer[1] != DATE_SEPARATOR )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_gMth_invalid
, fBuffer
, fMemoryManager);
}
//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 pos = XMLString::indexOf(UTC_SET, fBuffer[fStart]);
if ( pos == NOT_FOUND )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_gMth_invalid
, fBuffer
, fMemoryManager);
}
else
{
fValue[utc] = pos+1;
getTimeZone(fStart);
}
}
validateDateTime();
normalize();
}
//
//[-]{CCYY}[TimeZone]
// 0 1234
//
void XMLDateTime::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 XMLDateTime::parseMonthDay()
{
initParser();
if (fBuffer[0] != DATE_SEPARATOR ||
fBuffer[1] != DATE_SEPARATOR ||
fBuffer[4] != DATE_SEPARATOR )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_gMthDay_invalid
, fBuffer
, fMemoryManager);
}
//initialize
fValue[CentYear] = YEAR_DEFAULT;
fValue[Month] = parseInt(2, 4);
fValue[Day] = parseInt(5, 7);
if ( MONTHDAY_SIZE < fEnd )
{
int pos = XMLString::indexOf(UTC_SET, fBuffer[MONTHDAY_SIZE]);
if ( pos == NOT_FOUND )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_gMthDay_invalid
, fBuffer
, fMemoryManager);
}
else
{
fValue[utc] = pos+1;
getTimeZone(MONTHDAY_SIZE);
}
}
validateDateTime();
normalize();
}
void XMLDateTime::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 XMLDateTime::parseDuration()
{
initParser();
// must start with '-' or 'P'
//
XMLCh c = fBuffer[fStart++];
if ( (c != DURATION_STARTER) &&
(c != chDash) )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_Start_dashP
, fBuffer
, fMemoryManager);
}
// 'P' must ALWAYS be present in either case
if ( (c == chDash) &&
(fBuffer[fStart++]!= DURATION_STARTER ))
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_noP
, fBuffer
, fMemoryManager);
}
// 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)
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_DashNotFirst
, fBuffer
, fMemoryManager);
}
//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
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_inv_b4T
, fBuffer
, fMemoryManager);
}
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 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_inv_seconds
, fBuffer
, fMemoryManager);
}
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 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_NoTimeAfterT
, fBuffer
, fMemoryManager);
}
}
if ( !designator )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_dur_NoElementAtAll
, fBuffer
, fMemoryManager);
}
}
// ---------------------------------------------------------------------------
// 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 XMLDateTime::getDate()
{
// Ensure enough chars in buffer
if ( (fStart+YMD_MIN_SIZE) > fEnd)
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_date_incomplete
, fBuffer
, fMemoryManager);
getYearMonth(); // Scan YearMonth and
// fStart point to the next '-'
if (fBuffer[fStart++] != DATE_SEPARATOR)
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_date_invalid
, fBuffer
, fMemoryManager);
//("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 XMLDateTime::getTime()
{
// Ensure enough chars in buffer
if ( (fStart+TIME_MIN_SIZE) > fEnd)
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_time_incomplete
, fBuffer
, fMemoryManager);
//"Imcomplete Time Format"
// check (fixed) format first
if ((fBuffer[fStart + 2] != TIME_SEPARATOR) ||
(fBuffer[fStart + 5] != TIME_SEPARATOR) )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_time_invalid
, fBuffer
, fMemoryManager);
//("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)
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_ms_noDigit
, fBuffer
, fMemoryManager);
//("ms shall be present once '.' is present" );
}
if ( sign == NOT_FOUND )
{
fMiliSecond = parseMiliSecond(fStart, fEnd); //get ms between '.' and fEnd
fStart = fEnd;
}
else
{
fMiliSecond = parseMiliSecond(fStart, sign); //get ms between UTC sign and fEnd
}
}
else if(sign == 0 || sign != fStart)
{
// seconds has more than 2 digits
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_min_invalid
, fBuffer
, fMemoryManager);
}
//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 XMLDateTime::getYearMonth()
{
// Ensure enough chars in buffer
if ( (fStart+YMONTH_MIN_SIZE) > fEnd)
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_ym_incomplete
, fBuffer
, fMemoryManager);
//"Imcomplete YearMonth Format";
// skip the first leading '-'
int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;
//
// search for year separator '-'
//
int yearSeparator = indexOf(start, fEnd, DATE_SEPARATOR);
if ( yearSeparator == NOT_FOUND)
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_ym_invalid
, fBuffer
, fMemoryManager);
//("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 )
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_ym_noMonth
, fBuffer
, fMemoryManager);
//"no month in buffer"
fValue[Month] = parseInt(fStart, yearSeparator + 3);
fStart += 2; //fStart points right after the MONTH
return;
}
void XMLDateTime::parseTimeZone()
{
//fStart points right after the date
if ( fStart < fEnd ) {
int pos = XMLString::indexOf(UTC_SET, fBuffer[fStart]);
if (pos == NOT_FOUND) {
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_tz_noUTCsign
, fBuffer
, fMemoryManager);
}
else {
fValue[utc] = pos+1;
getTimeZone(fStart);
}
}
return;
}
//
// 'Z'
// ['+'|'-']hh:mm
//
// Note: Assuming fStart points to the beginning of TimeZone section
// fStart updated to meet fEnd
//
void XMLDateTime::getTimeZone(const int sign)
{
if ( fBuffer[sign] == UTC_STD_CHAR )
{
if ((sign + 1) != fEnd )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_tz_stuffAfterZ
, fBuffer
, fMemoryManager);
//"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 ) )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_tz_invalid
, fBuffer
, fMemoryManager);
//("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 XMLDateTime::normalize()
{
if ((fValue[utc] == UTC_UNKNOWN) ||
(fValue[utc] == UTC_STD) )
return;
int negate = (fValue[utc] == UTC_POS)? -1: 1;
int temp;
int carry;
// we normalize a duration so could have 200M...
//update months (may be modified additionaly below)
temp = fValue[Month];
fValue[Month] = modulo(temp, 1, 13);
carry = fQuotient(temp, 1, 13);
if (fValue[Month] <= 0) {
fValue[Month]+= 12;
carry--;
}
//add years (may be modified additionaly below)
fValue[CentYear] += carry;
// add mins
temp = fValue[Minute] + negate * fTimeZone[mm];
carry = fQuotient(temp, 60);
fValue[Minute] = mod(temp, 60, carry);
if (fValue[Minute] < 0) {
fValue[Minute] += 60;
carry--;
}
//add hours
temp = fValue[Hour] + negate * fTimeZone[hh] + carry;
carry = fQuotient(temp, 24);
fValue[Hour] = mod(temp, 24, carry);
if (fValue[Hour] < 0) {
fValue[Hour] += 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);
if (fValue[Month] <=0) {
fValue[Month]+= 12;
fValue[CentYear]--;
}
fValue[CentYear] += fQuotient(temp, 1, 13);
}
// set to normalized
fValue[utc] = UTC_STD;
return;
}
void XMLDateTime::validateDateTime() const
{
//REVISIT: should we throw an exception for not valid dates
// or reporting an error message should be sufficient?
if ( fValue[CentYear] == 0 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_year_zero
, fBuffer
, fMemoryManager);
//"The year \"0000\" is an illegal year value");
}
if ( fValue[Month] < 1 ||
fValue[Month] > 12 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_mth_invalid
, fBuffer
, fMemoryManager);
//"The month must have values 1 to 12");
}
//validate days
if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) ||
fValue[Day] == 0 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_day_invalid
, fBuffer
, fMemoryManager);
//"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))))
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_hour_invalid
, fBuffer
, fMemoryManager);
//("Hour must have values 0-23");
}
//validate minutes
if ( fValue[Minute] < 0 ||
fValue[Minute] > 59 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_min_invalid
, fBuffer
, fMemoryManager);
//"Minute must have values 0-59");
}
//validate seconds
if ( fValue[Second] < 0 ||
fValue[Second] > 60 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_second_invalid
, fBuffer
, fMemoryManager);
//"Second must have values 0-60");
}
//validate time-zone hours
if ( (abs(fTimeZone[hh]) > 14) ||
((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_tz_hh_invalid
, fBuffer
, fMemoryManager);
//"Time zone should have range -14..+14");
}
//validate time-zone minutes
if ( abs(fTimeZone[mm]) > 59 )
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_min_invalid
, fBuffer
, fMemoryManager);
//("Minute must have values 0-59");
}
return;
}
// -----------------------------------------------------------------------
// locator and converter
// -----------------------------------------------------------------------
int XMLDateTime::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 XMLDateTime::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 XMLDateTime::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)
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, fMemoryManager);
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 XMLDateTime::parseMiliSecond(const int start, const int end) const
{
double div = 10;
double retval = 0;
for (int i=start; i < end; i++) {
if (fBuffer[i] < chDigit_0 || fBuffer[i] > chDigit_9)
ThrowXMLwithMemMgr(NumberFormatException, XMLExcepts::XMLNUM_Inv_chars, fMemoryManager);
retval += (fBuffer[i] == chDigit_0) ? 0 : ((double) (fBuffer[i] - chDigit_0)/div);
div *= 10;
}
// 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 XMLDateTime::parseIntYear(const int end) const
{
// skip the first leading '-'
int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;
int length = end - start;
if (length < 4)
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_year_tooShort
, fBuffer
, fMemoryManager);
//"Year must have 'CCYY' format");
}
else if (length > 4 &&
fBuffer[start] == chDigit_0)
{
ThrowXMLwithMemMgr1(SchemaDateTimeException
, XMLExcepts::DateTime_year_leadingZero
, fBuffer
, fMemoryManager);
//"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* XMLDateTime::getDateTimeCanonicalRepresentation(MemoryManager* const memMgr) const
{
XMLCh *miliStartPtr, *miliEndPtr;
searchMiliSeconds(miliStartPtr, miliEndPtr);
int miliSecondsLen = miliEndPtr - miliStartPtr;
int utcSize = (fValue[utc] == UTC_UNKNOWN) ? 0 : 1;
MemoryManager* toUse = memMgr? memMgr : fMemoryManager;
XMLCh* retBuf = (XMLCh*) toUse->allocate( (21 + miliSecondsLen + utcSize + 1) * sizeof(XMLCh));
XMLCh* retPtr = retBuf;
// (-?) cc+yy-mm-dd'T'hh:mm:ss'Z' ('.'s+)?
// 2+ 8 1 8 1
//
int additionalLen = fillYearString(retPtr, fValue[CentYear]);
if(additionalLen != 0)
{
// very bad luck; have to resize the buffer...
XMLCh *tmpBuf = (XMLCh*) toUse->allocate( (additionalLen+21+miliSecondsLen +2) * sizeof(XMLCh));
XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen);
retPtr = tmpBuf+(retPtr-retBuf);
toUse->deallocate(retBuf);
retBuf = tmpBuf;
}
*retPtr++ = DATE_SEPARATOR;
fillString(retPtr, fValue[Month], 2);
*retPtr++ = DATE_SEPARATOR;
fillString(retPtr, fValue[Day], 2);
*retPtr++ = DATETIME_SEPARATOR;
fillString(retPtr, fValue[Hour], 2);
if (fValue[Hour] == 24)
{
*(retPtr - 2) = chDigit_0;
*(retPtr - 1) = chDigit_0;
}
*retPtr++ = TIME_SEPARATOR;
fillString(retPtr, fValue[Minute], 2);
*retPtr++ = TIME_SEPARATOR;
fillString(retPtr, fValue[Second], 2);
if (miliSecondsLen)
{
*retPtr++ = chPeriod;
XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);
retPtr += miliSecondsLen;
}
if (utcSize)
*retPtr++ = UTC_STD_CHAR;
*retPtr = chNull;
return retBuf;
}
/***
* E2-41
*
* 3.2.9.2 Canonical representation
*
* Given a member of the date value space, the date
* portion of the canonical representation (the entire
* representation for nontimezoned values, and all but
* the timezone representation for timezoned values)
* is always the date portion of the dateTime canonical
* representation of the interval midpoint (the
* dateTime representation, truncated on the right
* to eliminate 'T' and all following characters).
* For timezoned values, append the canonical
* representation of the recoverable timezone.
*
***/
XMLCh* XMLDateTime::getDateCanonicalRepresentation(MemoryManager* const memMgr) const
{
/*
* Case Date Actual Value Canonical Value
* 1 yyyy-mm-dd yyyy-mm-dd yyyy-mm-dd
* 2 yyyy-mm-ddZ yyyy-mm-ddT00:00Z yyyy-mm-ddZ
* 3 yyyy-mm-dd+00:00 yyyy-mm-ddT00:00Z yyyy-mm-ddZ
* 4 yyyy-mm-dd+00:01 YYYY-MM-DCT23:59Z yyyy-mm-dd+00:01
* 5 yyyy-mm-dd+12:00 YYYY-MM-DCT12:00Z yyyy-mm-dd+12:00
* 6 yyyy-mm-dd+12:01 YYYY-MM-DCT11:59Z YYYY-MM-DC-11:59
* 7 yyyy-mm-dd+14:00 YYYY-MM-DCT10:00Z YYYY-MM-DC-10:00
* 8 yyyy-mm-dd-00:00 yyyy-mm-ddT00:00Z yyyy-mm-ddZ
* 9 yyyy-mm-dd-00:01 yyyy-mm-ddT00:01Z yyyy-mm-dd-00:01
* 11 yyyy-mm-dd-11:59 yyyy-mm-ddT11:59Z YYYY-MM-DD-11:59
* 10 yyyy-mm-dd-12:00 yyyy-mm-ddT12:00Z YYYY-MM-DD+12:00
* 12 yyyy-mm-dd-14:00 yyyy-mm-ddT14:00Z YYYY-MM-DD+10:00
*/
int utcSize = (fValue[utc] == UTC_UNKNOWN) ? 0 : 1;
// YYYY-MM-DD + chNull
// 1234567890 + 1
int memLength = 10 + 1 + utcSize;
if (fTimeZone[hh] != 0 || fTimeZone[mm] != 0) {
// YYYY-MM-DD+HH:MM (utcSize will be 1 so drop that)
// 1234567890123456
memLength += 5; // 6 - 1 for utcSize
}
MemoryManager* toUse = memMgr? memMgr : fMemoryManager;
XMLCh* retBuf = (XMLCh*) toUse->allocate( (memLength) * sizeof(XMLCh));
XMLCh* retPtr = retBuf;
if (fValue[Hour] < 12) {
int additionalLen = fillYearString(retPtr, fValue[CentYear]);
if (additionalLen != 0) {
// very bad luck; have to resize the buffer...
XMLCh *tmpBuf = (XMLCh*) toUse->allocate( (additionalLen + memLength ) * sizeof(XMLCh));
XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen);
retPtr = tmpBuf+(retPtr-retBuf);
toUse->deallocate(retBuf);
retBuf = tmpBuf;
}
*retPtr++ = DATE_SEPARATOR;
fillString(retPtr, fValue[Month], 2);
*retPtr++ = DATE_SEPARATOR;
fillString(retPtr, fValue[Day], 2);
if (utcSize) {
if (fTimeZone[hh] != 0 || fTimeZone[mm] != 0) {
*retPtr++ = UTC_NEG_CHAR;
fillString(retPtr, fValue[Hour], 2);
*retPtr++ = TIME_SEPARATOR;
fillString(retPtr, fValue[Minute], 2);
}
else {
*retPtr++ = UTC_STD_CHAR;
}
}
*retPtr = chNull;
}
else {
/*
* Need to reconvert things to get a recoverable time zone between
* +12:00 and -11:59
*/
int carry;
int minute;
int hour;
int day;
int month;
int year;
if (fValue[Minute] == 0) {
minute = 0;
carry = 0;
}
else {
minute = 60 - fValue[Minute];
carry = 1;
}
hour = 24 - fValue[Hour] - carry;
day = fValue[Day] + 1;
month = fValue[Month];
year = fValue[CentYear];
while (1) {
int temp = maxDayInMonthFor(year, month);
if (day < 1) {
day += maxDayInMonthFor(year, month - 1);
carry = -1;
}
else if (day > temp) {
day -= temp;
carry = 1;
}
else {
break;
}
temp = month + carry;
month = modulo(temp, 1, 13);
if (month <= 0) {
month+= 12;
year--;
}
year += fQuotient(temp, 1, 13);
}
int additionalLen = fillYearString(retPtr, year);
if (additionalLen != 0) {
// very bad luck; have to resize the buffer...
XMLCh *tmpBuf = (XMLCh*) toUse->allocate( (additionalLen + memLength ) * sizeof(XMLCh));
XMLString::moveChars(tmpBuf, retBuf, 4+additionalLen);
retPtr = tmpBuf+(retPtr-retBuf);
toUse->deallocate(retBuf);
retBuf = tmpBuf;
}
*retPtr++ = DATE_SEPARATOR;
fillString(retPtr, month, 2);
*retPtr++ = DATE_SEPARATOR;
fillString(retPtr, day, 2);
*retPtr++ = UTC_POS_CHAR;
fillString(retPtr, hour, 2);
*retPtr++ = TIME_SEPARATOR;
fillString(retPtr, minute, 2);
*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* XMLDateTime::getTimeCanonicalRepresentation(MemoryManager* const memMgr) const
{
XMLCh *miliStartPtr, *miliEndPtr;
searchMiliSeconds(miliStartPtr, miliEndPtr);
int miliSecondsLen = miliEndPtr - miliStartPtr;
int utcSize = (fValue[utc] == UTC_UNKNOWN) ? 0 : 1;
MemoryManager* toUse = memMgr? memMgr : fMemoryManager;
XMLCh* retBuf = (XMLCh*) toUse->allocate( (10 + miliSecondsLen + utcSize + 1) * sizeof(XMLCh));
XMLCh* retPtr = retBuf;
// 'hh:mm:ss'Z' ('.'s+)?
// 8 1
//
fillString(retPtr, fValue[Hour], 2);
if (fValue[Hour] == 24)
{
*(retPtr - 2) = chDigit_0;
*(retPtr - 1) = chDigit_0;
}
*retPtr++ = TIME_SEPARATOR;
fillString(retPtr, fValue[Minute], 2);
*retPtr++ = TIME_SEPARATOR;
fillString(retPtr, fValue[Second], 2);
if (miliSecondsLen)
{
*retPtr++ = chPeriod;
XMLString::copyNString(retPtr, miliStartPtr, miliSecondsLen);
retPtr += miliSecondsLen;
}
if (utcSize)
*retPtr++ = UTC_STD_CHAR;
*retPtr = chNull;
return retBuf;
}
void XMLDateTime::fillString(XMLCh*& ptr, int value, int expLen) const
{
XMLCh strBuffer[16];
assert(expLen < 16);
XMLString::binToText(value, strBuffer, expLen, 10, fMemoryManager);
int actualLen = XMLString::stringLen(strBuffer);
int i;
//append leading zeros
for (i = 0; i < expLen - actualLen; i++)
{
*ptr++ = chDigit_0;
}
for (i = 0; i < actualLen; i++)
{
*ptr++ = strBuffer[i];
}
}
int XMLDateTime::fillYearString(XMLCh*& ptr, int value) const
{
XMLCh strBuffer[16];
// let's hope we get no years of 15 digits...
XMLString::binToText(value, strBuffer, 15, 10, fMemoryManager);
int actualLen = XMLString::stringLen(strBuffer);
// don't forget that years can be negative...
int negativeYear = 0;
if(strBuffer[0] == chDash)
{
*ptr++ = strBuffer[0];
negativeYear = 1;
}
int i;
//append leading zeros
for (i = 0; i < 4 - actualLen+negativeYear; i++)
{
*ptr++ = chDigit_0;
}
for (i = negativeYear; i < actualLen; i++)
{
*ptr++ = strBuffer[i];
}
if(actualLen > 4)
return actualLen-4;
return 0;
}
/***
*
* .check if the rawData has the mili second component
* .capture the substring
*
***/
void XMLDateTime::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;
}
/***
* Support for Serialization/De-serialization
***/
IMPL_XSERIALIZABLE_TOCREATE(XMLDateTime)
void XMLDateTime::serialize(XSerializeEngine& serEng)
{
//REVISIT: may not need to call base since it does nothing
XMLNumber::serialize(serEng);
int i = 0;
if (serEng.isStoring())
{
for (i = 0; i < TOTAL_SIZE; i++)
{
serEng<<fValue[i];
}
for (i = 0; i < TIMEZONE_ARRAYSIZE; i++)
{
serEng<<fTimeZone[i];
}
serEng<<fStart;
serEng<<fEnd;
serEng.writeString(fBuffer, fBufferMaxLen, XSerializeEngine::toWriteBufferLen);
}
else
{
for (i = 0; i < TOTAL_SIZE; i++)
{
serEng>>fValue[i];
}
for (i = 0; i < TIMEZONE_ARRAYSIZE; i++)
{
serEng>>fTimeZone[i];
}
serEng>>fStart;
serEng>>fEnd;
int dataLen = 0;
serEng.readString(fBuffer, fBufferMaxLen, dataLen ,XSerializeEngine::toReadBufferLen);
}
}
XERCES_CPP_NAMESPACE_END