From 53b781a8ad5387f789b59d2b49978668c291d96c Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Fri, 21 Nov 2008 05:15:54 +0000 Subject: [PATCH] https://issues.shibboleth.net/jira/browse/CPPXT-18 --- xmltooling/AbstractXMLObject.cpp | 2 +- xmltooling/base.h | 2 +- xmltooling/util/DateTime.cpp | 137 ++++++++++++++++++++++++--------------- xmltooling/util/DateTime.h | 70 ++++++++++---------- xmltoolingtest/DateTimeTest.h | 12 ++-- 5 files changed, 128 insertions(+), 95 deletions(-) diff --git a/xmltooling/AbstractXMLObject.cpp b/xmltooling/AbstractXMLObject.cpp index a8461a4..0f49b55 100644 --- a/xmltooling/AbstractXMLObject.cpp +++ b/xmltooling/AbstractXMLObject.cpp @@ -132,7 +132,7 @@ DateTime* AbstractXMLObject::prepareForAssignment(DateTime* oldValue, time_t new { delete oldValue; releaseThisandParentDOM(); - DateTime* ret = new DateTime(newValue); + DateTime* ret = new DateTime(newValue, duration); if (duration) ret->parseDuration(); else diff --git a/xmltooling/base.h b/xmltooling/base.h index 0ffb39c..85f2f54 100644 --- a/xmltooling/base.h +++ b/xmltooling/base.h @@ -633,7 +633,7 @@ void set##proper(const DateTime* proper) { \ m_##proper = prepareForAssignment(m_##proper,proper); \ if (m_##proper) \ - m_##proper##Epoch=m_##proper->getEpoch(); \ + m_##proper##Epoch=m_##proper->getEpoch(duration); \ } \ void set##proper(time_t proper) { \ m_##proper = prepareForAssignment(m_##proper,proper,duration); \ diff --git a/xmltooling/util/DateTime.cpp b/xmltooling/util/DateTime.cpp index 8e07608..e5cb42c 100644 --- a/xmltooling/util/DateTime.cpp +++ b/xmltooling/util/DateTime.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 Internet2 - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,8 +16,8 @@ /** * DateTime.cpp - * - * Manipulation of XML date/time data. + * + * Manipulation of XML date/time data. */ /* @@ -34,6 +34,7 @@ #endif #include +#include #include #include @@ -245,7 +246,7 @@ void DateTime::addDuration(DateTime* fNewDate temp = DATETIMES[index][Second] + fDuration->fValue[Second]; carry = fQuotient (temp, 60); fNewDate->fValue[Second] = mod(temp, 60, carry); - + //add minutes temp = DATETIMES[index][Minute] + fDuration->fValue[Minute] + carry; carry = fQuotient(temp, 60); @@ -255,7 +256,7 @@ void DateTime::addDuration(DateTime* fNewDate temp = DATETIMES[index][Hour] + fDuration->fValue[Hour] + carry; carry = fQuotient(temp, 24); fNewDate->fValue[Hour] = mod(temp, 24, carry); - + fNewDate->fValue[Day] = DATETIMES[index][Day] + fDuration->fValue[Day] + carry; @@ -316,7 +317,7 @@ int DateTime::compareResult(int resultA } return resultA; - + } // --------------------------------------------------------------------------- @@ -346,7 +347,7 @@ int DateTime::compare(const DateTime* const pDate1 return getRetVal(c1, c2); } - return XMLDateTime::INDETERMINATE; + return XMLDateTime::INDETERMINATE; } int DateTime::compareResult(const DateTime* const pDate1 @@ -436,7 +437,7 @@ DateTime::DateTime(const XMLCh* const aString) setBuffer(aString); } -DateTime::DateTime(time_t epoch) +DateTime::DateTime(time_t epoch, bool duration) : fStart(0) , fEnd(0) , fBufferMaxLen(0) @@ -444,16 +445,34 @@ DateTime::DateTime(time_t epoch) , fMiliSecond(0) , fHasTime(false) { + if (duration) { + ostringstream s; + if (epoch < 0) { + s << "-"; + epoch = -epoch; + } + time_t days = epoch / 86400; + epoch %= 86400; + time_t hours = epoch / 3600; + epoch %= 3600; + time_t minutes = epoch / 60; + epoch %= 60; + s << "P" << days << "DT" << hours << "H" << minutes << "M" << epoch << "S"; + auto_ptr_XMLCh timeptr(s.str().c_str()); + setBuffer(timeptr.get()); + } + else { #ifndef HAVE_GMTIME_R - struct tm* ptime=gmtime(&epoch); + struct tm* ptime=gmtime(&epoch); #else - struct tm res; - struct tm* ptime=gmtime_r(&epoch,&res); + struct tm res; + struct tm* ptime=gmtime_r(&epoch,&res); #endif - char timebuf[32]; - strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime); - auto_ptr_XMLCh timeptr(timebuf); - setBuffer(timeptr.get()); + char timebuf[32]; + strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime); + auto_ptr_XMLCh timeptr(timebuf); + setBuffer(timeptr.get()); + } } // ----------------------------------------------------------------------- @@ -497,25 +516,35 @@ const XMLCh* DateTime::getFormattedString() const int DateTime::getSign() const { - return 0; + return fValue[utc]; } -time_t DateTime::getEpoch() const +time_t DateTime::getEpoch(bool duration) const { - struct tm t; - t.tm_sec=getSecond(); - t.tm_min=getMinute(); - t.tm_hour=getHour(); - t.tm_mday=getDay(); - t.tm_mon=getMonth()-1; - t.tm_year=getYear()-1900; - t.tm_isdst=0; + if (duration) { + time_t epoch = getSecond() + (60 * getMinute()) + (3600 * getHour()) + (86400 * getDay()); + if (getMonth()) + epoch += (((365 * 4) + 1)/48 * 86400); + if (getYear()) + epoch += 365.25 * 86400; + return getSign()!=UTC_NEG ? epoch : -epoch; + } + else { + struct tm t; + t.tm_sec=getSecond(); + t.tm_min=getMinute(); + t.tm_hour=getHour(); + t.tm_mday=getDay(); + t.tm_mon=getMonth()-1; + t.tm_year=getYear()-1900; + t.tm_isdst=0; #if defined(HAVE_TIMEGM) - return timegm(&t); + return timegm(&t); #else - // Windows, and hopefully most others...? - return mktime(&t) - timezone; + // Windows, and hopefully most others...? + return mktime(&t) - timezone; #endif + } } // --------------------------------------------------------------------------- @@ -625,14 +654,14 @@ void DateTime::parseMonth() 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. + // 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; - } + if ( fEnd >= fStart+2 && fBuffer[fStart] == DATE_SEPARATOR && fBuffer[fStart+1] == DATE_SEPARATOR ) + { + fStart += 2; + } // // parse TimeZone if any @@ -702,7 +731,7 @@ void DateTime::parseMonthDay() //initialize fValue[CentYear] = YEAR_DEFAULT; - fValue[Month] = parseInt(2, 4); + fValue[Month] = parseInt(2, 4); fValue[Day] = parseInt(5, 7); if ( MONTHDAY_SIZE < fEnd ) @@ -855,9 +884,9 @@ void DateTime::parseDuration() /*** * 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]+)? + * 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 ) { @@ -1072,9 +1101,9 @@ void DateTime::getTimeZone(const int sign) if ((sign + 1) != fEnd ) { throw XMLParserException("Error in parsing time zone."); - } + } - return; + return; } // @@ -1089,9 +1118,9 @@ void DateTime::getTimeZone(const int sign) throw XMLParserException("Error in parsing time zone."); } - fTimeZone[hh] = parseInt(sign+1, sign+3); + fTimeZone[hh] = parseInt(sign+1, sign+3); fTimeZone[mm] = parseInt(sign+4, fEnd); - + return; } @@ -1214,7 +1243,7 @@ void DateTime::validateDateTime() const { throw XMLParserException("Minute must have values 0-59"); } - + return; } @@ -1332,15 +1361,15 @@ int DateTime::parseIntYear(const int end) const * 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. + * + * 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.) + * represented by appending 'Z' to the nontimezoned representation. (All + * timezoned dateTime values are UTC.) * * .'24:00:00' -> '00:00:00' * .milisecond: trailing zeros removed @@ -1402,8 +1431,8 @@ XMLCh* DateTime::getDateTimeCanonicalRepresentation() const /*** * 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". + * . 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. * diff --git a/xmltooling/util/DateTime.h b/xmltooling/util/DateTime.h index dead345..f73e244 100644 --- a/xmltooling/util/DateTime.h +++ b/xmltooling/util/DateTime.h @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 Internet2 - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,8 +16,8 @@ /** * @file xmltooling/util/DateTime.h - * - * Manipulation of XML date/time data. + * + * Manipulation of XML date/time data. */ #ifndef __xmltool_datetime_h__ @@ -40,7 +40,7 @@ namespace xmltooling { /** * Class for manipulating XML date/time information. - * + * * This is mostly copied from Xerces-C, but they haven't produced a usable date/time * class, so we had to incorporate a version of it for now. It can't be inherited * since the fields needed are private. @@ -51,20 +51,20 @@ namespace xmltooling /// @cond OFF DateTime(); DateTime(const XMLCh* const); - DateTime(time_t epoch); + DateTime(time_t epoch, bool duration=false); DateTime(const DateTime&); DateTime& operator=(const DateTime&); ~DateTime(); - + inline void setBuffer(const XMLCh* const); - + const XMLCh* getRawData() const; const XMLCh* getFormattedString() const; int getSign() const; - + XMLCh* getDateTimeCanonicalRepresentation() const; XMLCh* getTimeCanonicalRepresentation() const; - + void parseDateTime(); void parseDate(); void parseTime(); @@ -74,19 +74,19 @@ namespace xmltooling void parseMonthDay(); void parseYearMonth(); void parseDuration(); - + static int compare(const DateTime* const, const DateTime* const); static int compare(const DateTime* const, const DateTime* const, bool); - static int compareOrder(const DateTime* const, const DateTime* const); - + static int compareOrder(const DateTime* const, const DateTime* const); + int getYear() const {return fValue[CentYear];} int getMonth() const {return fValue[Month];} int getDay() const {return fValue[Day];} int getHour() const {return fValue[Hour];} int getMinute() const {return fValue[Minute];} int getSecond() const {return fValue[Second];} - time_t getEpoch() const; - + time_t getEpoch(bool duration=false) const; + /// @endcond private: enum valueIndex { @@ -100,52 +100,52 @@ namespace xmltooling utc , TOTAL_SIZE }; - + enum utcType { UTC_UNKNOWN = 0, UTC_STD , // set in parse() or normalize() UTC_POS , // set in parse() UTC_NEG // set in parse() }; - + enum timezoneIndex { hh = 0, mm , TIMEZONE_ARRAYSIZE }; - + static int compareResult(int, int, bool); static void addDuration(DateTime* pDuration, const DateTime* const pBaseDate, int index); static int compareResult(const DateTime* const, const DateTime* const, bool, int); static inline int getRetVal(int, int); - + inline void reset(); //inline void assertBuffer() const; inline void copy(const DateTime&); - + inline void initParser(); inline bool isNormalized() const; - + void getDate(); void getTime(); void getYearMonth(); void getTimeZone(const int); void parseTimeZone(); - + int findUTCSign(const int start); int indexOf(const int start, const int end, const XMLCh ch) const; int parseInt(const int start, const int end) const; int parseIntYear(const int end) const; double parseMiliSecond(const int start, const int end) const; - + void validateDateTime() const; void normalize(); void fillString(XMLCh*& ptr, valueIndex ind, int expLen) const; int fillYearString(XMLCh*& ptr, valueIndex ind) const; void searchMiliSeconds(XMLCh*& miliStartPtr, XMLCh*& miliEndPtr) const; - + bool operator==(const DateTime& toCompare) const; - + static const int DATETIMES[][TOTAL_SIZE]; int fValue[TOTAL_SIZE]; int fTimeZone[TIMEZONE_ARRAYSIZE]; @@ -153,7 +153,7 @@ namespace xmltooling int fEnd; int fBufferMaxLen; XMLCh* fBuffer; - + double fMiliSecond; bool fHasTime; }; @@ -171,33 +171,33 @@ namespace xmltooling memcpy(fBuffer, aString, (fEnd+1) * sizeof(XMLCh)); } } - + inline void DateTime::reset() { for ( int i=0; i < xercesc::XMLDateTime::TOTAL_SIZE; i++ ) fValue[i] = 0; - + fMiliSecond = 0; fHasTime = false; fTimeZone[hh] = fTimeZone[mm] = 0; fStart = fEnd = 0; - + if (fBuffer) *fBuffer = 0; } - + inline void DateTime::copy(const DateTime& rhs) { for ( int i = 0; i < xercesc::XMLDateTime::TOTAL_SIZE; i++ ) fValue[i] = rhs.fValue[i]; - + fMiliSecond = rhs.fMiliSecond; fHasTime = rhs.fHasTime; fTimeZone[hh] = rhs.fTimeZone[hh]; fTimeZone[mm] = rhs.fTimeZone[mm]; fStart = rhs.fStart; fEnd = rhs.fEnd; - + if (fEnd > 0) { if (fEnd > fBufferMaxLen) { delete[] fBuffer; @@ -207,24 +207,24 @@ namespace xmltooling memcpy(fBuffer, rhs.fBuffer, (fEnd+1) * sizeof(XMLCh)); } } - + inline void DateTime::initParser() { fStart = 0; // to ensure scan from the very first beginning // in case the pointer is updated accidentally by someone else. } - + inline bool DateTime::isNormalized() const { return (fValue[xercesc::XMLDateTime::utc] == xercesc::XMLDateTime::UTC_STD ? true : false); } - + inline int DateTime::getRetVal(int c1, int c2) { if ((c1 == xercesc::XMLDateTime::LESS_THAN && c2 == xercesc::XMLDateTime::GREATER_THAN) || (c1 == xercesc::XMLDateTime::GREATER_THAN && c2 == xercesc::XMLDateTime::LESS_THAN)) return xercesc::XMLDateTime::INDETERMINATE; - + return (c1 != xercesc::XMLDateTime::INDETERMINATE) ? c1 : c2; } diff --git a/xmltoolingtest/DateTimeTest.h b/xmltoolingtest/DateTimeTest.h index 4e641bd..a0eb255 100644 --- a/xmltoolingtest/DateTimeTest.h +++ b/xmltoolingtest/DateTimeTest.h @@ -41,15 +41,19 @@ public: auto_ptr_XMLCh d1("P1D"); DateTime dt1(d1.get()); dt1.parseDuration(); - TSM_ASSERT_EQUALS("Epoch for 1 day did not match.", dt1.getEpoch(), 86400); + TSM_ASSERT_EQUALS("Epoch for 1 day did not match.", dt1.getEpoch(true), 86400); auto_ptr_XMLCh d2("PT2H"); DateTime dt2(d2.get()); dt2.parseDuration(); - TSM_ASSERT_EQUALS("Epoch for 2 hours did not match.", dt2.getEpoch(), 7200); + TSM_ASSERT_EQUALS("Epoch for 2 hours did not match.", dt2.getEpoch(true), 7200); - DateTime dt3(28800); + DateTime dt3(28800, true); auto_ptr_char d3(dt3.getRawData()); - TSM_ASSERT_EQUALS("ISO string for 8 hours did not match.", d3.get(), "PT8H"); + TSM_ASSERT("ISO string for 8 hours did not match.", !strcmp(d3.get(), "P0DT8H0M0S")); + + DateTime dt4(-29000, true); + auto_ptr_char d4(dt4.getRawData()); + TSM_ASSERT("ISO string for negative 8 hours did not match.", !strcmp(d4.get(), "-P0DT8H3M20S")); } }; -- 2.1.4