/* * date.c: Implementation of the EXSLT -- Dates and Times module * * References: * http://www.exslt.org/date/date.html * * See Copyright for the status of this software. * * Authors: * Charlie Bozeman * Thomas Broyer * * TODO: * elements: * date-format * functions: * format-date * parse-date * sum */ #define IN_LIBEXSLT #include "libexslt/libexslt.h" #if defined(_WIN32) && !defined (__CYGWIN__) && (!__MINGW32__) #include #else #include "config.h" #endif #if defined(HAVE_LOCALTIME_R) && defined(__GLIBC__) /* _POSIX_SOURCE required by gnu libc */ #ifndef _AIX51 /* but on AIX we're not using gnu libc */ #define _POSIX_SOURCE #endif #endif #include #include #include #include #include #include #include #include "exslt.h" #include #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_MATH_H #include #endif /* needed to get localtime_r on Solaris */ #ifdef __sun #ifndef __EXTENSIONS__ #define __EXTENSIONS__ #endif #endif #ifdef HAVE_TIME_H #include #endif /* * types of date and/or time (from schema datatypes) * somewhat ordered from least specific to most specific (i.e. * most truncated to least truncated). */ typedef enum { EXSLT_UNKNOWN = 0, XS_TIME = 1, /* time is left-truncated */ XS_GDAY = (XS_TIME << 1), XS_GMONTH = (XS_GDAY << 1), XS_GMONTHDAY = (XS_GMONTH | XS_GDAY), 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) } exsltDateType; /* Date value */ 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 */ unsigned int hour :5; /* 0 <= hour <= 23 */ unsigned int min :6; /* 0 <= min <= 59 */ double sec; unsigned int tz_flag :1; /* is tzo explicitely set? */ signed int tzo :12; /* -1440 <= tzo <= 1440 currently only -840 to +840 are needed */ }; /* Duration value */ 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 0 <= sec < SECS_PER_DAY */ }; /**************************************************************** * * * Compat./Port. macros * * * ****************************************************************/ #if defined(HAVE_TIME_H) \ && (defined(HAVE_LOCALTIME) || defined(HAVE_LOCALTIME_R)) \ && (defined(HAVE_GMTIME) || defined(HAVE_GMTIME_R)) \ && defined(HAVE_TIME) #define WITH_TIME #endif /**************************************************************** * * * Convenience macros and functions * * * ****************************************************************/ #define IS_TZO_CHAR(c) \ ((c == 0) || (c == 'Z') || (c == '+') || (c == '-')) #define VALID_ALWAYS(num) (num >= 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)) #define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23)) #define VALID_MIN(min) ((min >= 0) && (min <= 59)) #define VALID_SEC(sec) ((sec >= 0) && (sec < 60)) #define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440)) #define IS_LEAP(y) \ (((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 }; static const unsigned long daysInMonthLeap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; #define MAX_DAYINMONTH(yr,mon) \ (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1]) #define VALID_MDAY(dt) \ (IS_LEAP(dt->year) ? \ (dt->day <= daysInMonthLeap[dt->mon - 1]) : \ (dt->day <= daysInMonth[dt->mon - 1])) #define VALID_DATE(dt) \ (VALID_MONTH(dt->mon) && VALID_MDAY(dt)) /* hour and min structure vals are unsigned, so normal macros give warnings on some compilers. */ #define VALID_TIME(dt) \ ((dt->hour <=23 ) && (dt->min <= 59) && \ VALID_SEC(dt->sec) && VALID_TZO(dt->tzo)) #define VALID_DATETIME(dt) \ (VALID_DATE(dt) && VALID_TIME(dt)) #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 }; static const unsigned long dayInLeapYearByMonth[12] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; #define DAY_IN_YEAR(day, month, year) \ ((IS_LEAP(year) ? \ dayInLeapYearByMonth[month - 1] : \ dayInYearByMonth[month - 1]) + day) /** * _exsltDateParseGYear: * @dt: pointer to a date structure * @str: pointer to the string to analyze * * Parses a xs:gYear without time zone and fills in the appropriate * field of the @dt structure. @str is updated to point just after the * 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 (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str, *firstChar; int isneg = 0, digcnt = 0; if (((*cur < '0') || (*cur > '9')) && (*cur != '-') && (*cur != '+')) return -1; if (*cur == '-') { isneg = 1; cur++; } firstChar = cur; while ((*cur >= '0') && (*cur <= '9')) { if (dt->year >= LONG_MAX / 10) return -1; dt->year = dt->year * 10 + (*cur - '0'); cur++; digcnt++; } /* year must be at least 4 digits (CCYY); over 4 * digits cannot have a leading zero. */ if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0'))) return 1; 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 %04ld\n", dt->year); #endif return 0; } /** * FORMAT_GYEAR: * @yr: the year to format * @cur: a pointer to an allocated buffer * * Formats @yr in xsl:gYear format. Result is appended to @cur and * @cur is updated to point after the xsl:gYear. */ #define FORMAT_GYEAR(yr, cur) \ if (yr <= 0) { \ *cur = '-'; \ cur++; \ } \ { \ long year = (yr <= 0) ? -yr + 1 : yr; \ xmlChar tmp_buf[100], *tmp = tmp_buf; \ /* result is in reverse-order */ \ while (year > 0) { \ *tmp = '0' + (xmlChar)(year % 10); \ year /= 10; \ tmp++; \ } \ /* virtually adds leading zeros */ \ while ((tmp - tmp_buf) < 4) \ *tmp++ = '0'; \ /* restore the correct order */ \ while (tmp > tmp_buf) { \ tmp--; \ *cur = *tmp; \ cur++; \ } \ } /** * PARSE_2_DIGITS: * @num: the integer to fill in * @cur: an #xmlChar * * @func: validation function for the number * @invalid: an integer * * Parses a 2-digits integer and updates @num with the value. @cur is * updated to point just after the integer. * In case of error, @invalid is set to %TRUE, values of @num and * @cur are undefined. */ #define PARSE_2_DIGITS(num, cur, func, invalid) \ if ((cur[0] < '0') || (cur[0] > '9') || \ (cur[1] < '0') || (cur[1] > '9')) \ invalid = 1; \ else { \ int val; \ val = (cur[0] - '0') * 10 + (cur[1] - '0'); \ if (!func(val)) \ invalid = 2; \ else \ num = val; \ } \ cur += 2; /** * FORMAT_2_DIGITS: * @num: the integer to format * @cur: a pointer to an allocated buffer * * Formats a 2-digits integer. Result is appended to @cur and * @cur is updated to point after the integer. */ #define FORMAT_2_DIGITS(num, cur) \ *cur = '0' + ((num / 10) % 10); \ cur++; \ *cur = '0' + (num % 10); \ cur++; /** * PARSE_FLOAT: * @num: the double to fill in * @cur: an #xmlChar * * @invalid: an integer * * Parses a float and updates @num with the value. @cur is * updated to point just after the float. The float must have a * 2-digits integer part and may or may not have a decimal part. * In case of error, @invalid is set to %TRUE, values of @num and * @cur are undefined. */ #define PARSE_FLOAT(num, cur, invalid) \ PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid); \ if (!invalid && (*cur == '.')) { \ double mult = 1; \ cur++; \ if ((*cur < '0') || (*cur > '9')) \ invalid = 1; \ while ((*cur >= '0') && (*cur <= '9')) { \ mult /= 10; \ num += (*cur - '0') * mult; \ cur++; \ } \ } /** * FORMAT_FLOAT: * @num: the double to format * @cur: a pointer to an allocated buffer * @pad: a flag for padding to 2 integer digits * * Formats a float. Result is appended to @cur and @cur is updated to * point after the integer. If the @pad flag is non-zero, then the * float representation has a minimum 2-digits integer part. The * fractional part is formatted if @num has a fractional value. */ #define FORMAT_FLOAT(num, cur, pad) \ { \ xmlChar *sav, *str; \ if ((pad) && (num < 10.0)) \ *cur++ = '0'; \ str = xmlXPathCastNumberToString(num); \ sav = str; \ while (*str != 0) \ *cur++ = *str++; \ xmlFree(sav); \ } /** * _exsltDateParseGMonth: * @dt: pointer to a date structure * @str: pointer to the string to analyze * * Parses a xs:gMonth without time zone and fills in the appropriate * field of the @dt structure. @str is updated to point just after the * xs:gMonth. * * Returns 0 or the error code */ static int _exsltDateParseGMonth (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str; int ret = 0; PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret); if (ret != 0) return ret; *str = cur; #ifdef DEBUG_EXSLT_DATE xsltGenericDebug(xsltGenericDebugContext, "Parsed month %02i\n", dt->mon); #endif return 0; } /** * FORMAT_GMONTH: * @mon: the month to format * @cur: a pointer to an allocated buffer * * Formats @mon in xsl:gMonth format. Result is appended to @cur and * @cur is updated to point after the xsl:gMonth. */ #define FORMAT_GMONTH(mon, cur) \ FORMAT_2_DIGITS(mon, cur) /** * _exsltDateParseGDay: * @dt: pointer to a date structure * @str: pointer to the string to analyze * * Parses a xs:gDay without time zone and fills in the appropriate * field of the @dt structure. @str is updated to point just after the * xs:gDay. * * Returns 0 or the error code */ static int _exsltDateParseGDay (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str; int ret = 0; PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret); if (ret != 0) return ret; *str = cur; #ifdef DEBUG_EXSLT_DATE xsltGenericDebug(xsltGenericDebugContext, "Parsed day %02i\n", dt->day); #endif return 0; } /** * FORMAT_GDAY: * @dt: the #exsltDateVal to format * @cur: a pointer to an allocated buffer * * Formats @dt in xsl:gDay format. Result is appended to @cur and * @cur is updated to point after the xsl:gDay. */ #define FORMAT_GDAY(dt, cur) \ FORMAT_2_DIGITS(dt->day, cur) /** * FORMAT_DATE: * @dt: the #exsltDateVal to format * @cur: a pointer to an allocated buffer * * Formats @dt in xsl:date format. Result is appended to @cur and * @cur is updated to point after the xsl:date. */ #define FORMAT_DATE(dt, cur) \ FORMAT_GYEAR(dt->year, cur); \ *cur = '-'; \ cur++; \ FORMAT_GMONTH(dt->mon, cur); \ *cur = '-'; \ cur++; \ FORMAT_GDAY(dt, cur); /** * _exsltDateParseTime: * @dt: pointer to a date structure * @str: pointer to the string to analyze * * Parses a xs:time without time zone and fills in the appropriate * fields of the @dt structure. @str is updated to point just after the * xs:time. * In case of error, values of @dt fields are undefined. * * Returns 0 or the error code */ static int _exsltDateParseTime (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur = *str; unsigned int hour = 0; /* use temp var in case str is not xs:time */ int ret = 0; PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret); if (ret != 0) return ret; if (*cur != ':') return 1; cur++; /* the ':' insures this string is xs:time */ dt->hour = hour; PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret); if (ret != 0) return ret; if (*cur != ':') return 1; cur++; PARSE_FLOAT(dt->sec, cur, ret); if (ret != 0) return ret; if (!VALID_TIME(dt)) return 2; *str = cur; #ifdef DEBUG_EXSLT_DATE xsltGenericDebug(xsltGenericDebugContext, "Parsed time %02i:%02i:%02.f\n", dt->hour, dt->min, dt->sec); #endif return 0; } /** * FORMAT_TIME: * @dt: the #exsltDateVal to format * @cur: a pointer to an allocated buffer * * Formats @dt in xsl:time format. Result is appended to @cur and * @cur is updated to point after the xsl:time. */ #define FORMAT_TIME(dt, cur) \ FORMAT_2_DIGITS(dt->hour, cur); \ *cur = ':'; \ cur++; \ FORMAT_2_DIGITS(dt->min, cur); \ *cur = ':'; \ cur++; \ FORMAT_FLOAT(dt->sec, cur, 1); /** * _exsltDateParseTimeZone: * @dt: pointer to a date structure * @str: pointer to the string to analyze * * Parses a time zone without time zone and fills in the appropriate * field of the @dt structure. @str is updated to point just after the * time zone. * * Returns 0 or the error code */ static int _exsltDateParseTimeZone (exsltDateValPtr dt, const xmlChar **str) { const xmlChar *cur; int ret = 0; if (str == NULL) return -1; cur = *str; switch (*cur) { case 0: dt->tz_flag = 0; dt->tzo = 0; break; case 'Z': dt->tz_flag = 1; dt->tzo = 0; cur++; break; case '+': case '-': { int isneg = 0, tmp = 0; isneg = (*cur == '-'); cur++; PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret); if (ret != 0) return ret; if (*cur != ':') return 1; cur++; dt->tzo = tmp * 60; PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret); if (ret != 0) return ret; dt->tzo += tmp; if (isneg) dt->tzo = - dt->tzo; if (!VALID_TZO(dt->tzo)) return 2; break; } default: return 1; } *str = cur; #ifdef DEBUG_EXSLT_DATE xsltGenericDebug(xsltGenericDebugContext, "Parsed time zone offset (%s) %i\n", dt->tz_flag ? "explicit" : "implicit", dt->tzo); #endif return 0; } /** * FORMAT_TZ: * @tzo: the timezone offset to format * @cur: a pointer to an allocated buffer * * Formats @tzo timezone. Result is appended to @cur and * @cur is updated to point after the timezone. */ #define FORMAT_TZ(tzo, cur) \ if (tzo == 0) { \ *cur = 'Z'; \ cur++; \ } else { \ int aTzo = (tzo < 0) ? - tzo : tzo; \ int tzHh = aTzo / 60, tzMm = aTzo % 60; \ *cur = (tzo < 0) ? '-' : '+' ; \ cur++; \ FORMAT_2_DIGITS(tzHh, cur); \ *cur = ':'; \ cur++; \ FORMAT_2_DIGITS(tzMm, cur); \ } /**************************************************************** * * * XML Schema Dates/Times Datatypes Handling * * * ****************************************************************/ /** * exsltDateCreateDate: * @type: type to create * * Creates a new #exsltDateVal, uninitialized. * * Returns the #exsltDateValPtr */ static exsltDateValPtr exsltDateCreateDate (exsltDateType type) { exsltDateValPtr ret; ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal)); if (ret == NULL) { xsltGenericError(xsltGenericErrorContext, "exsltDateCreateDate: out of memory\n"); return (NULL); } memset (ret, 0, sizeof(exsltDateVal)); ret->mon = 1; ret->day = 1; if (type != EXSLT_UNKNOWN) ret->type = type; return ret; } /** * exsltDateFreeDate: * @date: an #exsltDateValPtr * * Frees up the @date */ static void exsltDateFreeDate (exsltDateValPtr date) { if (date == NULL) return; xmlFree(date); } /** * exsltDateCreateDuration: * * Creates a new #exsltDateDurVal, uninitialized. * * Returns the #exsltDateDurValPtr */ 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; } /** * exsltDateFreeDuration: * @date: an #exsltDateDurValPtr * * Frees up the @duration */ static void exsltDateFreeDuration (exsltDateDurValPtr duration) { if (duration == NULL) return; xmlFree(duration); } #ifdef WITH_TIME /** * exsltDateCurrent: * * Returns the current date and time. */ static exsltDateValPtr exsltDateCurrent (void) { struct tm localTm, gmTm; #ifndef HAVE_GMTIME_R struct tm *tb = NULL; #endif time_t secs; int local_s, gm_s; exsltDateValPtr ret; #ifdef HAVE_ERRNO_H char *source_date_epoch; #endif /* HAVE_ERRNO_H */ int override = 0; ret = exsltDateCreateDate(XS_DATETIME); if (ret == NULL) return NULL; #ifdef HAVE_ERRNO_H /* * Allow the date and time to be set externally by an exported * environment variable to enable reproducible builds. */ source_date_epoch = getenv("SOURCE_DATE_EPOCH"); if (source_date_epoch) { errno = 0; secs = (time_t) strtol (source_date_epoch, NULL, 10); if (errno == 0) { #if HAVE_GMTIME_R if (gmtime_r(&secs, &localTm) != NULL) override = 1; #else tb = gmtime(&secs); if (tb != NULL) { localTm = *tb; override = 1; } #endif } } #endif /* HAVE_ERRNO_H */ if (override == 0) { /* get current time */ secs = time(NULL); #if HAVE_LOCALTIME_R localtime_r(&secs, &localTm); #else localTm = *localtime(&secs); #endif } /* get real year, not years since 1900 */ ret->year = localTm.tm_year + 1900; 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->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) return NULL; gmTm = *tb; #endif ret->tz_flag = 0; #if 0 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 + localTm.tm_sec; gm_s = gmTm.tm_hour * SECS_PER_HOUR + gmTm.tm_min * SECS_PER_MIN + gmTm.tm_sec; if (localTm.tm_year < gmTm.tm_year) { ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; } else if (localTm.tm_year > gmTm.tm_year) { ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; } else if (localTm.tm_mon < gmTm.tm_mon) { ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; } else if (localTm.tm_mon > gmTm.tm_mon) { ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; } else if (localTm.tm_mday < gmTm.tm_mday) { ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60; } else if (localTm.tm_mday > gmTm.tm_mday) { ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60; } else { ret->tzo = (local_s - gm_s)/60; } return ret; } #endif /** * exsltDateParse: * @dateTime: string to analyze * * Parses a date/time string * * Returns a newly built #exsltDateValPtr of NULL in case of error */ static exsltDateValPtr exsltDateParse (const xmlChar *dateTime) { exsltDateValPtr dt; int ret; const xmlChar *cur = dateTime; #define RETURN_TYPE_IF_VALID(t) \ if (IS_TZO_CHAR(*cur)) { \ ret = _exsltDateParseTimeZone(dt, &cur); \ if (ret == 0) { \ if (*cur != 0) \ goto error; \ dt->type = t; \ return dt; \ } \ } if (dateTime == NULL) return NULL; if ((*cur != '-') && (*cur < '0') && (*cur > '9')) return NULL; dt = exsltDateCreateDate(EXSLT_UNKNOWN); if (dt == NULL) return NULL; if ((cur[0] == '-') && (cur[1] == '-')) { /* * It's an incomplete date (xs:gMonthDay, xs:gMonth or * xs:gDay) */ cur += 2; /* is it an xs:gDay? */ if (*cur == '-') { ++cur; ret = _exsltDateParseGDay(dt, &cur); if (ret != 0) goto error; RETURN_TYPE_IF_VALID(XS_GDAY); goto error; } /* * it should be an xs:gMonthDay or xs:gMonth */ ret = _exsltDateParseGMonth(dt, &cur); if (ret != 0) goto error; if (*cur != '-') goto error; cur++; /* is it an xs:gMonth? */ if (*cur == '-') { cur++; RETURN_TYPE_IF_VALID(XS_GMONTH); goto error; } /* it should be an xs:gMonthDay */ ret = _exsltDateParseGDay(dt, &cur); if (ret != 0) goto error; RETURN_TYPE_IF_VALID(XS_GMONTHDAY); goto error; } /* * It's a right-truncated date or an xs:time. * Try to parse an xs:time then fallback on right-truncated dates. */ if ((*cur >= '0') && (*cur <= '9')) { ret = _exsltDateParseTime(dt, &cur); if (ret == 0) { /* it's an xs:time */ RETURN_TYPE_IF_VALID(XS_TIME); } } /* fallback on date parsing */ cur = dateTime; ret = _exsltDateParseGYear(dt, &cur); if (ret != 0) goto error; /* is it an xs:gYear? */ RETURN_TYPE_IF_VALID(XS_GYEAR); if (*cur != '-') goto error; cur++; ret = _exsltDateParseGMonth(dt, &cur); if (ret != 0) goto error; /* is it an xs:gYearMonth? */ RETURN_TYPE_IF_VALID(XS_GYEARMONTH); if (*cur != '-') goto error; cur++; ret = _exsltDateParseGDay(dt, &cur); if ((ret != 0) || !VALID_DATE(dt)) goto error; /* is it an xs:date? */ RETURN_TYPE_IF_VALID(XS_DATE); if (*cur != 'T') goto error; cur++; /* it should be an xs:dateTime */ ret = _exsltDateParseTime(dt, &cur); if (ret != 0) goto error; ret = _exsltDateParseTimeZone(dt, &cur); if ((ret != 0) || (*cur != 0) || !VALID_DATETIME(dt)) goto error; dt->type = XS_DATETIME; return dt; error: if (dt != NULL) exsltDateFreeDate(dt); return NULL; } /** * exsltDateParseDuration: * @duration: string to analyze * * Parses a duration string * * Returns a newly built #exsltDateDurValPtr of NULL in case of error */ static exsltDateDurValPtr exsltDateParseDuration (const xmlChar *duration) { const xmlChar *cur = duration; exsltDateDurValPtr dur; int isneg = 0; unsigned int seq = 0; long days, secs = 0; double sec_frac = 0.0; if (duration == NULL) return NULL; if (*cur == '-') { isneg = 1; cur++; } /* duration must start with 'P' (after sign) */ if (*cur++ != 'P') return NULL; dur = exsltDateCreateDuration(); if (dur == NULL) return NULL; while (*cur != 0) { long num = 0; size_t has_digits = 0; int has_frac = 0; const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'}; /* input string should be empty or invalid date/time item */ if (seq >= sizeof(desig)) goto error; /* T designator must be present for time items */ if (*cur == 'T') { if (seq > 3) goto error; cur++; seq = 3; } else if (seq == 3) goto error; /* Parse integral part. */ while (*cur >= '0' && *cur <= '9') { long digit = *cur - '0'; if (num > LONG_MAX / 10) goto error; num *= 10; if (num > LONG_MAX - digit) goto error; num += digit; has_digits = 1; cur++; } 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++; } } 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->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->sec); #endif return dur; error: if (dur != NULL) exsltDateFreeDuration(dur); return NULL; } /** * FORMAT_ITEM: * @num: number to format * @cur: current location to convert number * @limit: max value * @item: char designator * */ #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: * @dur: an #exsltDateDurValPtr * * Formats the duration. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * exsltDateFormatDuration (const exsltDateDurValPtr dur) { xmlChar buf[100], *cur = buf; double secs, days; double years, months; if (dur == NULL) return NULL; /* quick and dirty check */ if ((dur->sec == 0.0) && (dur->day == 0) && (dur->mon == 0)) return xmlStrdup((xmlChar*)"P0D"); secs = dur->sec; days = (double)dur->day; years = (double)(dur->mon / 12); months = (double)(dur->mon % 12); *cur = '\0'; if (days < 0) { if (secs != 0.0) { secs = SECS_PER_DAY - secs; days += 1; } days = -days; *cur = '-'; } if (years < 0) { years = -years; *cur = '-'; } if (months < 0) { months = -months; *cur = '-'; } if (*cur == '-') cur++; *cur++ = 'P'; if (years != 0.0) { FORMAT_ITEM(years, cur, 1, 'Y'); } if (months != 0.0) { FORMAT_ITEM(months, cur, 1, 'M'); } FORMAT_ITEM(days, cur, 1, 'D'); if (secs > 0.0) { *cur++ = 'T'; } FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H'); FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M'); if (secs > 0.0) { FORMAT_FLOAT(secs, cur, 0); *cur++ = 'S'; } *cur = 0; return xmlStrdup(buf); } /** * exsltDateFormatDateTime: * @dt: an #exsltDateValPtr * * Formats @dt in xs:dateTime format. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * exsltDateFormatDateTime (const exsltDateValPtr dt) { xmlChar buf[100], *cur = buf; if ((dt == NULL) || !VALID_DATETIME(dt)) return NULL; FORMAT_DATE(dt, cur); *cur = 'T'; cur++; FORMAT_TIME(dt, cur); FORMAT_TZ(dt->tzo, cur); *cur = 0; return xmlStrdup(buf); } /** * exsltDateFormatDate: * @dt: an #exsltDateValPtr * * Formats @dt in xs:date format. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * exsltDateFormatDate (const exsltDateValPtr dt) { xmlChar buf[100], *cur = buf; if ((dt == NULL) || !VALID_DATETIME(dt)) return NULL; FORMAT_DATE(dt, cur); if (dt->tz_flag || (dt->tzo != 0)) { FORMAT_TZ(dt->tzo, cur); } *cur = 0; return xmlStrdup(buf); } /** * exsltDateFormatTime: * @dt: an #exsltDateValPtr * * Formats @dt in xs:time format. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * exsltDateFormatTime (const exsltDateValPtr dt) { xmlChar buf[100], *cur = buf; if ((dt == NULL) || !VALID_TIME(dt)) return NULL; FORMAT_TIME(dt, cur); if (dt->tz_flag || (dt->tzo != 0)) { FORMAT_TZ(dt->tzo, cur); } *cur = 0; return xmlStrdup(buf); } /** * exsltDateFormat: * @dt: an #exsltDateValPtr * * Formats @dt in the proper format. * Note: xs:gmonth and xs:gday are not formatted as there are no * routines that output them. * * Returns a newly allocated string, or NULL in case of error */ static xmlChar * exsltDateFormat (const exsltDateValPtr dt) { if (dt == NULL) return NULL; switch (dt->type) { case XS_DATETIME: return exsltDateFormatDateTime(dt); case XS_DATE: return exsltDateFormatDate(dt); case XS_TIME: return exsltDateFormatTime(dt); default: break; } if (dt->type & XS_GYEAR) { xmlChar buf[100], *cur = buf; FORMAT_GYEAR(dt->year, cur); if (dt->type == XS_GYEARMONTH) { *cur = '-'; cur++; FORMAT_GMONTH(dt->mon, cur); } if (dt->tz_flag || (dt->tzo != 0)) { FORMAT_TZ(dt->tzo, cur); } *cur = 0; return xmlStrdup(buf); } return NULL; } /** * _exsltDateCastYMToDays: * @dt: an #exsltDateValPtr * * 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. * * Returns number of days. */ static long _exsltDateCastYMToDays (const exsltDateValPtr dt) { long ret; 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->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; } /** * TIME_TO_NUMBER: * @dt: an #exsltDateValPtr * * Calculates the number of seconds in the time portion of @dt. * * Returns seconds. */ #define TIME_TO_NUMBER(dt) \ ((double)((dt->hour * SECS_PER_HOUR) + \ (dt->min * SECS_PER_MIN)) + dt->sec) /** * _exsltDateTruncateDate: * @dt: an #exsltDateValPtr * @type: dateTime type to set to * * Set @dt to truncated @type. * * Returns 0 success, non-zero otherwise. */ static int _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type) { if (dt == NULL) return 1; if ((type & XS_TIME) != XS_TIME) { dt->hour = 0; dt->min = 0; dt->sec = 0.0; } if ((type & XS_GDAY) != XS_GDAY) dt->day = 1; if ((type & XS_GMONTH) != XS_GMONTH) dt->mon = 1; if ((type & XS_GYEAR) != XS_GYEAR) dt->year = 0; dt->type = type; return 0; } /** * _exsltDayInWeek: * @yday: year day (1-366) * @yr: year * * Determine the day-in-week from @yday and @yr. 0001-01-01 was * 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. * * Returns day in week (Sunday = 0). */ static long _exsltDateDayInWeek(long yday, long yr) { long ret; if (yr <= 0) { ret = ((yr-2 + ((yr/4)-(yr/100)+(yr/400)) + yday) % 7); if (ret < 0) ret += 7; } else ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7); return ret; } /** * _exsltDateAdd: * @dt: an #exsltDateValPtr * @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. * * Returns date/time pointer or NULL. */ static exsltDateValPtr _exsltDateAdd (exsltDateValPtr dt, exsltDateDurValPtr dur) { exsltDateValPtr ret; long carry, temp; double sum; if ((dt == NULL) || (dur == NULL)) return NULL; ret = exsltDateCreateDate(dt->type); if (ret == NULL) return NULL; /* * Note that temporary values may need more bits than the values in * bit field. */ /* month */ 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) * * 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 */ ret->tzo = dt->tzo; ret->tz_flag = dt->tz_flag; /* seconds */ sum = dt->sec + dur->sec; ret->sec = fmod(sum, 60.0); carry = (long)(sum / 60.0); /* minute */ temp = dt->min + carry % 60; carry = carry / 60; if (temp >= 60) { temp -= 60; carry += 1; } ret->min = temp; /* hours */ temp = dt->hour + carry % 24; carry = carry / 24; if (temp >= 24) { temp -= 24; carry += 1; } ret->hour = temp; /* 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 temp = dt->day; temp += dur->day % DAYS_PER_EPOCH + carry; while (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; } ret->day = temp; /* * adjust the date/time type to the date values */ if (ret->type != XS_DATETIME) { if ((ret->hour) || (ret->min) || (ret->sec)) ret->type = XS_DATETIME; else if (ret->type != XS_DATE) { if (ret->day != 1) ret->type = XS_DATE; else if ((ret->type != XS_GYEARMONTH) && (ret->mon != 1)) ret->type = XS_GYEARMONTH; } } return ret; } /** * _exsltDateDifference: * @x: an #exsltDateValPtr * @y: an #exsltDateValPtr * @flag: force difference in days * * Calculate the difference between @x and @y as a duration * (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 a duration pointer or NULL. */ static exsltDateDurValPtr _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag) { exsltDateDurValPtr ret; if ((x == NULL) || (y == NULL)) return NULL; if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) || ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) return NULL; /* * the operand with the most specific format must be converted to * the same type as the operand with the least specific format. */ if (x->type != y->type) { if (x->type < y->type) { _exsltDateTruncateDate(y, x->type); } else { _exsltDateTruncateDate(x, y->type); } } ret = exsltDateCreateDuration(); if (ret == NULL) return NULL; if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) { /* compute the difference in months */ 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 { 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; } /** * _exsltDateAddDurCalc * @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. * * Returns 1 for success, 0 if error detected. */ static int _exsltDateAddDurCalc (exsltDateDurValPtr ret, exsltDateDurValPtr x, exsltDateDurValPtr y) { /* months */ 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 */ 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->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 #exsltDateDurValPtr * @y: an #exsltDateDurValPtr * * Compute a new duration from @x and @y. * * Returns a duration pointer or NULL. */ static exsltDateDurValPtr _exsltDateAddDuration (exsltDateDurValPtr x, exsltDateDurValPtr y) { exsltDateDurValPtr ret; if ((x == NULL) || (y == NULL)) return NULL; ret = exsltDateCreateDuration(); if (ret == NULL) return NULL; if (_exsltDateAddDurCalc(ret, x, y)) return ret; exsltDateFreeDuration(ret); return NULL; } /**************************************************************** * * * EXSLT - Dates and Times functions * * * ****************************************************************/ /** * exsltDateDateTime: * * Implements the EXSLT - Dates and Times date-time() function: * string date:date-time() * * Returns the current date and time as a date/time string. */ static xmlChar * exsltDateDateTime (void) { xmlChar *ret = NULL; #ifdef WITH_TIME exsltDateValPtr cur; cur = exsltDateCurrent(); if (cur != NULL) { ret = exsltDateFormatDateTime(cur); exsltDateFreeDate(cur); } #endif return ret; } /** * exsltDateDate: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times date() function: * string date:date (string?) * * Returns the date specified in the date/time string given as the * argument. If no argument is given, then the current local * date/time, as returned by date:date-time is used as a default * argument. * The date/time string specified as an argument must be a string in * the format defined as the lexical representation of either * xs:dateTime or xs:date. If the argument is not in either of these * formats, returns NULL. */ static xmlChar * exsltDateDate (const xmlChar *dateTime) { exsltDateValPtr dt = NULL; xmlChar *ret = NULL; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return NULL; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return NULL; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) { exsltDateFreeDate(dt); return NULL; } } ret = exsltDateFormatDate(dt); exsltDateFreeDate(dt); return ret; } /** * exsltDateTime: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times time() function: * string date:time (string?) * * Returns the time specified in the date/time string given as the * argument. If no argument is given, then the current local * date/time, as returned by date:date-time is used as a default * argument. * The date/time string specified as an argument must be a string in * the format defined as the lexical representation of either * xs:dateTime or xs:time. If the argument is not in either of these * formats, returns NULL. */ static xmlChar * exsltDateTime (const xmlChar *dateTime) { exsltDateValPtr dt = NULL; xmlChar *ret = NULL; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return NULL; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return NULL; if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) { exsltDateFreeDate(dt); return NULL; } } ret = exsltDateFormatTime(dt); exsltDateFreeDate(dt); return ret; } /** * exsltDateYear: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times year() function * number date:year (string?) * Returns the year of a date as a number. If no argument is given, * then the current local date/time, as returned by date:date-time is * used as a default argument. * The date/time string specified as the first argument must be a * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gYear (CCYY) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateYear (const xmlChar *dateTime) { exsltDateValPtr dt; long year; double ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) && (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } year = dt->year; if (year <= 0) year -= 1; /* Adjust for missing year 0. */ ret = (double) year; exsltDateFreeDate(dt); return ret; } /** * exsltDateLeapYear: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times leap-year() function: * boolean date:leap-yea (string?) * Returns true if the year given in a date is a leap year. If no * argument is given, then the current local date/time, as returned by * date:date-time is used as a default argument. * The date/time string specified as the first argument must be a * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gYear (CCYY) * If the date/time string is not in one of these formats, then NaN is * returned. */ static xmlXPathObjectPtr exsltDateLeapYear (const xmlChar *dateTime) { exsltDateValPtr dt = NULL; xmlXPathObjectPtr ret; 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 (dt == NULL) { ret = xmlXPathNewFloat(xmlXPathNAN); } else { ret = xmlXPathNewBoolean(IS_LEAP(dt->year)); exsltDateFreeDate(dt); } return ret; } /** * exsltDateMonthInYear: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times month-in-year() function: * number date:month-in-year (string?) * Returns the month of a date as a number. If no argument is given, * then the current local date/time, as returned by date:date-time is * used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gMonth (--MM--) * - xs:gMonthDay (--MM-DD) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateMonthInYear (const xmlChar *dateTime) { exsltDateValPtr dt; double ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) && (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) && (dt->type != XS_GMONTHDAY)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = (double) dt->mon; exsltDateFreeDate(dt); return ret; } /** * exsltDateMonthName: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Time month-name() function * string date:month-name (string?) * Returns the full name of the month of a date. If no argument is * given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gMonth (--MM--) * If the date/time string is not in one of these formats, then an * empty string ('') is returned. * The result is an English month name: one of 'January', 'February', * 'March', 'April', 'May', 'June', 'July', 'August', 'September', * 'October', 'November' or 'December'. */ static const xmlChar * exsltDateMonthName (const xmlChar *dateTime) { static const xmlChar monthNames[13][10] = { { 0 }, { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 }, { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 }, { 'M', 'a', 'r', 'c', 'h', 0 }, { 'A', 'p', 'r', 'i', 'l', 0 }, { 'M', 'a', 'y', 0 }, { 'J', 'u', 'n', 'e', 0 }, { 'J', 'u', 'l', 'y', 0 }, { 'A', 'u', 'g', 'u', 's', 't', 0 }, { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 }, { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 }, { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 }, { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 } }; double month; int index = 0; month = exsltDateMonthInYear(dateTime); if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0)) index = (int) month; return monthNames[index]; } /** * exsltDateMonthAbbreviation: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Time month-abbreviation() function * string date:month-abbreviation (string?) * Returns the abbreviation of the month of a date. If no argument is * given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gMonth (--MM--) * If the date/time string is not in one of these formats, then an * empty string ('') is returned. * The result is an English month abbreviation: one of 'Jan', 'Feb', * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or * 'Dec'. */ static const xmlChar * exsltDateMonthAbbreviation (const xmlChar *dateTime) { static const xmlChar monthAbbreviations[13][4] = { { 0 }, { 'J', 'a', 'n', 0 }, { 'F', 'e', 'b', 0 }, { 'M', 'a', 'r', 0 }, { 'A', 'p', 'r', 0 }, { 'M', 'a', 'y', 0 }, { 'J', 'u', 'n', 0 }, { 'J', 'u', 'l', 0 }, { 'A', 'u', 'g', 0 }, { 'S', 'e', 'p', 0 }, { 'O', 'c', 't', 0 }, { 'N', 'o', 'v', 0 }, { 'D', 'e', 'c', 0 } }; double month; int index = 0; month = exsltDateMonthInYear(dateTime); if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0)) index = (int) month; return monthAbbreviations[index]; } /** * exsltDateWeekInYear: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times week-in-year() function * number date:week-in-year (string?) * Returns the week of the year as a number. If no argument is given, * then the current local date/time, as returned by date:date-time is * used as the default argument. For the purposes of numbering, * counting follows ISO 8601: week 1 in a year is the week containing * the first Thursday of the year, with new weeks beginning on a * Monday. * The date/time string specified as the argument is a right-truncated * string in the format defined as the lexical representation of * xs:dateTime in one of the formats defined in [XML Schema Part 2: * Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateWeekInYear (const xmlChar *dateTime) { exsltDateValPtr dt; long diy, diw, year, ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } 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->year) + 6) % 7; /* ISO 8601 adjustment, 3 is Thu */ diy += (3 - diw); if(diy < 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->year)) { diy -= DAY_IN_YEAR(31, 12, dt->year); } ret = ((diy - 1) / 7) + 1; exsltDateFreeDate(dt); return (double) ret; } /** * exsltDateWeekInMonth: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times week-in-month() function * number date:week-in-month (string?) * The date:week-in-month function returns the week in a month of a * date as a number. If no argument is given, then the current local * date/time, as returned by date:date-time is used the default * argument. For the purposes of numbering, the first day of the month * is in week 1 and new weeks begin on a Monday (so the first and last * weeks in a month will often have less than 7 days in them). * The date/time string specified as the argument is a right-truncated * string in the format defined as the lexical representation of * xs:dateTime in one of the formats defined in [XML Schema Part 2: * Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateWeekInMonth (const xmlChar *dateTime) { exsltDateValPtr dt; long fdiy, fdiw, ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } 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->year) + 6) % 7; ret = ((dt->day + fdiw - 1) / 7) + 1; exsltDateFreeDate(dt); return (double) ret; } /** * exsltDateDayInYear: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times day-in-year() function * number date:day-in-year (string?) * Returns the day of a date in a year as a number. If no argument is * given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a right-truncated * string in the format defined as the lexical representation of * xs:dateTime in one of the formats defined in [XML Schema Part 2: * Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateDayInYear (const xmlChar *dateTime) { exsltDateValPtr dt; long ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = DAY_IN_YEAR(dt->day, dt->mon, dt->year); exsltDateFreeDate(dt); return (double) ret; } /** * exsltDateDayInMonth: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times day-in-month() function: * number date:day-in-month (string?) * Returns the day of a date as a number. If no argument is given, * then the current local date/time, as returned by date:date-time is * used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gMonthDay (--MM-DD) * - xs:gDay (---DD) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateDayInMonth (const xmlChar *dateTime) { exsltDateValPtr dt; double ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) && (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = (double) dt->day; exsltDateFreeDate(dt); return ret; } /** * exsltDateDayOfWeekInMonth: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times day-of-week-in-month() function: * number date:day-of-week-in-month (string?) * Returns the day-of-the-week in a month of a date as a number * (e.g. 3 for the 3rd Tuesday in May). If no argument is * given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a right-truncated * string in the format defined as the lexical representation of * xs:dateTime in one of the formats defined in [XML Schema Part 2: * Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateDayOfWeekInMonth (const xmlChar *dateTime) { exsltDateValPtr dt; long ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = ((dt->day -1) / 7) + 1; exsltDateFreeDate(dt); return (double) ret; } /** * exsltDateDayInWeek: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times day-in-week() function: * number date:day-in-week (string?) * Returns the day of the week given in a date as a number. If no * argument is given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then NaN is * returned. * The numbering of days of the week starts at 1 for Sunday, 2 for * Monday and so on up to 7 for Saturday. */ static double exsltDateDayInWeek (const xmlChar *dateTime) { exsltDateValPtr dt; long diy, ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year); ret = _exsltDateDayInWeek(diy, dt->year) + 1; exsltDateFreeDate(dt); return (double) ret; } /** * exsltDateDayName: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Time day-name() function * string date:day-name (string?) * Returns the full name of the day of the week of a date. If no * argument is given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then an * empty string ('') is returned. * The result is an English day name: one of 'Sunday', 'Monday', * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'. */ static const xmlChar * exsltDateDayName (const xmlChar *dateTime) { static const xmlChar dayNames[8][10] = { { 0 }, { 'S', 'u', 'n', 'd', 'a', 'y', 0 }, { 'M', 'o', 'n', 'd', 'a', 'y', 0 }, { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 }, { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 }, { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 }, { 'F', 'r', 'i', 'd', 'a', 'y', 0 }, { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 } }; double day; int index = 0; day = exsltDateDayInWeek(dateTime); if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0)) index = (int) day; return dayNames[index]; } /** * exsltDateDayAbbreviation: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Time day-abbreviation() function * string date:day-abbreviation (string?) * Returns the abbreviation of the day of the week of a date. If no * argument is given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * If the date/time string is not in one of these formats, then an * empty string ('') is returned. * The result is a three-letter English day abbreviation: one of * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'. */ static const xmlChar * exsltDateDayAbbreviation (const xmlChar *dateTime) { static const xmlChar dayAbbreviations[8][4] = { { 0 }, { 'S', 'u', 'n', 0 }, { 'M', 'o', 'n', 0 }, { 'T', 'u', 'e', 0 }, { 'W', 'e', 'd', 0 }, { 'T', 'h', 'u', 0 }, { 'F', 'r', 'i', 0 }, { 'S', 'a', 't', 0 } }; double day; int index = 0; day = exsltDateDayInWeek(dateTime); if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0)) index = (int) day; return dayAbbreviations[index]; } /** * exsltDateHourInDay: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times day-in-month() function: * number date:day-in-month (string?) * Returns the hour of the day as a number. If no argument is given, * then the current local date/time, as returned by date:date-time is * used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:time (hh:mm:ss) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateHourInDay (const xmlChar *dateTime) { exsltDateValPtr dt; double ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = (double) dt->hour; exsltDateFreeDate(dt); return ret; } /** * exsltDateMinuteInHour: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times day-in-month() function: * number date:day-in-month (string?) * Returns the minute of the hour as a number. If no argument is * given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:time (hh:mm:ss) * If the date/time string is not in one of these formats, then NaN is * returned. */ static double exsltDateMinuteInHour (const xmlChar *dateTime) { exsltDateValPtr dt; double ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = (double) dt->min; exsltDateFreeDate(dt); return ret; } /** * exsltDateSecondInMinute: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times second-in-minute() function: * number date:day-in-month (string?) * Returns the second of the minute as a number. If no argument is * given, then the current local date/time, as returned by * date:date-time is used the default argument. * The date/time string specified as the argument is a left or * right-truncated string in the format defined as the lexical * representation of xs:dateTime in one of the formats defined in [XML * Schema Part 2: Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:time (hh:mm:ss) * If the date/time string is not in one of these formats, then NaN is * returned. * * Returns the second or NaN. */ static double exsltDateSecondInMinute (const xmlChar *dateTime) { exsltDateValPtr dt; double ret; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) return xmlXPathNAN; if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) { exsltDateFreeDate(dt); return xmlXPathNAN; } } ret = dt->sec; exsltDateFreeDate(dt); return ret; } /** * exsltDateAdd: * @xstr: date/time string * @ystr: date/time string * * Implements the date:add (string,string) function which returns the * date/time * resulting from adding a duration to a date/time. * The first argument (@xstr) must be right-truncated date/time * strings in one of the formats defined in [XML Schema Part 2: * Datatypes]. The permitted formats are as follows: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gYear (CCYY) * The second argument (@ystr) is a string in the format defined for * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. * The return value is a right-truncated date/time strings in one of * the formats defined in [XML Schema Part 2: Datatypes] and listed * above. This value is calculated using the algorithm described in * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2: * Datatypes]. * Returns date/time string or NULL. */ static xmlChar * exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr) { exsltDateValPtr dt, res; exsltDateDurValPtr dur; xmlChar *ret; if ((xstr == NULL) || (ystr == NULL)) return NULL; dt = exsltDateParse(xstr); if (dt == NULL) return NULL; else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) { exsltDateFreeDate(dt); return NULL; } dur = exsltDateParseDuration(ystr); if (dur == NULL) { exsltDateFreeDate(dt); return NULL; } res = _exsltDateAdd(dt, dur); exsltDateFreeDate(dt); exsltDateFreeDuration(dur); if (res == NULL) return NULL; ret = exsltDateFormat(res); exsltDateFreeDate(res); return ret; } /** * exsltDateAddDuration: * @xstr: first duration string * @ystr: second duration string * * Implements the date:add-duration (string,string) function which returns * the duration resulting from adding two durations together. * Both arguments are strings in the format defined for xs:duration * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either * argument is not in this format, the function returns an empty string * (''). * The return value is a string in the format defined for xs:duration * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. * The durations can usually be added by summing the numbers given for * each of the components in the durations. However, if the durations * are differently signed, then this sometimes results in durations * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D'). * In these cases, the function returns an empty string (''). * * Returns duration string or NULL. */ static xmlChar * exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr) { exsltDateDurValPtr x, y, res; xmlChar *ret; if ((xstr == NULL) || (ystr == NULL)) return NULL; x = exsltDateParseDuration(xstr); if (x == NULL) return NULL; y = exsltDateParseDuration(ystr); if (y == NULL) { exsltDateFreeDuration(x); return NULL; } res = _exsltDateAddDuration(x, y); exsltDateFreeDuration(x); exsltDateFreeDuration(y); if (res == NULL) return NULL; ret = exsltDateFormatDuration(res); exsltDateFreeDuration(res); return ret; } /** * exsltDateSumFunction: * @ns: a node set of duration strings * * The date:sum function adds a set of durations together. * The string values of the nodes in the node set passed as an argument * are interpreted as durations and added together as if using the * date:add-duration function. (from exslt.org) * * The return value is a string in the format defined for xs:duration * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. * The durations can usually be added by summing the numbers given for * each of the components in the durations. However, if the durations * are differently signed, then this sometimes results in durations * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D'). * In these cases, the function returns an empty string (''). * * Returns duration string or NULL. */ static void exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlNodeSetPtr ns; void *user = NULL; xmlChar *tmp; exsltDateDurValPtr x, total; xmlChar *ret; int i; if (nargs != 1) { xmlXPathSetArityError (ctxt); return; } /* We need to delay the freeing of value->user */ if ((ctxt->value != NULL) && ctxt->value->boolval != 0) { user = ctxt->value->user; ctxt->value->boolval = 0; ctxt->value->user = NULL; } ns = xmlXPathPopNodeSet (ctxt); if (xmlXPathCheckError (ctxt)) return; if ((ns == NULL) || (ns->nodeNr == 0)) { xmlXPathReturnEmptyString (ctxt); if (ns != NULL) xmlXPathFreeNodeSet (ns); return; } total = exsltDateCreateDuration (); if (total == NULL) { xmlXPathFreeNodeSet (ns); return; } for (i = 0; i < ns->nodeNr; i++) { int result; tmp = xmlXPathCastNodeToString (ns->nodeTab[i]); if (tmp == NULL) { xmlXPathFreeNodeSet (ns); exsltDateFreeDuration (total); return; } x = exsltDateParseDuration (tmp); if (x == NULL) { xmlFree (tmp); exsltDateFreeDuration (total); xmlXPathFreeNodeSet (ns); xmlXPathReturnEmptyString (ctxt); return; } result = _exsltDateAddDurCalc(total, total, x); exsltDateFreeDuration (x); xmlFree (tmp); if (!result) { exsltDateFreeDuration (total); xmlXPathFreeNodeSet (ns); xmlXPathReturnEmptyString (ctxt); return; } } ret = exsltDateFormatDuration (total); exsltDateFreeDuration (total); xmlXPathFreeNodeSet (ns); if (user != NULL) xmlFreeNodeList ((xmlNodePtr) user); if (ret == NULL) xmlXPathReturnEmptyString (ctxt); else xmlXPathReturnString (ctxt, ret); } /** * exsltDateSeconds: * @dateTime: a date/time string * * Implements the EXSLT - Dates and Times seconds() function: * number date:seconds(string?) * The date:seconds function returns the number of seconds specified * by the argument string. If no argument is given, then the current * local date/time, as returned by exsltDateCurrent() is used as the * default argument. If the date/time string is a xs:duration, then the * years and months must be zero (or not present). Parsing a duration * converts the fields to seconds. If the date/time string is not a * duration (and not null), then the legal formats are: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gYear (CCYY) * In these cases the difference between the @dateTime and * 1970-01-01T00:00:00Z is calculated and converted to seconds. * * Note that there was some confusion over whether "difference" meant * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or * a negative one. After correspondence with exslt.org, it was determined * that the intent of the specification was to have it positive. The * coding was modified in July 2003 to reflect this. * * Returns seconds or Nan. */ static double exsltDateSeconds (const xmlChar *dateTime) { exsltDateValPtr dt; exsltDateDurValPtr dur = NULL; double ret = xmlXPathNAN; if (dateTime == NULL) { #ifdef WITH_TIME dt = exsltDateCurrent(); if (dt == NULL) #endif return xmlXPathNAN; } else { dt = exsltDateParse(dateTime); if (dt == NULL) dur = exsltDateParseDuration(dateTime); } if ((dt != NULL) && (dt->type >= XS_GYEAR)) { exsltDateValPtr y; exsltDateDurValPtr diff; /* * compute the difference between the given (or current) date * and epoch date */ y = exsltDateCreateDate(XS_DATETIME); if (y != NULL) { 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 ((dur != NULL) && (dur->mon == 0)) { ret = (double)dur->day * SECS_PER_DAY + dur->sec; } if (dt != NULL) exsltDateFreeDate(dt); if (dur != NULL) exsltDateFreeDuration(dur); return ret; } /** * exsltDateDifference: * @xstr: date/time string * @ystr: date/time string * * Implements the date:difference (string,string) function which returns * the duration between the first date and the second date. If the first * date occurs before the second date, then the result is a positive * duration; if it occurs after the second date, the result is a * negative duration. The two dates must both be right-truncated * date/time strings in one of the formats defined in [XML Schema Part * 2: Datatypes]. The date/time with the most specific format (i.e. the * least truncation) is converted into the same format as the date with * the least specific format (i.e. the most truncation). The permitted * formats are as follows, from most specific to least specific: * - xs:dateTime (CCYY-MM-DDThh:mm:ss) * - xs:date (CCYY-MM-DD) * - xs:gYearMonth (CCYY-MM) * - xs:gYear (CCYY) * If either of the arguments is not in one of these formats, * date:difference returns the empty string (''). * The difference between the date/times is returned as a string in the * format defined for xs:duration in [3.2.6 duration] of [XML Schema * Part 2: Datatypes]. * If the date/time string with the least specific format is in either * xs:gYearMonth or xs:gYear format, then the number of days, hours, * minutes and seconds in the duration string must be equal to zero. * (The format of the string will be PnYnM.) The number of months * specified in the duration must be less than 12. * Otherwise, the number of years and months in the duration string * must be equal to zero. (The format of the string will be * PnDTnHnMnS.) The number of seconds specified in the duration string * must be less than 60; the number of minutes must be less than 60; * the number of hours must be less than 24. * * Returns duration string or NULL. */ static xmlChar * exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr) { exsltDateValPtr x, y; exsltDateDurValPtr dur; xmlChar *ret = NULL; if ((xstr == NULL) || (ystr == NULL)) return NULL; x = exsltDateParse(xstr); if (x == NULL) return NULL; y = exsltDateParse(ystr); if (y == NULL) { exsltDateFreeDate(x); return NULL; } if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) || ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) { exsltDateFreeDate(x); exsltDateFreeDate(y); return NULL; } dur = _exsltDateDifference(x, y, 0); exsltDateFreeDate(x); exsltDateFreeDate(y); if (dur == NULL) return NULL; ret = exsltDateFormatDuration(dur); exsltDateFreeDuration(dur); return ret; } /** * exsltDateDuration: * @number: a xmlChar string * * Implements the The date:duration function returns a duration string * representing the number of seconds specified by the argument string. * If no argument is given, then the result of calling date:seconds * without any arguments is used as a default argument. * The duration is returned as a string in the format defined for * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. * The number of years and months in the duration string must be equal * to zero. (The format of the string will be PnDTnHnMnS.) The number * of seconds specified in the duration string must be less than 60; * the number of minutes must be less than 60; the number of hours must * be less than 24. * If the argument is Infinity, -Infinity or NaN, then date:duration * returns an empty string (''). * * Returns duration string or NULL. */ static xmlChar * exsltDateDuration (const xmlChar *number) { exsltDateDurValPtr dur; double secs, days; xmlChar *ret; if (number == NULL) secs = exsltDateSeconds(number); else secs = xmlXPathCastStringToNumber(number); if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs))) return NULL; dur = exsltDateCreateDuration(); if (dur == NULL) return NULL; days = floor(secs / SECS_PER_DAY); dur->day = (long)days; dur->sec = secs - days * SECS_PER_DAY; ret = exsltDateFormatDuration(dur); exsltDateFreeDuration(dur); return ret; } /**************************************************************** * * * Wrappers for use by the XPath engine * * * ****************************************************************/ #ifdef WITH_TIME /** * exsltDateDateTimeFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDateTime() for use by the XPath engine. */ static void exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret; if (nargs != 0) { xmlXPathSetArityError(ctxt); return; } ret = exsltDateDateTime(); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, ret); } #endif /** * exsltDateDateFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDate() for use by the XPath engine. */ static void exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret, *dt = NULL; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateDate(dt); if (ret == NULL) { xsltGenericDebug(xsltGenericDebugContext, "{http://exslt.org/dates-and-times}date: " "invalid date or format %s\n", dt); xmlXPathReturnEmptyString(ctxt); } else { xmlXPathReturnString(ctxt, ret); } if (dt != NULL) xmlFree(dt); } /** * exsltDateTimeFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateTime() for use by the XPath engine. */ static void exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret, *dt = NULL; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateTime(dt); if (ret == NULL) { xsltGenericDebug(xsltGenericDebugContext, "{http://exslt.org/dates-and-times}time: " "invalid date or format %s\n", dt); xmlXPathReturnEmptyString(ctxt); } else { xmlXPathReturnString(ctxt, ret); } if (dt != NULL) xmlFree(dt); } /** * exsltDateYearFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateYear() for use by the XPath engine. */ static void exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *dt = NULL; double ret; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateYear(dt); if (dt != NULL) xmlFree(dt); xmlXPathReturnNumber(ctxt, ret); } /** * exsltDateLeapYearFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateLeapYear() for use by the XPath engine. */ static void exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *dt = NULL; xmlXPathObjectPtr ret; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateLeapYear(dt); if (dt != NULL) xmlFree(dt); valuePush(ctxt, ret); } #define X_IN_Y(x, y) \ static void \ exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \ int nargs) { \ xmlChar *dt = NULL; \ double ret; \ \ if ((nargs < 0) || (nargs > 1)) { \ xmlXPathSetArityError(ctxt); \ return; \ } \ \ if (nargs == 1) { \ dt = xmlXPathPopString(ctxt); \ if (xmlXPathCheckError(ctxt)) { \ xmlXPathSetTypeError(ctxt); \ return; \ } \ } \ \ ret = exsltDate##x##In##y(dt); \ \ if (dt != NULL) \ xmlFree(dt); \ \ xmlXPathReturnNumber(ctxt, ret); \ } /** * exsltDateMonthInYearFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateMonthInYear() for use by the XPath engine. */ X_IN_Y(Month,Year) /** * exsltDateMonthNameFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateMonthName() for use by the XPath engine. */ static void exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *dt = NULL; const xmlChar *ret; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateMonthName(dt); if (dt != NULL) xmlFree(dt); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, xmlStrdup(ret)); } /** * exsltDateMonthAbbreviationFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateMonthAbbreviation() for use by the XPath engine. */ static void exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *dt = NULL; const xmlChar *ret; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateMonthAbbreviation(dt); if (dt != NULL) xmlFree(dt); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, xmlStrdup(ret)); } /** * exsltDateWeekInYearFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateWeekInYear() for use by the XPath engine. */ X_IN_Y(Week,Year) /** * exsltDateWeekInMonthFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateWeekInMonthYear() for use by the XPath engine. */ X_IN_Y(Week,Month) /** * exsltDateDayInYearFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDayInYear() for use by the XPath engine. */ X_IN_Y(Day,Year) /** * exsltDateDayInMonthFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDayInMonth() for use by the XPath engine. */ X_IN_Y(Day,Month) /** * exsltDateDayOfWeekInMonthFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDayOfWeekInMonth() for use by the XPath engine. */ X_IN_Y(DayOfWeek,Month) /** * exsltDateDayInWeekFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDayInWeek() for use by the XPath engine. */ X_IN_Y(Day,Week) /** * exsltDateDayNameFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDayName() for use by the XPath engine. */ static void exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *dt = NULL; const xmlChar *ret; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateDayName(dt); if (dt != NULL) xmlFree(dt); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, xmlStrdup(ret)); } /** * exsltDateMonthDayFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDayAbbreviation() for use by the XPath engine. */ static void exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *dt = NULL; const xmlChar *ret; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { dt = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateDayAbbreviation(dt); if (dt != NULL) xmlFree(dt); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, xmlStrdup(ret)); } /** * exsltDateHourInDayFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateHourInDay() for use by the XPath engine. */ X_IN_Y(Hour,Day) /** * exsltDateMinuteInHourFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateMinuteInHour() for use by the XPath engine. */ X_IN_Y(Minute,Hour) /** * exsltDateSecondInMinuteFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateSecondInMinute() for use by the XPath engine. */ X_IN_Y(Second,Minute) /** * exsltDateSecondsFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateSeconds() for use by the XPath engine. */ static void exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *str = NULL; double ret; if (nargs > 1) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { str = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateSeconds(str); if (str != NULL) xmlFree(str); xmlXPathReturnNumber(ctxt, ret); } /** * exsltDateAddFunction: * @ctxt: an XPath parser context * @nargs: the number of arguments * * Wraps exsltDateAdd() for use by the XPath processor. */ static void exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret, *xstr, *ystr; if (nargs != 2) { xmlXPathSetArityError(ctxt); return; } ystr = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) return; xstr = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlFree(ystr); return; } ret = exsltDateAdd(xstr, ystr); xmlFree(ystr); xmlFree(xstr); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, ret); } /** * exsltDateAddDurationFunction: * @ctxt: an XPath parser context * @nargs: the number of arguments * * Wraps exsltDateAddDuration() for use by the XPath processor. */ static void exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret, *xstr, *ystr; if (nargs != 2) { xmlXPathSetArityError(ctxt); return; } ystr = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) return; xstr = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlFree(ystr); return; } ret = exsltDateAddDuration(xstr, ystr); xmlFree(ystr); xmlFree(xstr); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, ret); } /** * exsltDateDifferenceFunction: * @ctxt: an XPath parser context * @nargs: the number of arguments * * Wraps exsltDateDifference() for use by the XPath processor. */ static void exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret, *xstr, *ystr; if (nargs != 2) { xmlXPathSetArityError(ctxt); return; } ystr = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) return; xstr = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlFree(ystr); return; } ret = exsltDateDifference(xstr, ystr); xmlFree(ystr); xmlFree(xstr); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, ret); } /** * exsltDateDurationFunction: * @ctxt: an XPath parser context * @nargs : the number of arguments * * Wraps exsltDateDuration() for use by the XPath engine */ static void exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs) { xmlChar *ret; xmlChar *number = NULL; if ((nargs < 0) || (nargs > 1)) { xmlXPathSetArityError(ctxt); return; } if (nargs == 1) { number = xmlXPathPopString(ctxt); if (xmlXPathCheckError(ctxt)) { xmlXPathSetTypeError(ctxt); return; } } ret = exsltDateDuration(number); if (number != NULL) xmlFree(number); if (ret == NULL) xmlXPathReturnEmptyString(ctxt); else xmlXPathReturnString(ctxt, ret); } /** * exsltDateRegister: * * Registers the EXSLT - Dates and Times module */ void exsltDateRegister (void) { xsltRegisterExtModuleFunction ((const xmlChar *) "add", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateAddFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateAddDurationFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "date", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDateFunction); #ifdef WITH_TIME xsltRegisterExtModuleFunction ((const xmlChar *) "date-time", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDateTimeFunction); #endif xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayAbbreviationFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayInMonthFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayInWeekFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayInYearFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "day-name", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayNameFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayOfWeekInMonthFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "difference", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDifferenceFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "duration", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDurationFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateHourInDayFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateLeapYearFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMinuteInHourFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMonthAbbreviationFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMonthInYearFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "month-name", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMonthNameFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateSecondInMinuteFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "seconds", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateSecondsFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "sum", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateSumFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "time", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateTimeFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateWeekInMonthFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateWeekInYearFunction); xsltRegisterExtModuleFunction ((const xmlChar *) "year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateYearFunction); } /** * exsltDateXpathCtxtRegister: * * Registers the EXSLT - Dates and Times module for use outside XSLT */ int exsltDateXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix) { if (ctxt && prefix && !xmlXPathRegisterNs(ctxt, prefix, (const xmlChar *) EXSLT_DATE_NAMESPACE) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "add", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateAddFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "add-duration", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateAddDurationFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "date", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDateFunction) #ifdef WITH_TIME && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "date-time", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDateTimeFunction) #endif && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "day-abbreviation", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayAbbreviationFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "day-in-month", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayInMonthFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "day-in-week", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayInWeekFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "day-in-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayInYearFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "day-name", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayNameFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "day-of-week-in-month", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDayOfWeekInMonthFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "difference", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDifferenceFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "duration", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateDurationFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "hour-in-day", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateHourInDayFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "leap-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateLeapYearFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "minute-in-hour", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMinuteInHourFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "month-abbreviation", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMonthAbbreviationFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "month-in-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMonthInYearFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "month-name", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateMonthNameFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "second-in-minute", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateSecondInMinuteFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "seconds", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateSecondsFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "sum", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateSumFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "time", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateTimeFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "week-in-month", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateWeekInMonthFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "week-in-year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateWeekInYearFunction) && !xmlXPathRegisterFuncNS(ctxt, (const xmlChar *) "year", (const xmlChar *) EXSLT_DATE_NAMESPACE, exsltDateYearFunction)) { return 0; } return -1; }