Skip to content
Snippets Groups Projects
XMLDateTime.cpp 38.4 KiB
Newer Older
PeiYong Zhang's avatar
PeiYong Zhang committed
void XMLDateTime::getTime()
{

    // Ensure enough chars in buffer
    if ( (fStart+TIME_MIN_SIZE) > fEnd)
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_time_incomplete
                , fBuffer);
        //"Imcomplete Time Format"

    // check (fixed) format first
    if ((fBuffer[fStart + 2] != TIME_SEPARATOR) ||
        (fBuffer[fStart + 5] != TIME_SEPARATOR)  )
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_time_invalid
                , fBuffer);
        //("Error in parsing time" );
    }

    //
    // get hours, minute and second
    //
    fValue[Hour]   = parseInt(fStart + 0, fStart + 2);
Tinny Ng's avatar
Tinny Ng committed
    fValue[Minute] = parseInt(fStart + 3, fStart + 5);
PeiYong Zhang's avatar
PeiYong Zhang committed
    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);

Tinny Ng's avatar
Tinny Ng committed
    //parse miliseconds
PeiYong Zhang's avatar
PeiYong Zhang committed
    int milisec = (fBuffer[fStart] == MILISECOND_SEPARATOR)? fStart : NOT_FOUND;
    if ( milisec != NOT_FOUND )
    {
        fStart++;   // skip the '.'
        // make sure we have some thing between the '.' and fEnd
        if (fStart >= fEnd)
        {
            ThrowXML1(SchemaDateTimeException
                    , XMLExcepts::DateTime_ms_noDigit
                    , fBuffer);
            //("ms shall be present once '.' is present" );
        }

Tinny Ng's avatar
Tinny Ng committed
        if ( sign == NOT_FOUND )
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            fValue[MiliSecond] = parseInt(fStart, fEnd);  //get ms between '.' and fEnd
            fStart = fEnd;
        }
Tinny Ng's avatar
Tinny Ng committed
        else
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            fValue[MiliSecond] = parseInt(fStart, sign);  //get ms between UTC sign and fEnd
        }
	}
    else if(sign == 0 || sign != fStart)
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        // seconds has more than 2 digits
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_min_invalid
                , fBuffer);
    }
PeiYong Zhang's avatar
PeiYong Zhang committed

Tinny Ng's avatar
Tinny Ng committed
    //parse UTC time zone (hh:mm)
PeiYong Zhang's avatar
PeiYong Zhang committed
    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)
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_ym_incomplete
                , fBuffer);
        //"Imcomplete YearMonth Format";

    // skip the first leading '-'
    int start = ( fBuffer[0] == chDash ) ? fStart + 1 : fStart;

    //
    // search for year separator '-'
    //
    int yearSeparator = indexOf(start, fEnd, DATE_SEPARATOR);
Tinny Ng's avatar
Tinny Ng committed
    if ( yearSeparator == NOT_FOUND)
PeiYong Zhang's avatar
PeiYong Zhang committed
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_ym_invalid
                , fBuffer);
        //("Year separator is missing or misplaced");

    fValue[CentYear] = parseIntYear(yearSeparator);
    fStart = yearSeparator + 1;  //skip the '-' and point to the first M

    //
    //gonna check we have enough byte for month
    //
    if ((fStart + 2) > fEnd )
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_ym_noMonth
                , fBuffer);
        //"no month in buffer"

    fValue[Month] = parseInt(fStart, yearSeparator + 3);
    fStart += 2;  //fStart points right after the MONTH

    return;
}

void XMLDateTime::parseTimeZone()
{
Tinny Ng's avatar
Tinny Ng committed
    if ( fStart < fEnd )
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        int sign = findUTCSign(fStart);
Tinny Ng's avatar
Tinny Ng committed
        if ( sign < 0 )
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            ThrowXML1(SchemaDateTimeException
                    , XMLExcepts::DateTime_tz_noUTCsign
                    , fBuffer);
            //("Error in month parsing");
        }
Tinny Ng's avatar
Tinny Ng committed
        else
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            getTimeZone(sign);
        }
    }

    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 )
        {
            ThrowXML1(SchemaDateTimeException
                    , XMLExcepts::DateTime_tz_stuffAfterZ
                    , fBuffer);
            //"Error in parsing time zone");
        }		

        return;	
    }

    //
    // otherwise, it has to be this format
    // '[+|-]'hh:mm
    //    1   23456 7
    //   sign      fEnd
    //
    if ( ( ( sign + TIMEZONE_SIZE + 1) != fEnd )      ||
         ( fBuffer[sign + 3] != TIMEZONE_SEPARATOR ) )
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_tz_invalid
                , fBuffer);
        //("Error in parsing time zone");
    }

    fTimeZone[hh] = parseInt(sign+1, sign+3);		
    fTimeZone[mm] = parseInt(sign+4, fEnd);
        		
    return;
}

// ---------------------------------------------------------------------------
//  Validator and normalizer
// ---------------------------------------------------------------------------

/**
 * If timezone present - normalize dateTime  [E Adding durations to dateTimes]
Tinny Ng's avatar
Tinny Ng committed
 *
PeiYong Zhang's avatar
PeiYong Zhang committed
 * @param date   CCYY-MM-DDThh:mm:ss+03
 * @return CCYY-MM-DDThh:mm:ssZ
 */
void XMLDateTime::normalize()
Tinny Ng's avatar
Tinny Ng committed
{
PeiYong Zhang's avatar
PeiYong Zhang committed

    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);
Tinny Ng's avatar
Tinny Ng committed

PeiYong Zhang's avatar
PeiYong Zhang committed
    //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]);
Tinny Ng's avatar
Tinny Ng committed
        if (fValue[Day] < 1)
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            fValue[Day] += maxDayInMonthFor(fValue[CentYear], fValue[Month] - 1);
            carry = -1;
        }
Tinny Ng's avatar
Tinny Ng committed
        else if ( fValue[Day] > temp )
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            fValue[Day] -= temp;
            carry = 1;
        }
Tinny Ng's avatar
Tinny Ng committed
        else
PeiYong Zhang's avatar
PeiYong Zhang committed
        {
            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 XMLDateTime::validateDateTime() const
{

    //REVISIT: should we throw an exception for not valid dates
Tinny Ng's avatar
Tinny Ng committed
    //          or reporting an error message should be sufficient?
    if ( fValue[CentYear] == 0 )
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_year_zero
                , fBuffer);
        //"The year \"0000\" is an illegal year value");
    }

Tinny Ng's avatar
Tinny Ng committed
    if ( fValue[Month] < 1  ||
         fValue[Month] > 12  )
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_mth_invalid
                , fBuffer);
		//"The month must have values 1 to 12");
    }

    //validate days
    if ( fValue[Day] > maxDayInMonthFor( fValue[CentYear], fValue[Month]) ||
Tinny Ng's avatar
Tinny Ng committed
         fValue[Day] == 0 )
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_day_invalid
                , fBuffer);
        //"The day must have values 1 to 31");
    }

    //validate hours
Tinny Ng's avatar
Tinny Ng committed
    if ((fValue[Hour] < 0)  ||
        (fValue[Hour] > 24) ||
Tinny Ng's avatar
Tinny Ng committed
        ((fValue[Hour] == 24) && ((fValue[Minute] !=0) ||
PeiYong Zhang's avatar
PeiYong Zhang committed
                                  (fValue[Second] !=0) ||
Tinny Ng's avatar
Tinny Ng committed
                                  (fValue[MiliSecond] !=0))))
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_hour_invalid
                , fBuffer);
        //("Hour must have values 0-23");
    }

    //validate minutes
    if ( fValue[Minute] < 0 ||
         fValue[Minute] > 59 )
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_min_invalid
                , fBuffer);
        //"Minute must have values 0-59");
    }

    //validate seconds
    if ( fValue[Second] < 0 ||
         fValue[Second] > 60 )
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_second_invalid
                , fBuffer);
        //"Second must have values 0-60");
    }

    //validate time-zone hours
    if ( (abs(fTimeZone[hh]) > 14) ||
         ((abs(fTimeZone[hh]) == 14) && (fTimeZone[mm] != 0)) )
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_tz_hh_invalid
                , fBuffer);
        //"Time zone should have range -14..+14");
    }

    //validate time-zone minutes
    if ( abs(fTimeZone[mm]) > 59 )
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_min_invalid
                , fBuffer);
        //("Minute must have values 0-59");
    }
	
    return;
}

// -----------------------------------------------------------------------
// locator and converter
// -----------------------------------------------------------------------
int XMLDateTime::indexOf(const int start, const int end, const XMLCh ch) const
{
Tinny Ng's avatar
Tinny Ng committed
    for ( int i = start; i < end; i++ )
        if ( fBuffer[i] == ch )
PeiYong Zhang's avatar
PeiYong Zhang committed
            return i;

    return NOT_FOUND;
}

int XMLDateTime::findUTCSign (const int start)
{
    int  pos;
Tinny Ng's avatar
Tinny Ng committed
    for ( int index = start; index < fEnd; index++ )
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        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
{

    XMLCh* strToScan = (XMLCh*) fMemoryManager->allocate
    (
        (end - start + 1) * sizeof(XMLCh)
    );//new XMLCh[end - start + 1];
    ArrayJanitor<XMLCh>  jname(strToScan, fMemoryManager);
PeiYong Zhang's avatar
PeiYong Zhang committed
    XMLString::subString(strToScan, fBuffer, start, end);

    unsigned int retVal;
    XMLString::textToBin(strToScan, retVal);

    return (int) retVal;
}

//
// [-]CCYY
Tinny Ng's avatar
Tinny Ng committed
//
PeiYong Zhang's avatar
PeiYong Zhang committed
// 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;
Tinny Ng's avatar
Tinny Ng committed
    if (length < 4)
PeiYong Zhang's avatar
PeiYong Zhang committed
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_year_tooShort
                , fBuffer);
        //"Year must have 'CCYY' format");
    }
Tinny Ng's avatar
Tinny Ng committed
    else if (length > 4 &&
PeiYong Zhang's avatar
PeiYong Zhang committed
             fBuffer[start] == chDigit_0)
    {
        ThrowXML1(SchemaDateTimeException
                , XMLExcepts::DateTime_year_leadingZero
                , fBuffer);
Tinny Ng's avatar
Tinny Ng committed
        //"Leading zeros are required if the year value would otherwise have fewer than four digits;
PeiYong Zhang's avatar
PeiYong Zhang committed
        // otherwise they are forbidden");
    }

    bool negative = (fBuffer[0] == chDash);
    int  yearVal = parseInt((negative ? 1 : 0), end);
    return ( negative ? (-1) * yearVal : yearVal );
}

Tinny Ng's avatar
Tinny Ng committed
XERCES_CPP_NAMESPACE_END