diff options
Diffstat (limited to 'libexslt/date.c')
-rw-r--r-- | libexslt/date.c | 1047 |
1 files changed, 546 insertions, 501 deletions
diff --git a/libexslt/date.c b/libexslt/date.c index 3af6f7fd..4b74677f 100644 --- a/libexslt/date.c +++ b/libexslt/date.c @@ -22,7 +22,7 @@ #define IN_LIBEXSLT #include "libexslt/libexslt.h" -#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) +#if defined(_WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) #include <win32config.h> #else #include "config.h" @@ -79,14 +79,14 @@ typedef enum { XS_GYEAR = (XS_GMONTH << 1), XS_GYEARMONTH = (XS_GYEAR | XS_GMONTH), XS_DATE = (XS_GYEAR | XS_GMONTH | XS_GDAY), - XS_DATETIME = (XS_DATE | XS_TIME), - XS_DURATION = (XS_GYEAR << 1) + XS_DATETIME = (XS_DATE | XS_TIME) } exsltDateType; /* Date value */ -typedef struct _exsltDateValDate exsltDateValDate; -typedef exsltDateValDate *exsltDateValDatePtr; -struct _exsltDateValDate { +typedef struct _exsltDateVal exsltDateVal; +typedef exsltDateVal *exsltDateValPtr; +struct _exsltDateVal { + exsltDateType type; long year; unsigned int mon :4; /* 1 <= mon <= 12 */ unsigned int day :5; /* 1 <= day <= 31 */ @@ -98,22 +98,13 @@ struct _exsltDateValDate { }; /* Duration value */ -typedef struct _exsltDateValDuration exsltDateValDuration; -typedef exsltDateValDuration *exsltDateValDurationPtr; -struct _exsltDateValDuration { - long mon; /* mon stores years also */ +typedef struct _exsltDateDurVal exsltDateDurVal; +typedef exsltDateDurVal *exsltDateDurValPtr; +struct _exsltDateDurVal { + long mon; /* mon stores years also */ long day; - double sec; /* sec stores min and hour also */ -}; - -typedef struct _exsltDateVal exsltDateVal; -typedef exsltDateVal *exsltDateValPtr; -struct _exsltDateVal { - exsltDateType type; - union { - exsltDateValDate date; - exsltDateValDuration dur; - } value; + double sec; /* sec stores min and hour also + 0 <= sec < SECS_PER_DAY */ }; /**************************************************************** @@ -139,7 +130,6 @@ struct _exsltDateVal { ((c == 0) || (c == 'Z') || (c == '+') || (c == '-')) #define VALID_ALWAYS(num) (num >= 0) -#define VALID_YEAR(yr) (yr != 0) #define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12)) /* VALID_DAY should only be used when month is unknown */ #define VALID_DAY(day) ((day >= 1) && (day <= 31)) @@ -148,7 +138,7 @@ struct _exsltDateVal { #define VALID_SEC(sec) ((sec >= 0) && (sec < 60)) #define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440)) #define IS_LEAP(y) \ - (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) + (((y & 3) == 0) && ((y % 25 != 0) || ((y & 15) == 0))) static const unsigned long daysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -164,7 +154,7 @@ static const unsigned long daysInMonthLeap[12] = (dt->day <= daysInMonth[dt->mon - 1])) #define VALID_DATE(dt) \ - (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt)) + (VALID_MONTH(dt->mon) && VALID_MDAY(dt)) /* hour and min structure vals are unsigned, so normal macros give @@ -177,9 +167,14 @@ static const unsigned long daysInMonthLeap[12] = #define VALID_DATETIME(dt) \ (VALID_DATE(dt) && VALID_TIME(dt)) -#define SECS_PER_MIN (60) -#define SECS_PER_HOUR (60 * SECS_PER_MIN) -#define SECS_PER_DAY (24 * SECS_PER_HOUR) +#define SECS_PER_MIN 60 +#define MINS_PER_HOUR 60 +#define HOURS_PER_DAY 24 +#define SECS_PER_HOUR (MINS_PER_HOUR * SECS_PER_MIN) +#define SECS_PER_DAY (HOURS_PER_DAY * SECS_PER_HOUR) +#define MINS_PER_DAY (HOURS_PER_DAY * MINS_PER_HOUR) +#define DAYS_PER_EPOCH (400 * 365 + 100 - 4 + 1) +#define YEARS_PER_EPOCH 400 static const unsigned long dayInYearByMonth[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; @@ -201,10 +196,14 @@ static const unsigned long dayInLeapYearByMonth[12] = * xs:gYear. It is supposed that @dt->year is big enough to contain * the year. * + * According to XML Schema Part 2, the year "0000" is an illegal year value + * which probably means that the year preceding AD 1 is BC 1. Internally, + * we allow a year 0 and adjust the value when parsing and formatting. + * * Returns 0 or the error code */ static int -_exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str) +_exsltDateParseGYear (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str, *firstChar; int isneg = 0, digcnt = 0; @@ -221,6 +220,8 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str) firstChar = cur; while ((*cur >= '0') && (*cur <= '9')) { + if (dt->year >= LONG_MAX / 10) + return -1; dt->year = dt->year * 10 + (*cur - '0'); cur++; digcnt++; @@ -231,17 +232,18 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str) if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0'))) return 1; - if (isneg) - dt->year = - dt->year; - - if (!VALID_YEAR(dt->year)) + if (dt->year == 0) return 2; + /* The internal representation of negative years is continuous. */ + if (isneg) + dt->year = -dt->year + 1; + *str = cur; #ifdef DEBUG_EXSLT_DATE xsltGenericDebug(xsltGenericDebugContext, - "Parsed year %04i\n", dt->year); + "Parsed year %04ld\n", dt->year); #endif return 0; @@ -256,12 +258,12 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str) * @cur is updated to point after the xsl:gYear. */ #define FORMAT_GYEAR(yr, cur) \ - if (yr < 0) { \ + if (yr <= 0) { \ *cur = '-'; \ cur++; \ } \ { \ - long year = (yr < 0) ? - yr : yr; \ + long year = (yr <= 0) ? -yr + 1 : yr; \ xmlChar tmp_buf[100], *tmp = tmp_buf; \ /* result is in reverse-order */ \ while (year > 0) { \ @@ -381,7 +383,7 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str) * Returns 0 or the error code */ static int -_exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str) +_exsltDateParseGMonth (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str; int ret = 0; @@ -423,7 +425,7 @@ _exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str) * Returns 0 or the error code */ static int -_exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str) +_exsltDateParseGDay (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str; int ret = 0; @@ -444,7 +446,7 @@ _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str) /** * FORMAT_GDAY: - * @dt: the #exsltDateValDate to format + * @dt: the #exsltDateVal to format * @cur: a pointer to an allocated buffer * * Formats @dt in xsl:gDay format. Result is appended to @cur and @@ -455,7 +457,7 @@ _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str) /** * FORMAT_DATE: - * @dt: the #exsltDateValDate to format + * @dt: the #exsltDateVal to format * @cur: a pointer to an allocated buffer * * Formats @dt in xsl:date format. Result is appended to @cur and @@ -483,7 +485,7 @@ _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str) * Returns 0 or the error code */ static int -_exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str) +_exsltDateParseTime (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str; unsigned int hour = 0; /* use temp var in case str is not xs:time */ @@ -528,7 +530,7 @@ _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str) /** * FORMAT_TIME: - * @dt: the #exsltDateValDate to format + * @dt: the #exsltDateVal to format * @cur: a pointer to an allocated buffer * * Formats @dt in xsl:time format. Result is appended to @cur and @@ -555,7 +557,7 @@ _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str) * Returns 0 or the error code */ static int -_exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str) +_exsltDateParseTimeZone (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur; int ret = 0; @@ -670,10 +672,8 @@ exsltDateCreateDate (exsltDateType type) } memset (ret, 0, sizeof(exsltDateVal)); - if (type != XS_DURATION) { - ret->value.date.mon = 1; - ret->value.date.day = 1; - } + ret->mon = 1; + ret->day = 1; if (type != EXSLT_UNKNOWN) ret->type = type; @@ -696,54 +696,41 @@ exsltDateFreeDate (exsltDateValPtr date) { } /** - * PARSE_DIGITS: - * @num: the integer to fill in - * @cur: an #xmlChar * - * @num_type: an integer flag + * exsltDateCreateDuration: * - * Parses a digits integer and updates @num with the value. @cur is - * updated to point just after the integer. - * In case of error, @num_type is set to -1, values of @num and - * @cur are undefined. + * Creates a new #exsltDateDurVal, uninitialized. + * + * Returns the #exsltDateDurValPtr */ -#define PARSE_DIGITS(num, cur, num_type) \ - if ((*cur < '0') || (*cur > '9')) \ - num_type = -1; \ - else \ - while ((*cur >= '0') && (*cur <= '9')) { \ - num = num * 10 + (*cur - '0'); \ - cur++; \ - } +static exsltDateDurValPtr +exsltDateCreateDuration (void) +{ + exsltDateDurValPtr ret; + + ret = (exsltDateDurValPtr) xmlMalloc(sizeof(exsltDateDurVal)); + if (ret == NULL) { + xsltGenericError(xsltGenericErrorContext, + "exsltDateCreateDuration: out of memory\n"); + return (NULL); + } + memset (ret, 0, sizeof(exsltDateDurVal)); + + return ret; +} /** - * PARSE_NUM: - * @num: the double to fill in - * @cur: an #xmlChar * - * @num_type: an integer flag + * exsltDateFreeDuration: + * @date: an #exsltDateDurValPtr * - * Parses a float or integer and updates @num with the value. @cur is - * updated to point just after the number. If the number is a float, - * then it must have an integer part and a decimal part; @num_type will - * be set to 1. If there is no decimal part, @num_type is set to zero. - * In case of error, @num_type is set to -1, values of @num and - * @cur are undefined. + * Frees up the @duration */ -#define PARSE_NUM(num, cur, num_type) \ - num = 0; \ - PARSE_DIGITS(num, cur, num_type); \ - if (!num_type && (*cur == '.')) { \ - double mult = 1; \ - cur++; \ - if ((*cur < '0') || (*cur > '9')) \ - num_type = -1; \ - else \ - num_type = 1; \ - while ((*cur >= '0') && (*cur <= '9')) { \ - mult /= 10; \ - num += (*cur - '0') * mult; \ - cur++; \ - } \ - } +static void +exsltDateFreeDuration (exsltDateDurValPtr duration) { + if (duration == NULL) + return; + + xmlFree(duration); +} #ifdef WITH_TIME /** @@ -806,31 +793,32 @@ exsltDateCurrent (void) } /* get real year, not years since 1900 */ - ret->value.date.year = localTm.tm_year + 1900; + ret->year = localTm.tm_year + 1900; - ret->value.date.mon = localTm.tm_mon + 1; - ret->value.date.day = localTm.tm_mday; - ret->value.date.hour = localTm.tm_hour; - ret->value.date.min = localTm.tm_min; + ret->mon = localTm.tm_mon + 1; + ret->day = localTm.tm_mday; + ret->hour = localTm.tm_hour; + ret->min = localTm.tm_min; /* floating point seconds */ - ret->value.date.sec = (double) localTm.tm_sec; + ret->sec = (double) localTm.tm_sec; /* determine the time zone offset from local to gm time */ #if HAVE_GMTIME_R gmtime_r(&secs, &gmTm); #else tb = gmtime(&secs); - if (tb != NULL) - gmTm = *tb; + if (tb == NULL) + return NULL; + gmTm = *tb; #endif - ret->value.date.tz_flag = 0; + ret->tz_flag = 0; #if 0 - ret->value.date.tzo = (((ret->value.date.day * 1440) + - (ret->value.date.hour * 60) + - ret->value.date.min) - - ((gmTm.tm_mday * 1440) + (gmTm.tm_hour * 60) + - gmTm.tm_min)); + ret->tzo = (((ret->day * 1440) + + (ret->hour * 60) + + ret->min) - + ((gmTm.tm_mday * 1440) + (gmTm.tm_hour * 60) + + gmTm.tm_min)); #endif local_s = localTm.tm_hour * SECS_PER_HOUR + localTm.tm_min * SECS_PER_MIN + @@ -841,19 +829,19 @@ exsltDateCurrent (void) gmTm.tm_sec; if (localTm.tm_year < gmTm.tm_year) { - ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; + ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; } else if (localTm.tm_year > gmTm.tm_year) { - ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; + ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; } else if (localTm.tm_mon < gmTm.tm_mon) { - ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; + ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; } else if (localTm.tm_mon > gmTm.tm_mon) { - ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; + ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; } else if (localTm.tm_mday < gmTm.tm_mday) { - ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; + ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; } else if (localTm.tm_mday > gmTm.tm_mday) { - ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; + ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; } else { - ret->value.date.tzo = (local_s - gm_s)/60; + ret->tzo = (local_s - gm_s)/60; } return ret; @@ -877,7 +865,7 @@ exsltDateParse (const xmlChar *dateTime) #define RETURN_TYPE_IF_VALID(t) \ if (IS_TZO_CHAR(*cur)) { \ - ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \ + ret = _exsltDateParseTimeZone(dt, &cur); \ if (ret == 0) { \ if (*cur != 0) \ goto error; \ @@ -906,7 +894,7 @@ exsltDateParse (const xmlChar *dateTime) /* is it an xs:gDay? */ if (*cur == '-') { ++cur; - ret = _exsltDateParseGDay(&(dt->value.date), &cur); + ret = _exsltDateParseGDay(dt, &cur); if (ret != 0) goto error; @@ -918,7 +906,7 @@ exsltDateParse (const xmlChar *dateTime) /* * it should be an xs:gMonthDay or xs:gMonth */ - ret = _exsltDateParseGMonth(&(dt->value.date), &cur); + ret = _exsltDateParseGMonth(dt, &cur); if (ret != 0) goto error; @@ -934,7 +922,7 @@ exsltDateParse (const xmlChar *dateTime) } /* it should be an xs:gMonthDay */ - ret = _exsltDateParseGDay(&(dt->value.date), &cur); + ret = _exsltDateParseGDay(dt, &cur); if (ret != 0) goto error; @@ -948,7 +936,7 @@ exsltDateParse (const xmlChar *dateTime) * Try to parse an xs:time then fallback on right-truncated dates. */ if ((*cur >= '0') && (*cur <= '9')) { - ret = _exsltDateParseTime(&(dt->value.date), &cur); + ret = _exsltDateParseTime(dt, &cur); if (ret == 0) { /* it's an xs:time */ RETURN_TYPE_IF_VALID(XS_TIME); @@ -958,7 +946,7 @@ exsltDateParse (const xmlChar *dateTime) /* fallback on date parsing */ cur = dateTime; - ret = _exsltDateParseGYear(&(dt->value.date), &cur); + ret = _exsltDateParseGYear(dt, &cur); if (ret != 0) goto error; @@ -969,7 +957,7 @@ exsltDateParse (const xmlChar *dateTime) goto error; cur++; - ret = _exsltDateParseGMonth(&(dt->value.date), &cur); + ret = _exsltDateParseGMonth(dt, &cur); if (ret != 0) goto error; @@ -980,8 +968,8 @@ exsltDateParse (const xmlChar *dateTime) goto error; cur++; - ret = _exsltDateParseGDay(&(dt->value.date), &cur); - if ((ret != 0) || !VALID_DATE((&(dt->value.date)))) + ret = _exsltDateParseGDay(dt, &cur); + if ((ret != 0) || !VALID_DATE(dt)) goto error; /* is it an xs:date? */ @@ -992,12 +980,12 @@ exsltDateParse (const xmlChar *dateTime) cur++; /* it should be an xs:dateTime */ - ret = _exsltDateParseTime(&(dt->value.date), &cur); + ret = _exsltDateParseTime(dt, &cur); if (ret != 0) goto error; - ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); - if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date)))) + ret = _exsltDateParseTimeZone(dt, &cur); + if ((ret != 0) || (*cur != 0) || !VALID_DATETIME(dt)) goto error; dt->type = XS_DATETIME; @@ -1016,15 +1004,17 @@ error: * * Parses a duration string * - * Returns a newly built #exsltDateValPtr of NULL in case of error + * Returns a newly built #exsltDateDurValPtr of NULL in case of error */ -static exsltDateValPtr +static exsltDateDurValPtr exsltDateParseDuration (const xmlChar *duration) { const xmlChar *cur = duration; - exsltDateValPtr dur; + exsltDateDurValPtr dur; int isneg = 0; unsigned int seq = 0; + long days, secs = 0; + double sec_frac = 0.0; if (duration == NULL) return NULL; @@ -1038,15 +1028,15 @@ exsltDateParseDuration (const xmlChar *duration) if (*cur++ != 'P') return NULL; - dur = exsltDateCreateDate(XS_DURATION); + dur = exsltDateCreateDuration(); if (dur == NULL) return NULL; while (*cur != 0) { - double num; - int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */ + long num = 0; + size_t has_digits = 0; + int has_frac = 0; const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'}; - const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0}; /* input string should be empty or invalid date/time item */ if (seq >= sizeof(desig)) @@ -1054,67 +1044,123 @@ exsltDateParseDuration (const xmlChar *duration) /* T designator must be present for time items */ if (*cur == 'T') { - if (seq <= 3) { - seq = 3; - cur++; - } else - return NULL; + if (seq > 3) + goto error; + cur++; + seq = 3; } else if (seq == 3) goto error; - /* parse the number portion of the item */ - PARSE_NUM(num, cur, num_type); + /* Parse integral part. */ + while (*cur >= '0' && *cur <= '9') { + long digit = *cur - '0'; - if ((num_type == -1) || (*cur == 0)) - goto error; - - /* update duration based on item type */ - while (seq < sizeof(desig)) { - if (*cur == desig[seq]) { - - /* verify numeric type; only seconds can be float */ - if ((num_type != 0) && (seq < (sizeof(desig)-1))) - goto error; + if (num > LONG_MAX / 10) + goto error; + num *= 10; + if (num > LONG_MAX - digit) + goto error; + num += digit; - switch (seq) { - case 0: - dur->value.dur.mon = (long)num * 12; - break; - case 1: - dur->value.dur.mon += (long)num; - break; - default: - /* convert to seconds using multiplier */ - dur->value.dur.sec += num * multi[seq]; - seq++; - break; - } + has_digits = 1; + cur++; + } - break; /* exit loop */ + if (*cur == '.') { + /* Parse fractional part. */ + double mult = 1.0; + cur++; + has_frac = 1; + while (*cur >= '0' && *cur <= '9') { + mult /= 10.0; + sec_frac += (*cur - '0') * mult; + has_digits = 1; + cur++; } - /* no date designators found? */ - if (++seq == 3) + } + + while (*cur != desig[seq]) { + seq++; + /* No T designator or invalid char. */ + if (seq == 3 || seq == sizeof(desig)) goto error; } cur++; + + if (!has_digits || (has_frac && (seq != 5))) + goto error; + + switch (seq) { + case 0: + /* Year */ + if (num > LONG_MAX / 12) + goto error; + dur->mon = num * 12; + break; + case 1: + /* Month */ + if (dur->mon > LONG_MAX - num) + goto error; + dur->mon += num; + break; + case 2: + /* Day */ + dur->day = num; + break; + case 3: + /* Hour */ + days = num / HOURS_PER_DAY; + if (dur->day > LONG_MAX - days) + goto error; + dur->day += days; + secs = (num % HOURS_PER_DAY) * SECS_PER_HOUR; + break; + case 4: + /* Minute */ + days = num / MINS_PER_DAY; + if (dur->day > LONG_MAX - days) + goto error; + dur->day += days; + secs += (num % MINS_PER_DAY) * SECS_PER_MIN; + break; + case 5: + /* Second */ + days = num / SECS_PER_DAY; + if (dur->day > LONG_MAX - days) + goto error; + dur->day += days; + secs += num % SECS_PER_DAY; + break; + } + + seq++; } + days = secs / SECS_PER_DAY; + if (dur->day > LONG_MAX - days) + goto error; + dur->day += days; + dur->sec = (secs % SECS_PER_DAY) + sec_frac; + if (isneg) { - dur->value.dur.mon = -dur->value.dur.mon; - dur->value.dur.day = -dur->value.dur.day; - dur->value.dur.sec = -dur->value.dur.sec; + dur->mon = -dur->mon; + dur->day = -dur->day; + if (dur->sec != 0.0) { + dur->sec = SECS_PER_DAY - dur->sec; + dur->day -= 1; + } } #ifdef DEBUG_EXSLT_DATE xsltGenericDebug(xsltGenericDebugContext, - "Parsed duration %f\n", dur->value.dur.sec); + "Parsed duration %f\n", dur->sec); #endif return dur; error: if (dur != NULL) - exsltDateFreeDate(dur); + exsltDateFreeDuration(dur); return NULL; } @@ -1126,49 +1172,47 @@ error: * @item: char designator * */ -#define FORMAT_ITEM(num, cur, limit, item) \ - if (num != 0) { \ - long comp = (long)num / limit; \ - if (comp != 0) { \ - FORMAT_FLOAT((double)comp, cur, 0); \ - *cur++ = item; \ - num -= (double)(comp * limit); \ - } \ +#define FORMAT_ITEM(num, cur, limit, item) \ + if (num >= limit) { \ + double comp = floor(num / limit); \ + FORMAT_FLOAT(comp, cur, 0); \ + *cur++ = item; \ + num -= comp * limit; \ } /** * exsltDateFormatDuration: - * @dt: an #exsltDateValDurationPtr + * @dur: an #exsltDateDurValPtr * - * Formats @dt in xs:duration format. + * Formats the duration. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * -exsltDateFormatDuration (const exsltDateValDurationPtr dt) +exsltDateFormatDuration (const exsltDateDurValPtr dur) { xmlChar buf[100], *cur = buf; double secs, days; double years, months; - if (dt == NULL) + if (dur == NULL) return NULL; /* quick and dirty check */ - if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0)) + if ((dur->sec == 0.0) && (dur->day == 0) && (dur->mon == 0)) return xmlStrdup((xmlChar*)"P0D"); - secs = dt->sec; - days = (double)dt->day; - years = (double)(dt->mon / 12); - months = (double)(dt->mon % 12); + secs = dur->sec; + days = (double)dur->day; + years = (double)(dur->mon / 12); + months = (double)(dur->mon % 12); *cur = '\0'; - if (secs < 0.0) { - secs = -secs; - *cur = '-'; - } if (days < 0) { + if (secs != 0.0) { + secs = SECS_PER_DAY - secs; + days += 1; + } days = -days; *cur = '-'; } @@ -1193,12 +1237,6 @@ exsltDateFormatDuration (const exsltDateValDurationPtr dt) FORMAT_ITEM(months, cur, 1, 'M'); } - if (secs >= SECS_PER_DAY) { - double tmp = floor(secs / SECS_PER_DAY); - days += tmp; - secs -= (tmp * SECS_PER_DAY); - } - FORMAT_ITEM(days, cur, 1, 'D'); if (secs > 0.0) { *cur++ = 'T'; @@ -1217,14 +1255,14 @@ exsltDateFormatDuration (const exsltDateValDurationPtr dt) /** * exsltDateFormatDateTime: - * @dt: an #exsltDateValDatePtr + * @dt: an #exsltDateValPtr * * Formats @dt in xs:dateTime format. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * -exsltDateFormatDateTime (const exsltDateValDatePtr dt) +exsltDateFormatDateTime (const exsltDateValPtr dt) { xmlChar buf[100], *cur = buf; @@ -1243,14 +1281,14 @@ exsltDateFormatDateTime (const exsltDateValDatePtr dt) /** * exsltDateFormatDate: - * @dt: an #exsltDateValDatePtr + * @dt: an #exsltDateValPtr * * Formats @dt in xs:date format. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * -exsltDateFormatDate (const exsltDateValDatePtr dt) +exsltDateFormatDate (const exsltDateValPtr dt) { xmlChar buf[100], *cur = buf; @@ -1268,14 +1306,14 @@ exsltDateFormatDate (const exsltDateValDatePtr dt) /** * exsltDateFormatTime: - * @dt: an #exsltDateValDatePtr + * @dt: an #exsltDateValPtr * * Formats @dt in xs:time format. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * -exsltDateFormatTime (const exsltDateValDatePtr dt) +exsltDateFormatTime (const exsltDateValPtr dt) { xmlChar buf[100], *cur = buf; @@ -1309,14 +1347,12 @@ exsltDateFormat (const exsltDateValPtr dt) return NULL; switch (dt->type) { - case XS_DURATION: - return exsltDateFormatDuration(&(dt->value.dur)); case XS_DATETIME: - return exsltDateFormatDateTime(&(dt->value.date)); + return exsltDateFormatDateTime(dt); case XS_DATE: - return exsltDateFormatDate(&(dt->value.date)); + return exsltDateFormatDate(dt); case XS_TIME: - return exsltDateFormatTime(&(dt->value.date)); + return exsltDateFormatTime(dt); default: break; } @@ -1324,15 +1360,15 @@ exsltDateFormat (const exsltDateValPtr dt) if (dt->type & XS_GYEAR) { xmlChar buf[100], *cur = buf; - FORMAT_GYEAR(dt->value.date.year, cur); + FORMAT_GYEAR(dt->year, cur); if (dt->type == XS_GYEARMONTH) { *cur = '-'; cur++; - FORMAT_GMONTH(dt->value.date.mon, cur); + FORMAT_GMONTH(dt->mon, cur); } - if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) { - FORMAT_TZ(dt->value.date.tzo, cur); + if (dt->tz_flag || (dt->tzo != 0)) { + FORMAT_TZ(dt->tzo, cur); } *cur = 0; return xmlStrdup(buf); @@ -1348,7 +1384,7 @@ exsltDateFormat (const exsltDateValPtr dt) * Convert mon and year of @dt to total number of days. Take the * number of years since (or before) 1 AD and add the number of leap * years. This is a function because negative - * years must be handled a little differently and there is no zero year. + * years must be handled a little differently. * * Returns number of days. */ @@ -1357,16 +1393,16 @@ _exsltDateCastYMToDays (const exsltDateValPtr dt) { long ret; - if (dt->value.date.year < 0) - ret = (dt->value.date.year * 365) + - (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+ - ((dt->value.date.year+1)/400)) + - DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year); + if (dt->year <= 0) + ret = ((dt->year-1) * 365) + + (((dt->year)/4)-((dt->year)/100)+ + ((dt->year)/400)) + + DAY_IN_YEAR(0, dt->mon, dt->year) - 1; else - ret = ((dt->value.date.year-1) * 365) + - (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+ - ((dt->value.date.year-1)/400)) + - DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year); + ret = ((dt->year-1) * 365) + + (((dt->year-1)/4)-((dt->year-1)/100)+ + ((dt->year-1)/400)) + + DAY_IN_YEAR(0, dt->mon, dt->year); return ret; } @@ -1380,42 +1416,8 @@ _exsltDateCastYMToDays (const exsltDateValPtr dt) * Returns seconds. */ #define TIME_TO_NUMBER(dt) \ - ((double)((dt->value.date.hour * SECS_PER_HOUR) + \ - (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec) - -/** - * exsltDateCastDateToNumber: - * @dt: an #exsltDateValPtr - * - * Calculates the number of seconds from year zero. - * - * Returns seconds from zero year. - */ -static double -exsltDateCastDateToNumber (const exsltDateValPtr dt) -{ - double ret = 0.0; - - if (dt == NULL) - return 0.0; - - if ((dt->type & XS_GYEAR) == XS_GYEAR) { - ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY; - } - - /* add in days */ - if (dt->type == XS_DURATION) { - ret += (double)dt->value.dur.day * SECS_PER_DAY; - ret += dt->value.dur.sec; - } else { - ret += (double)dt->value.date.day * SECS_PER_DAY; - /* add in time */ - ret += TIME_TO_NUMBER(dt); - } - - - return ret; -} + ((double)((dt->hour * SECS_PER_HOUR) + \ + (dt->min * SECS_PER_MIN)) + dt->sec) /** * _exsltDateTruncateDate: @@ -1433,19 +1435,19 @@ _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type) return 1; if ((type & XS_TIME) != XS_TIME) { - dt->value.date.hour = 0; - dt->value.date.min = 0; - dt->value.date.sec = 0.0; + dt->hour = 0; + dt->min = 0; + dt->sec = 0.0; } if ((type & XS_GDAY) != XS_GDAY) - dt->value.date.day = 1; + dt->day = 1; if ((type & XS_GMONTH) != XS_GMONTH) - dt->value.date.mon = 1; + dt->mon = 1; if ((type & XS_GYEAR) != XS_GYEAR) - dt->value.date.year = 0; + dt->year = 0; dt->type = type; @@ -1461,7 +1463,7 @@ _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type) * a Monday so all other days are calculated from there. Take the * number of years since (or before) add the number of leap years and * the day-in-year and mod by 7. This is a function because negative - * years must be handled a little differently and there is no zero year. + * years must be handled a little differently. * * Returns day in week (Sunday = 0). */ @@ -1470,8 +1472,8 @@ _exsltDateDayInWeek(long yday, long yr) { long ret; - if (yr < 0) { - ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7); + if (yr <= 0) { + ret = ((yr-2 + ((yr/4)-(yr/100)+(yr/400)) + yday) % 7); if (ret < 0) ret += 7; } else @@ -1480,18 +1482,10 @@ _exsltDateDayInWeek(long yday, long yr) return ret; } -/* - * macros for adding date/times and durations - */ -#define FQUOTIENT(a,b) ((floor(((double)a/(double)b)))) -#define MODULO(a,b) ((a - FQUOTIENT(a,b) * b)) -#define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low))) -#define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low) - /** * _exsltDateAdd: * @dt: an #exsltDateValPtr - * @dur: an #exsltDateValPtr of type #XS_DURATION + * @dur: an #exsltDateDurValPtr * * Compute a new date/time from @dt and @dur. This function assumes @dt * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR. @@ -1499,12 +1493,11 @@ _exsltDateDayInWeek(long yday, long yr) * Returns date/time pointer or NULL. */ static exsltDateValPtr -_exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur) +_exsltDateAdd (exsltDateValPtr dt, exsltDateDurValPtr dur) { exsltDateValPtr ret; - long carry, tempdays, temp; - exsltDateValDatePtr r, d; - exsltDateValDurationPtr u; + long carry, temp; + double sum; if ((dt == NULL) || (dur == NULL)) return NULL; @@ -1513,105 +1506,119 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur) if (ret == NULL) return NULL; - r = &(ret->value.date); - d = &(dt->value.date); - u = &(dur->value.dur); + /* + * Note that temporary values may need more bits than the values in + * bit field. + */ /* month */ - carry = d->mon + u->mon; - r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13); - carry = (long)FQUOTIENT_RANGE(carry, 1, 13); + temp = dt->mon + dur->mon % 12; + carry = dur->mon / 12; + if (temp < 1) { + temp += 12; + carry -= 1; + } + else if (temp > 12) { + temp -= 12; + carry += 1; + } + ret->mon = temp; - /* year (may be modified later) */ - r->year = d->year + carry; - if (r->year == 0) { - if (d->year > 0) - r->year--; - else - r->year++; + /* + * year (may be modified later) + * + * Add epochs from dur->day now to avoid overflow later and to speed up + * pathological cases. + */ + carry += (dur->day / DAYS_PER_EPOCH) * YEARS_PER_EPOCH; + if ((carry > 0 && dt->year > LONG_MAX - carry) || + (carry < 0 && dt->year < LONG_MIN - carry)) { + /* Overflow */ + exsltDateFreeDate(ret); + return NULL; } + ret->year = dt->year + carry; /* time zone */ - r->tzo = d->tzo; - r->tz_flag = d->tz_flag; + ret->tzo = dt->tzo; + ret->tz_flag = dt->tz_flag; /* seconds */ - r->sec = d->sec + u->sec; - carry = (long)FQUOTIENT((long)r->sec, 60); - if (r->sec != 0.0) { - r->sec = MODULO(r->sec, 60.0); - } + sum = dt->sec + dur->sec; + ret->sec = fmod(sum, 60.0); + carry = (long)(sum / 60.0); /* minute */ - carry += d->min; - r->min = (unsigned int)MODULO(carry, 60); - carry = (long)FQUOTIENT(carry, 60); + temp = dt->min + carry % 60; + carry = carry / 60; + if (temp >= 60) { + temp -= 60; + carry += 1; + } + ret->min = temp; /* hours */ - carry += d->hour; - r->hour = (unsigned int)MODULO(carry, 24); - carry = (long)FQUOTIENT(carry, 24); + temp = dt->hour + carry % 24; + carry = carry / 24; + if (temp >= 24) { + temp -= 24; + carry += 1; + } + ret->hour = temp; - /* - * days - * Note we use tempdays because the temporary values may need more - * than 5 bits - */ - if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) && - (d->day > MAX_DAYINMONTH(r->year, r->mon))) - tempdays = MAX_DAYINMONTH(r->year, r->mon); - else if (d->day < 1) - tempdays = 1; + /* days */ + if (dt->day > MAX_DAYINMONTH(ret->year, ret->mon)) + temp = MAX_DAYINMONTH(ret->year, ret->mon); + else if (dt->day < 1) + temp = 1; else - tempdays = d->day; + temp = dt->day; - tempdays += u->day + carry; + temp += dur->day % DAYS_PER_EPOCH + carry; while (1) { - if (tempdays < 1) { - long tmon = (long)MODULO_RANGE((int)r->mon-1, 1, 13); - long tyr = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13); - if (tyr == 0) - tyr--; - /* - * Coverity detected an overrun in daysInMonth - * of size 12 at position 12 with index variable "((r)->mon - 1)" - */ - if (tmon < 0) - tmon = 0; - if (tmon > 12) - tmon = 12; - tempdays += MAX_DAYINMONTH(tyr, tmon); - carry = -1; - } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) { - tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon); - carry = 1; + if (temp < 1) { + if (ret->mon > 1) { + ret->mon -= 1; + } + else { + if (ret->year == LONG_MIN) { + exsltDateFreeDate(ret); + return NULL; + } + ret->mon = 12; + ret->year -= 1; + } + temp += MAX_DAYINMONTH(ret->year, ret->mon); + } else if (temp > (long)MAX_DAYINMONTH(ret->year, ret->mon)) { + temp -= MAX_DAYINMONTH(ret->year, ret->mon); + if (ret->mon < 12) { + ret->mon += 1; + } + else { + if (ret->year == LONG_MAX) { + exsltDateFreeDate(ret); + return NULL; + } + ret->mon = 1; + ret->year += 1; + } } else break; - - temp = r->mon + carry; - r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13); - r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13); - if (r->year == 0) { - if (temp < 1) - r->year--; - else - r->year++; - } } - r->day = tempdays; + ret->day = temp; /* * adjust the date/time type to the date values */ if (ret->type != XS_DATETIME) { - if ((r->hour) || (r->min) || (r->sec)) + if ((ret->hour) || (ret->min) || (ret->sec)) ret->type = XS_DATETIME; else if (ret->type != XS_DATE) { - if (r->day != 1) + if (ret->day != 1) ret->type = XS_DATE; - else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1)) + else if ((ret->type != XS_GYEARMONTH) && (ret->mon != 1)) ret->type = XS_GYEARMONTH; } } @@ -1629,12 +1636,12 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur) * (i.e. y - x). If the @flag is set then even if the least specific * format of @x or @y is xs:gYear or xs:gYearMonth. * - * Returns date/time pointer or NULL. + * Returns a duration pointer or NULL. */ -static exsltDateValPtr +static exsltDateDurValPtr _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag) { - exsltDateValPtr ret; + exsltDateDurValPtr ret; if ((x == NULL) || (y == NULL)) return NULL; @@ -1655,30 +1662,37 @@ _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag) } } - ret = exsltDateCreateDate(XS_DURATION); + ret = exsltDateCreateDuration(); if (ret == NULL) return NULL; if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) { /* compute the difference in months */ - ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) - - ((x->value.date.year * 12) + x->value.date.mon); - /* The above will give a wrong result if x and y are on different sides - of the September 1752. Resolution is welcome :-) */ + if ((x->year >= LONG_MAX / 24) || (x->year <= LONG_MIN / 24) || + (y->year >= LONG_MAX / 24) || (y->year <= LONG_MIN / 24)) { + /* Possible overflow. */ + exsltDateFreeDuration(ret); + return NULL; + } + ret->mon = (y->year - x->year) * 12 + (y->mon - x->mon); } else { - ret->value.dur.day = _exsltDateCastYMToDays(y) - - _exsltDateCastYMToDays(x); - ret->value.dur.day += y->value.date.day - x->value.date.day; - ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x); - ret->value.dur.sec += (x->value.date.tzo - y->value.date.tzo) * - SECS_PER_MIN; - if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) { - ret->value.dur.day -= 1; - ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY; - } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) { - ret->value.dur.day += 1; - ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY; - } + long carry; + + if ((x->year > LONG_MAX / 731) || (x->year < LONG_MIN / 731) || + (y->year > LONG_MAX / 731) || (y->year < LONG_MIN / 731)) { + /* Possible overflow. */ + exsltDateFreeDuration(ret); + return NULL; + } + + ret->sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x); + ret->sec += (x->tzo - y->tzo) * SECS_PER_MIN; + carry = (long)floor(ret->sec / SECS_PER_DAY); + ret->sec = ret->sec - carry * SECS_PER_DAY; + + ret->day = _exsltDateCastYMToDays(y) - _exsltDateCastYMToDays(x); + ret->day += y->day - x->day; + ret->day += carry; } return ret; @@ -1686,9 +1700,9 @@ _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag) /** * _exsltDateAddDurCalc - * @ret: an exsltDateValPtr for the return value: - * @x: an exsltDateValPtr for the first operand - * @y: an exsltDateValPtr for the second operand + * @ret: an exsltDateDurValPtr for the return value: + * @x: an exsltDateDurValPtr for the first operand + * @y: an exsltDateDurValPtr for the second operand * * Add two durations, catering for possible negative values. * The sum is placed in @ret. @@ -1696,72 +1710,76 @@ _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag) * Returns 1 for success, 0 if error detected. */ static int -_exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x, - exsltDateValPtr y) +_exsltDateAddDurCalc (exsltDateDurValPtr ret, exsltDateDurValPtr x, + exsltDateDurValPtr y) { - long carry; - /* months */ - ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon; - - /* seconds */ - ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec; - carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY); - if (ret->value.dur.sec != 0.0) { - ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY); - /* - * Our function MODULO always gives us a positive value, so - * if we end up with a "-ve" carry we need to adjust it - * appropriately (bug 154021) - */ - if ((carry < 0) && (ret->value.dur.sec != 0)) { - /* change seconds to equiv negative modulus */ - ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY; - carry++; - } + if ((x->mon > 0 && y->mon > LONG_MAX - x->mon) || + (x->mon < 0 && y->mon < LONG_MIN - x->mon)) { + /* Overflow */ + return 0; } + ret->mon = x->mon + y->mon; /* days */ - ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry; + if ((x->day > 0 && y->day > LONG_MAX - x->day) || + (x->day < 0 && y->day < LONG_MIN - x->day)) { + /* Overflow */ + return 0; + } + ret->day = x->day + y->day; + + /* seconds */ + ret->sec = x->sec + y->sec; + if (ret->sec >= SECS_PER_DAY) { + if (ret->day == LONG_MAX) { + /* Overflow */ + return 0; + } + ret->sec -= SECS_PER_DAY; + ret->day += 1; + } /* * are the results indeterminate? i.e. how do you subtract days from * months or years? */ - if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) && - (ret->value.dur.mon < 0)) || - (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) && - (ret->value.dur.mon > 0))) { - return 0; + if (ret->day >= 0) { + if (((ret->day > 0) || (ret->sec > 0)) && (ret->mon < 0)) + return 0; + } + else { + if (ret->mon > 0) + return 0; } return 1; } /** * _exsltDateAddDuration: - * @x: an #exsltDateValPtr of type #XS_DURATION - * @y: an #exsltDateValPtr of type #XS_DURATION + * @x: an #exsltDateDurValPtr + * @y: an #exsltDateDurValPtr * * Compute a new duration from @x and @y. * - * Returns date/time pointer or NULL. + * Returns a duration pointer or NULL. */ -static exsltDateValPtr -_exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y) +static exsltDateDurValPtr +_exsltDateAddDuration (exsltDateDurValPtr x, exsltDateDurValPtr y) { - exsltDateValPtr ret; + exsltDateDurValPtr ret; if ((x == NULL) || (y == NULL)) return NULL; - ret = exsltDateCreateDate(XS_DURATION); + ret = exsltDateCreateDuration(); if (ret == NULL) return NULL; if (_exsltDateAddDurCalc(ret, x, y)) return ret; - exsltDateFreeDate(ret); + exsltDateFreeDuration(ret); return NULL; } @@ -1788,7 +1806,7 @@ exsltDateDateTime (void) cur = exsltDateCurrent(); if (cur != NULL) { - ret = exsltDateFormatDateTime(&(cur->value.date)); + ret = exsltDateFormatDateTime(cur); exsltDateFreeDate(cur); } #endif @@ -1834,7 +1852,7 @@ exsltDateDate (const xmlChar *dateTime) } } - ret = exsltDateFormatDate(&(dt->value.date)); + ret = exsltDateFormatDate(dt); exsltDateFreeDate(dt); return ret; @@ -1878,7 +1896,7 @@ exsltDateTime (const xmlChar *dateTime) } } - ret = exsltDateFormatTime(&(dt->value.date)); + ret = exsltDateFormatTime(dt); exsltDateFreeDate(dt); return ret; @@ -1908,6 +1926,7 @@ static double exsltDateYear (const xmlChar *dateTime) { exsltDateValPtr dt; + long year; double ret; if (dateTime == NULL) { @@ -1927,7 +1946,9 @@ exsltDateYear (const xmlChar *dateTime) } } - ret = (double) dt->value.date.year; + year = dt->year; + if (year <= 0) year -= 1; /* Adjust for missing year 0. */ + ret = (double) year; exsltDateFreeDate(dt); return ret; @@ -1956,16 +1977,32 @@ exsltDateYear (const xmlChar *dateTime) static xmlXPathObjectPtr exsltDateLeapYear (const xmlChar *dateTime) { - double year; + exsltDateValPtr dt = NULL; + xmlXPathObjectPtr ret; - year = exsltDateYear(dateTime); - if (xmlXPathIsNaN(year)) - return xmlXPathNewFloat(xmlXPathNAN); + if (dateTime == NULL) { +#ifdef WITH_TIME + dt = exsltDateCurrent(); +#endif + } else { + dt = exsltDateParse(dateTime); + if ((dt != NULL) && + (dt->type != XS_DATETIME) && (dt->type != XS_DATE) && + (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) { + exsltDateFreeDate(dt); + dt = NULL; + } + } - if (IS_LEAP((long)year)) - return xmlXPathNewBoolean(1); + if (dt == NULL) { + ret = xmlXPathNewFloat(xmlXPathNAN); + } + else { + ret = xmlXPathNewBoolean(IS_LEAP(dt->year)); + exsltDateFreeDate(dt); + } - return xmlXPathNewBoolean(0); + return ret; } /** @@ -2013,7 +2050,7 @@ exsltDateMonthInYear (const xmlChar *dateTime) } } - ret = (double) dt->value.date.mon; + ret = (double) dt->mon; exsltDateFreeDate(dt); return ret; @@ -2060,11 +2097,12 @@ exsltDateMonthName (const xmlChar *dateTime) { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 }, { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 } }; - int month; - month = (int) exsltDateMonthInYear(dateTime); - if (!VALID_MONTH(month)) - month = 0; - return monthNames[month]; + double month; + int index = 0; + month = exsltDateMonthInYear(dateTime); + if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0)) + index = (int) month; + return monthNames[index]; } /** @@ -2108,11 +2146,12 @@ exsltDateMonthAbbreviation (const xmlChar *dateTime) { 'N', 'o', 'v', 0 }, { 'D', 'e', 'c', 0 } }; - int month; - month = (int) exsltDateMonthInYear(dateTime); - if(!VALID_MONTH(month)) - month = 0; - return monthAbbreviations[month]; + double month; + int index = 0; + month = exsltDateMonthInYear(dateTime); + if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0)) + index = (int) month; + return monthAbbreviations[index]; } /** @@ -2158,23 +2197,22 @@ exsltDateWeekInYear (const xmlChar *dateTime) } } - diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon, - dt->value.date.year); + diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year); /* * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday * is the first day-in-week */ - diw = (_exsltDateDayInWeek(diy, dt->value.date.year) + 6) % 7; + diw = (_exsltDateDayInWeek(diy, dt->year) + 6) % 7; /* ISO 8601 adjustment, 3 is Thu */ diy += (3 - diw); if(diy < 1) { - year = dt->value.date.year - 1; + year = dt->year - 1; if(year == 0) year--; diy = DAY_IN_YEAR(31, 12, year) + diy; - } else if (diy > (long)DAY_IN_YEAR(31, 12, dt->value.date.year)) { - diy -= DAY_IN_YEAR(31, 12, dt->value.date.year); + } else if (diy > (long)DAY_IN_YEAR(31, 12, dt->year)) { + diy -= DAY_IN_YEAR(31, 12, dt->year); } ret = ((diy - 1) / 7) + 1; @@ -2227,14 +2265,14 @@ exsltDateWeekInMonth (const xmlChar *dateTime) } } - fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year); + fdiy = DAY_IN_YEAR(1, dt->mon, dt->year); /* * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday * is the first day-in-week */ - fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7; + fdiw = (_exsltDateDayInWeek(fdiy, dt->year) + 6) % 7; - ret = ((dt->value.date.day + fdiw - 1) / 7) + 1; + ret = ((dt->day + fdiw - 1) / 7) + 1; exsltDateFreeDate(dt); @@ -2281,8 +2319,7 @@ exsltDateDayInYear (const xmlChar *dateTime) } } - ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon, - dt->value.date.year); + ret = DAY_IN_YEAR(dt->day, dt->mon, dt->year); exsltDateFreeDate(dt); @@ -2332,7 +2369,7 @@ exsltDateDayInMonth (const xmlChar *dateTime) } } - ret = (double) dt->value.date.day; + ret = (double) dt->day; exsltDateFreeDate(dt); return ret; @@ -2379,7 +2416,7 @@ exsltDateDayOfWeekInMonth (const xmlChar *dateTime) } } - ret = ((dt->value.date.day -1) / 7) + 1; + ret = ((dt->day -1) / 7) + 1; exsltDateFreeDate(dt); @@ -2428,10 +2465,9 @@ exsltDateDayInWeek (const xmlChar *dateTime) } } - diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon, - dt->value.date.year); + diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year); - ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1; + ret = _exsltDateDayInWeek(diy, dt->year) + 1; exsltDateFreeDate(dt); @@ -2471,11 +2507,12 @@ exsltDateDayName (const xmlChar *dateTime) { 'F', 'r', 'i', 'd', 'a', 'y', 0 }, { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 } }; - int day; - day = (int) exsltDateDayInWeek(dateTime); - if((day < 1) || (day > 7)) - day = 0; - return dayNames[day]; + double day; + int index = 0; + day = exsltDateDayInWeek(dateTime); + if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0)) + index = (int) day; + return dayNames[index]; } /** @@ -2511,11 +2548,12 @@ exsltDateDayAbbreviation (const xmlChar *dateTime) { 'F', 'r', 'i', 0 }, { 'S', 'a', 't', 0 } }; - int day; - day = (int) exsltDateDayInWeek(dateTime); - if((day < 1) || (day > 7)) - day = 0; - return dayAbbreviations[day]; + double day; + int index = 0; + day = exsltDateDayInWeek(dateTime); + if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0)) + index = (int) day; + return dayAbbreviations[index]; } /** @@ -2558,7 +2596,7 @@ exsltDateHourInDay (const xmlChar *dateTime) } } - ret = (double) dt->value.date.hour; + ret = (double) dt->hour; exsltDateFreeDate(dt); return ret; @@ -2604,7 +2642,7 @@ exsltDateMinuteInHour (const xmlChar *dateTime) } } - ret = (double) dt->value.date.min; + ret = (double) dt->min; exsltDateFreeDate(dt); return ret; @@ -2652,7 +2690,7 @@ exsltDateSecondInMinute (const xmlChar *dateTime) } } - ret = dt->value.date.sec; + ret = dt->sec; exsltDateFreeDate(dt); return ret; @@ -2685,7 +2723,8 @@ exsltDateSecondInMinute (const xmlChar *dateTime) static xmlChar * exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr) { - exsltDateValPtr dt, dur, res; + exsltDateValPtr dt, res; + exsltDateDurValPtr dur; xmlChar *ret; if ((xstr == NULL) || (ystr == NULL)) @@ -2708,7 +2747,7 @@ exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr) res = _exsltDateAdd(dt, dur); exsltDateFreeDate(dt); - exsltDateFreeDate(dur); + exsltDateFreeDuration(dur); if (res == NULL) return NULL; @@ -2743,7 +2782,7 @@ exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr) static xmlChar * exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr) { - exsltDateValPtr x, y, res; + exsltDateDurValPtr x, y, res; xmlChar *ret; if ((xstr == NULL) || (ystr == NULL)) @@ -2755,20 +2794,20 @@ exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr) y = exsltDateParseDuration(ystr); if (y == NULL) { - exsltDateFreeDate(x); + exsltDateFreeDuration(x); return NULL; } res = _exsltDateAddDuration(x, y); - exsltDateFreeDate(x); - exsltDateFreeDate(y); + exsltDateFreeDuration(x); + exsltDateFreeDuration(y); if (res == NULL) return NULL; - ret = exsltDateFormatDuration(&(res->value.dur)); - exsltDateFreeDate(res); + ret = exsltDateFormatDuration(res); + exsltDateFreeDuration(res); return ret; } @@ -2798,7 +2837,7 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs) xmlNodeSetPtr ns; void *user = NULL; xmlChar *tmp; - exsltDateValPtr x, total; + exsltDateDurValPtr x, total; xmlChar *ret; int i; @@ -2825,7 +2864,7 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs) return; } - total = exsltDateCreateDate (XS_DURATION); + total = exsltDateCreateDuration (); if (total == NULL) { xmlXPathFreeNodeSet (ns); return; @@ -2836,14 +2875,14 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs) tmp = xmlXPathCastNodeToString (ns->nodeTab[i]); if (tmp == NULL) { xmlXPathFreeNodeSet (ns); - exsltDateFreeDate (total); + exsltDateFreeDuration (total); return; } x = exsltDateParseDuration (tmp); if (x == NULL) { xmlFree (tmp); - exsltDateFreeDate (total); + exsltDateFreeDuration (total); xmlXPathFreeNodeSet (ns); xmlXPathReturnEmptyString (ctxt); return; @@ -2851,18 +2890,18 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs) result = _exsltDateAddDurCalc(total, total, x); - exsltDateFreeDate (x); + exsltDateFreeDuration (x); xmlFree (tmp); if (!result) { - exsltDateFreeDate (total); + exsltDateFreeDuration (total); xmlXPathFreeNodeSet (ns); xmlXPathReturnEmptyString (ctxt); return; } } - ret = exsltDateFormatDuration (&(total->value.dur)); - exsltDateFreeDate (total); + ret = exsltDateFormatDuration (total); + exsltDateFreeDuration (total); xmlXPathFreeNodeSet (ns); if (user != NULL) @@ -2906,6 +2945,7 @@ static double exsltDateSeconds (const xmlChar *dateTime) { exsltDateValPtr dt; + exsltDateDurValPtr dur = NULL; double ret = xmlXPathNAN; if (dateTime == NULL) { @@ -2915,16 +2955,14 @@ exsltDateSeconds (const xmlChar *dateTime) #endif return xmlXPathNAN; } else { - dt = exsltDateParseDuration(dateTime); + dt = exsltDateParse(dateTime); if (dt == NULL) - dt = exsltDateParse(dateTime); + dur = exsltDateParseDuration(dateTime); } - if (dt == NULL) - return xmlXPathNAN; - - if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) { - exsltDateValPtr y, dur; + if ((dt != NULL) && (dt->type >= XS_GYEAR)) { + exsltDateValPtr y; + exsltDateDurValPtr diff; /* * compute the difference between the given (or current) date @@ -2932,23 +2970,27 @@ exsltDateSeconds (const xmlChar *dateTime) */ y = exsltDateCreateDate(XS_DATETIME); if (y != NULL) { - y->value.date.year = 1970; - y->value.date.mon = 1; - y->value.date.day = 1; - y->value.date.tz_flag = 1; - - dur = _exsltDateDifference(y, dt, 1); - if (dur != NULL) { - ret = exsltDateCastDateToNumber(dur); - exsltDateFreeDate(dur); + y->year = 1970; + y->mon = 1; + y->day = 1; + y->tz_flag = 1; + + diff = _exsltDateDifference(y, dt, 1); + if (diff != NULL) { + ret = (double)diff->day * SECS_PER_DAY + diff->sec; + exsltDateFreeDuration(diff); } exsltDateFreeDate(y); } - } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0)) - ret = exsltDateCastDateToNumber(dt); + } else if ((dur != NULL) && (dur->mon == 0)) { + ret = (double)dur->day * SECS_PER_DAY + dur->sec; + } - exsltDateFreeDate(dt); + if (dt != NULL) + exsltDateFreeDate(dt); + if (dur != NULL) + exsltDateFreeDuration(dur); return ret; } @@ -2993,8 +3035,9 @@ exsltDateSeconds (const xmlChar *dateTime) static xmlChar * exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr) { - exsltDateValPtr x, y, dur; - xmlChar *ret = NULL; + exsltDateValPtr x, y; + exsltDateDurValPtr dur; + xmlChar *ret = NULL; if ((xstr == NULL) || (ystr == NULL)) return NULL; @@ -3024,8 +3067,8 @@ exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr) if (dur == NULL) return NULL; - ret = exsltDateFormatDuration(&(dur->value.dur)); - exsltDateFreeDate(dur); + ret = exsltDateFormatDuration(dur); + exsltDateFreeDuration(dur); return ret; } @@ -3053,8 +3096,8 @@ exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr) static xmlChar * exsltDateDuration (const xmlChar *number) { - exsltDateValPtr dur; - double secs; + exsltDateDurValPtr dur; + double secs, days; xmlChar *ret; if (number == NULL) @@ -3065,14 +3108,16 @@ exsltDateDuration (const xmlChar *number) if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs))) return NULL; - dur = exsltDateCreateDate(XS_DURATION); + dur = exsltDateCreateDuration(); if (dur == NULL) return NULL; - dur->value.dur.sec = secs; + days = floor(secs / SECS_PER_DAY); + dur->day = (long)days; + dur->sec = secs - days * SECS_PER_DAY; - ret = exsltDateFormatDuration(&(dur->value.dur)); - exsltDateFreeDate(dur); + ret = exsltDateFormatDuration(dur); + exsltDateFreeDuration(dur); return ret; } |