Home | History | Annotate | Download | only in libexslt
      1 /*
      2  * date.c: Implementation of the EXSLT -- Dates and Times module
      3  *
      4  * References:
      5  *   http://www.exslt.org/date/date.html
      6  *
      7  * See Copyright for the status of this software.
      8  *
      9  * Authors:
     10  *   Charlie Bozeman <cbozeman (at) HiWAAY.net>
     11  *   Thomas Broyer <tbroyer (at) ltgt.net>
     12  *
     13  * TODO:
     14  * elements:
     15  *   date-format
     16  * functions:
     17  *   format-date
     18  *   parse-date
     19  *   sum
     20  */
     21 
     22 #define IN_LIBEXSLT
     23 #include "libexslt/libexslt.h"
     24 
     25 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
     26 #include <win32config.h>
     27 #else
     28 #include "config.h"
     29 #endif
     30 
     31 #if HAVE_LOCALTIME_R	/* _POSIX_SOURCE required by gnu libc */
     32 #ifndef _AIX51		/* but on AIX we're not using gnu libc */
     33 #define _POSIX_SOURCE
     34 #endif
     35 #endif
     36 
     37 #include <libxml/tree.h>
     38 #include <libxml/xpath.h>
     39 #include <libxml/xpathInternals.h>
     40 
     41 #include <libxslt/xsltconfig.h>
     42 #include <libxslt/xsltutils.h>
     43 #include <libxslt/xsltInternals.h>
     44 #include <libxslt/extensions.h>
     45 
     46 #include "exslt.h"
     47 
     48 #include <string.h>
     49 
     50 #ifdef HAVE_MATH_H
     51 #include <math.h>
     52 #endif
     53 
     54 /* needed to get localtime_r on Solaris */
     55 #ifdef __sun
     56 #ifndef __EXTENSIONS__
     57 #define __EXTENSIONS__
     58 #endif
     59 #endif
     60 
     61 #ifdef HAVE_TIME_H
     62 #include <time.h>
     63 #endif
     64 
     65 /*
     66  * types of date and/or time (from schema datatypes)
     67  *   somewhat ordered from least specific to most specific (i.e.
     68  *   most truncated to least truncated).
     69  */
     70 typedef enum {
     71     EXSLT_UNKNOWN  =    0,
     72     XS_TIME        =    1,       /* time is left-truncated */
     73     XS_GDAY        = (XS_TIME   << 1),
     74     XS_GMONTH      = (XS_GDAY   << 1),
     75     XS_GMONTHDAY   = (XS_GMONTH | XS_GDAY),
     76     XS_GYEAR       = (XS_GMONTH << 1),
     77     XS_GYEARMONTH  = (XS_GYEAR  | XS_GMONTH),
     78     XS_DATE        = (XS_GYEAR  | XS_GMONTH | XS_GDAY),
     79     XS_DATETIME    = (XS_DATE   | XS_TIME),
     80     XS_DURATION    = (XS_GYEAR  << 1)
     81 } exsltDateType;
     82 
     83 /* Date value */
     84 typedef struct _exsltDateValDate exsltDateValDate;
     85 typedef exsltDateValDate *exsltDateValDatePtr;
     86 struct _exsltDateValDate {
     87     long		year;
     88     unsigned int	mon	:4;	/* 1 <=  mon    <= 12   */
     89     unsigned int	day	:5;	/* 1 <=  day    <= 31   */
     90     unsigned int	hour	:5;	/* 0 <=  hour   <= 23   */
     91     unsigned int	min	:6;	/* 0 <=  min    <= 59	*/
     92     double		sec;
     93     unsigned int	tz_flag	:1;	/* is tzo explicitely set? */
     94     signed int		tzo	:12;	/* -1440 <= tzo <= 1440 currently only -840 to +840 are needed */
     95 };
     96 
     97 /* Duration value */
     98 typedef struct _exsltDateValDuration exsltDateValDuration;
     99 typedef exsltDateValDuration *exsltDateValDurationPtr;
    100 struct _exsltDateValDuration {
    101     long	        mon;		/* mon stores years also */
    102     long        	day;
    103     double		sec;            /* sec stores min and hour also */
    104 };
    105 
    106 typedef struct _exsltDateVal exsltDateVal;
    107 typedef exsltDateVal *exsltDateValPtr;
    108 struct _exsltDateVal {
    109     exsltDateType       type;
    110     union {
    111         exsltDateValDate        date;
    112         exsltDateValDuration    dur;
    113     } value;
    114 };
    115 
    116 /****************************************************************
    117  *								*
    118  *			Compat./Port. macros			*
    119  *								*
    120  ****************************************************************/
    121 
    122 #if defined(HAVE_TIME_H) 					\
    123     && (defined(HAVE_LOCALTIME) || defined(HAVE_LOCALTIME_R))	\
    124     && (defined(HAVE_GMTIME) || defined(HAVE_GMTIME_R))		\
    125     && defined(HAVE_TIME)
    126 #define WITH_TIME
    127 #endif
    128 
    129 /****************************************************************
    130  *								*
    131  *		Convenience macros and functions		*
    132  *								*
    133  ****************************************************************/
    134 
    135 #define IS_TZO_CHAR(c)						\
    136 	((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
    137 
    138 #define VALID_ALWAYS(num)	(num >= 0)
    139 #define VALID_YEAR(yr)          (yr != 0)
    140 #define VALID_MONTH(mon)        ((mon >= 1) && (mon <= 12))
    141 /* VALID_DAY should only be used when month is unknown */
    142 #define VALID_DAY(day)          ((day >= 1) && (day <= 31))
    143 #define VALID_HOUR(hr)          ((hr >= 0) && (hr <= 23))
    144 #define VALID_MIN(min)          ((min >= 0) && (min <= 59))
    145 #define VALID_SEC(sec)          ((sec >= 0) && (sec < 60))
    146 #define VALID_TZO(tzo)          ((tzo > -1440) && (tzo < 1440))
    147 #define IS_LEAP(y)						\
    148 	(((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
    149 
    150 static const unsigned long daysInMonth[12] =
    151 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    152 static const unsigned long daysInMonthLeap[12] =
    153 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    154 
    155 #define MAX_DAYINMONTH(yr,mon)                                  \
    156         (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
    157 
    158 #define VALID_MDAY(dt)						\
    159 	(IS_LEAP(dt->year) ?				        \
    160 	    (dt->day <= daysInMonthLeap[dt->mon - 1]) :	        \
    161 	    (dt->day <= daysInMonth[dt->mon - 1]))
    162 
    163 #define VALID_DATE(dt)						\
    164 	(VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
    165 
    166 /*
    167     hour and min structure vals are unsigned, so normal macros give
    168     warnings on some compilers.
    169 */
    170 #define VALID_TIME(dt)						\
    171 	((dt->hour <=23 ) && (dt->min <= 59) &&			\
    172 	 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
    173 
    174 #define VALID_DATETIME(dt)					\
    175 	(VALID_DATE(dt) && VALID_TIME(dt))
    176 
    177 #define SECS_PER_MIN            (60)
    178 #define SECS_PER_HOUR           (60 * SECS_PER_MIN)
    179 #define SECS_PER_DAY            (24 * SECS_PER_HOUR)
    180 
    181 static const unsigned long dayInYearByMonth[12] =
    182 	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
    183 static const unsigned long dayInLeapYearByMonth[12] =
    184 	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
    185 
    186 #define DAY_IN_YEAR(day, month, year)				\
    187         ((IS_LEAP(year) ?					\
    188                 dayInLeapYearByMonth[month - 1] :		\
    189                 dayInYearByMonth[month - 1]) + day)
    190 
    191 /**
    192  * _exsltDateParseGYear:
    193  * @dt:  pointer to a date structure
    194  * @str: pointer to the string to analyze
    195  *
    196  * Parses a xs:gYear without time zone and fills in the appropriate
    197  * field of the @dt structure. @str is updated to point just after the
    198  * xs:gYear. It is supposed that @dt->year is big enough to contain
    199  * the year.
    200  *
    201  * Returns 0 or the error code
    202  */
    203 static int
    204 _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
    205 {
    206     const xmlChar *cur = *str, *firstChar;
    207     int isneg = 0, digcnt = 0;
    208 
    209     if (((*cur < '0') || (*cur > '9')) &&
    210 	(*cur != '-') && (*cur != '+'))
    211 	return -1;
    212 
    213     if (*cur == '-') {
    214 	isneg = 1;
    215 	cur++;
    216     }
    217 
    218     firstChar = cur;
    219 
    220     while ((*cur >= '0') && (*cur <= '9')) {
    221 	dt->year = dt->year * 10 + (*cur - '0');
    222 	cur++;
    223 	digcnt++;
    224     }
    225 
    226     /* year must be at least 4 digits (CCYY); over 4
    227      * digits cannot have a leading zero. */
    228     if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
    229 	return 1;
    230 
    231     if (isneg)
    232 	dt->year = - dt->year;
    233 
    234     if (!VALID_YEAR(dt->year))
    235 	return 2;
    236 
    237     *str = cur;
    238 
    239 #ifdef DEBUG_EXSLT_DATE
    240     xsltGenericDebug(xsltGenericDebugContext,
    241 		     "Parsed year %04i\n", dt->year);
    242 #endif
    243 
    244     return 0;
    245 }
    246 
    247 /**
    248  * FORMAT_GYEAR:
    249  * @yr:  the year to format
    250  * @cur: a pointer to an allocated buffer
    251  *
    252  * Formats @yr in xsl:gYear format. Result is appended to @cur and
    253  * @cur is updated to point after the xsl:gYear.
    254  */
    255 #define FORMAT_GYEAR(yr, cur)					\
    256 	if (yr < 0) {					        \
    257 	    *cur = '-';						\
    258 	    cur++;						\
    259 	}							\
    260 	{							\
    261 	    long year = (yr < 0) ? - yr : yr;                   \
    262 	    xmlChar tmp_buf[100], *tmp = tmp_buf;		\
    263 	    /* result is in reverse-order */			\
    264 	    while (year > 0) {					\
    265 		*tmp = '0' + (xmlChar)(year % 10);		\
    266 		year /= 10;					\
    267 		tmp++;						\
    268 	    }							\
    269 	    /* virtually adds leading zeros */			\
    270 	    while ((tmp - tmp_buf) < 4)				\
    271 		*tmp++ = '0';					\
    272 	    /* restore the correct order */			\
    273 	    while (tmp > tmp_buf) {				\
    274 		tmp--;						\
    275 		*cur = *tmp;					\
    276 		cur++;						\
    277 	    }							\
    278 	}
    279 
    280 /**
    281  * PARSE_2_DIGITS:
    282  * @num:  the integer to fill in
    283  * @cur:  an #xmlChar *
    284  * @func: validation function for the number
    285  * @invalid: an integer
    286  *
    287  * Parses a 2-digits integer and updates @num with the value. @cur is
    288  * updated to point just after the integer.
    289  * In case of error, @invalid is set to %TRUE, values of @num and
    290  * @cur are undefined.
    291  */
    292 #define PARSE_2_DIGITS(num, cur, func, invalid)			\
    293 	if ((cur[0] < '0') || (cur[0] > '9') ||			\
    294 	    (cur[1] < '0') || (cur[1] > '9'))			\
    295 	    invalid = 1;					\
    296 	else {							\
    297 	    int val;						\
    298 	    val = (cur[0] - '0') * 10 + (cur[1] - '0');		\
    299 	    if (!func(val))					\
    300 	        invalid = 2;					\
    301 	    else						\
    302 	        num = val;					\
    303 	}							\
    304 	cur += 2;
    305 
    306 /**
    307  * FORMAT_2_DIGITS:
    308  * @num:  the integer to format
    309  * @cur: a pointer to an allocated buffer
    310  *
    311  * Formats a 2-digits integer. Result is appended to @cur and
    312  * @cur is updated to point after the integer.
    313  */
    314 #define FORMAT_2_DIGITS(num, cur)				\
    315 	*cur = '0' + ((num / 10) % 10);				\
    316 	cur++;							\
    317 	*cur = '0' + (num % 10);				\
    318 	cur++;
    319 
    320 /**
    321  * PARSE_FLOAT:
    322  * @num:  the double to fill in
    323  * @cur:  an #xmlChar *
    324  * @invalid: an integer
    325  *
    326  * Parses a float and updates @num with the value. @cur is
    327  * updated to point just after the float. The float must have a
    328  * 2-digits integer part and may or may not have a decimal part.
    329  * In case of error, @invalid is set to %TRUE, values of @num and
    330  * @cur are undefined.
    331  */
    332 #define PARSE_FLOAT(num, cur, invalid)				\
    333 	PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid);	\
    334 	if (!invalid && (*cur == '.')) {			\
    335 	    double mult = 1;				        \
    336 	    cur++;						\
    337 	    if ((*cur < '0') || (*cur > '9'))			\
    338 		invalid = 1;					\
    339 	    while ((*cur >= '0') && (*cur <= '9')) {		\
    340 		mult /= 10;					\
    341 		num += (*cur - '0') * mult;			\
    342 		cur++;						\
    343 	    }							\
    344 	}
    345 
    346 /**
    347  * FORMAT_FLOAT:
    348  * @num:  the double to format
    349  * @cur: a pointer to an allocated buffer
    350  * @pad: a flag for padding to 2 integer digits
    351  *
    352  * Formats a float. Result is appended to @cur and @cur is updated to
    353  * point after the integer. If the @pad flag is non-zero, then the
    354  * float representation has a minimum 2-digits integer part. The
    355  * fractional part is formatted if @num has a fractional value.
    356  */
    357 #define FORMAT_FLOAT(num, cur, pad)				\
    358 	{							\
    359             xmlChar *sav, *str;                                 \
    360             if ((pad) && (num < 10.0))                          \
    361                 *cur++ = '0';                                   \
    362             str = xmlXPathCastNumberToString(num);              \
    363             sav = str;                                          \
    364             while (*str != 0)                                   \
    365                 *cur++ = *str++;                                \
    366             xmlFree(sav);                                       \
    367 	}
    368 
    369 /**
    370  * _exsltDateParseGMonth:
    371  * @dt:  pointer to a date structure
    372  * @str: pointer to the string to analyze
    373  *
    374  * Parses a xs:gMonth without time zone and fills in the appropriate
    375  * field of the @dt structure. @str is updated to point just after the
    376  * xs:gMonth.
    377  *
    378  * Returns 0 or the error code
    379  */
    380 static int
    381 _exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
    382 {
    383     const xmlChar *cur = *str;
    384     int ret = 0;
    385 
    386     PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret);
    387     if (ret != 0)
    388 	return ret;
    389 
    390     *str = cur;
    391 
    392 #ifdef DEBUG_EXSLT_DATE
    393     xsltGenericDebug(xsltGenericDebugContext,
    394 		     "Parsed month %02i\n", dt->mon);
    395 #endif
    396 
    397     return 0;
    398 }
    399 
    400 /**
    401  * FORMAT_GMONTH:
    402  * @mon:  the month to format
    403  * @cur: a pointer to an allocated buffer
    404  *
    405  * Formats @mon in xsl:gMonth format. Result is appended to @cur and
    406  * @cur is updated to point after the xsl:gMonth.
    407  */
    408 #define FORMAT_GMONTH(mon, cur)					\
    409 	FORMAT_2_DIGITS(mon, cur)
    410 
    411 /**
    412  * _exsltDateParseGDay:
    413  * @dt:  pointer to a date structure
    414  * @str: pointer to the string to analyze
    415  *
    416  * Parses a xs:gDay without time zone and fills in the appropriate
    417  * field of the @dt structure. @str is updated to point just after the
    418  * xs:gDay.
    419  *
    420  * Returns 0 or the error code
    421  */
    422 static int
    423 _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
    424 {
    425     const xmlChar *cur = *str;
    426     int ret = 0;
    427 
    428     PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret);
    429     if (ret != 0)
    430 	return ret;
    431 
    432     *str = cur;
    433 
    434 #ifdef DEBUG_EXSLT_DATE
    435     xsltGenericDebug(xsltGenericDebugContext,
    436 		     "Parsed day %02i\n", dt->day);
    437 #endif
    438 
    439     return 0;
    440 }
    441 
    442 /**
    443  * FORMAT_GDAY:
    444  * @dt:  the #exsltDateValDate to format
    445  * @cur: a pointer to an allocated buffer
    446  *
    447  * Formats @dt in xsl:gDay format. Result is appended to @cur and
    448  * @cur is updated to point after the xsl:gDay.
    449  */
    450 #define FORMAT_GDAY(dt, cur)					\
    451 	FORMAT_2_DIGITS(dt->day, cur)
    452 
    453 /**
    454  * FORMAT_DATE:
    455  * @dt:  the #exsltDateValDate to format
    456  * @cur: a pointer to an allocated buffer
    457  *
    458  * Formats @dt in xsl:date format. Result is appended to @cur and
    459  * @cur is updated to point after the xsl:date.
    460  */
    461 #define FORMAT_DATE(dt, cur)					\
    462 	FORMAT_GYEAR(dt->year, cur);				\
    463 	*cur = '-';						\
    464 	cur++;							\
    465 	FORMAT_GMONTH(dt->mon, cur);				\
    466 	*cur = '-';						\
    467 	cur++;							\
    468 	FORMAT_GDAY(dt, cur);
    469 
    470 /**
    471  * _exsltDateParseTime:
    472  * @dt:  pointer to a date structure
    473  * @str: pointer to the string to analyze
    474  *
    475  * Parses a xs:time without time zone and fills in the appropriate
    476  * fields of the @dt structure. @str is updated to point just after the
    477  * xs:time.
    478  * In case of error, values of @dt fields are undefined.
    479  *
    480  * Returns 0 or the error code
    481  */
    482 static int
    483 _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
    484 {
    485     const xmlChar *cur = *str;
    486     unsigned int hour = 0; /* use temp var in case str is not xs:time */
    487     int ret = 0;
    488 
    489     PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret);
    490     if (ret != 0)
    491 	return ret;
    492 
    493     if (*cur != ':')
    494 	return 1;
    495     cur++;
    496 
    497     /* the ':' insures this string is xs:time */
    498     dt->hour = hour;
    499 
    500     PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret);
    501     if (ret != 0)
    502 	return ret;
    503 
    504     if (*cur != ':')
    505 	return 1;
    506     cur++;
    507 
    508     PARSE_FLOAT(dt->sec, cur, ret);
    509     if (ret != 0)
    510 	return ret;
    511 
    512     if (!VALID_TIME(dt))
    513 	return 2;
    514 
    515     *str = cur;
    516 
    517 #ifdef DEBUG_EXSLT_DATE
    518     xsltGenericDebug(xsltGenericDebugContext,
    519 		     "Parsed time %02i:%02i:%02.f\n",
    520 		     dt->hour, dt->min, dt->sec);
    521 #endif
    522 
    523     return 0;
    524 }
    525 
    526 /**
    527  * FORMAT_TIME:
    528  * @dt:  the #exsltDateValDate to format
    529  * @cur: a pointer to an allocated buffer
    530  *
    531  * Formats @dt in xsl:time format. Result is appended to @cur and
    532  * @cur is updated to point after the xsl:time.
    533  */
    534 #define FORMAT_TIME(dt, cur)					\
    535 	FORMAT_2_DIGITS(dt->hour, cur);				\
    536 	*cur = ':';						\
    537 	cur++;							\
    538 	FORMAT_2_DIGITS(dt->min, cur);				\
    539 	*cur = ':';						\
    540 	cur++;							\
    541 	FORMAT_FLOAT(dt->sec, cur, 1);
    542 
    543 /**
    544  * _exsltDateParseTimeZone:
    545  * @dt:  pointer to a date structure
    546  * @str: pointer to the string to analyze
    547  *
    548  * Parses a time zone without time zone and fills in the appropriate
    549  * field of the @dt structure. @str is updated to point just after the
    550  * time zone.
    551  *
    552  * Returns 0 or the error code
    553  */
    554 static int
    555 _exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str)
    556 {
    557     const xmlChar *cur;
    558     int ret = 0;
    559 
    560     if (str == NULL)
    561 	return -1;
    562     cur = *str;
    563     switch (*cur) {
    564     case 0:
    565 	dt->tz_flag = 0;
    566 	dt->tzo = 0;
    567 	break;
    568 
    569     case 'Z':
    570 	dt->tz_flag = 1;
    571 	dt->tzo = 0;
    572 	cur++;
    573 	break;
    574 
    575     case '+':
    576     case '-': {
    577 	int isneg = 0, tmp = 0;
    578 	isneg = (*cur == '-');
    579 
    580 	cur++;
    581 
    582 	PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret);
    583 	if (ret != 0)
    584 	    return ret;
    585 
    586 	if (*cur != ':')
    587 	    return 1;
    588 	cur++;
    589 
    590 	dt->tzo = tmp * 60;
    591 
    592 	PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret);
    593 	if (ret != 0)
    594 	    return ret;
    595 
    596 	dt->tzo += tmp;
    597 	if (isneg)
    598 	    dt->tzo = - dt->tzo;
    599 
    600 	if (!VALID_TZO(dt->tzo))
    601 	    return 2;
    602 
    603 	break;
    604       }
    605     default:
    606 	return 1;
    607     }
    608 
    609     *str = cur;
    610 
    611 #ifdef DEBUG_EXSLT_DATE
    612     xsltGenericDebug(xsltGenericDebugContext,
    613 		     "Parsed time zone offset (%s) %i\n",
    614 		     dt->tz_flag ? "explicit" : "implicit", dt->tzo);
    615 #endif
    616 
    617     return 0;
    618 }
    619 
    620 /**
    621  * FORMAT_TZ:
    622  * @tzo:  the timezone offset to format
    623  * @cur: a pointer to an allocated buffer
    624  *
    625  * Formats @tzo timezone. Result is appended to @cur and
    626  * @cur is updated to point after the timezone.
    627  */
    628 #define FORMAT_TZ(tzo, cur)					\
    629 	if (tzo == 0) {					        \
    630 	    *cur = 'Z';						\
    631 	    cur++;						\
    632 	} else {						\
    633 	    int aTzo = (tzo < 0) ? - tzo : tzo;                 \
    634 	    int tzHh = aTzo / 60, tzMm = aTzo % 60;		\
    635 	    *cur = (tzo < 0) ? '-' : '+' ;			\
    636 	    cur++;						\
    637 	    FORMAT_2_DIGITS(tzHh, cur);				\
    638 	    *cur = ':';						\
    639 	    cur++;						\
    640 	    FORMAT_2_DIGITS(tzMm, cur);				\
    641 	}
    642 
    643 /****************************************************************
    644  *								*
    645  *	XML Schema Dates/Times Datatypes Handling		*
    646  *								*
    647  ****************************************************************/
    648 
    649 /**
    650  * exsltDateCreateDate:
    651  * @type:       type to create
    652  *
    653  * Creates a new #exsltDateVal, uninitialized.
    654  *
    655  * Returns the #exsltDateValPtr
    656  */
    657 static exsltDateValPtr
    658 exsltDateCreateDate (exsltDateType type)
    659 {
    660     exsltDateValPtr ret;
    661 
    662     ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal));
    663     if (ret == NULL) {
    664 	xsltGenericError(xsltGenericErrorContext,
    665 			 "exsltDateCreateDate: out of memory\n");
    666 	return (NULL);
    667     }
    668     memset (ret, 0, sizeof(exsltDateVal));
    669 
    670     if (type != EXSLT_UNKNOWN)
    671         ret->type = type;
    672 
    673     return ret;
    674 }
    675 
    676 /**
    677  * exsltDateFreeDate:
    678  * @date: an #exsltDateValPtr
    679  *
    680  * Frees up the @date
    681  */
    682 static void
    683 exsltDateFreeDate (exsltDateValPtr date) {
    684     if (date == NULL)
    685 	return;
    686 
    687     xmlFree(date);
    688 }
    689 
    690 /**
    691  * PARSE_DIGITS:
    692  * @num:  the integer to fill in
    693  * @cur:  an #xmlChar *
    694  * @num_type: an integer flag
    695  *
    696  * Parses a digits integer and updates @num with the value. @cur is
    697  * updated to point just after the integer.
    698  * In case of error, @num_type is set to -1, values of @num and
    699  * @cur are undefined.
    700  */
    701 #define PARSE_DIGITS(num, cur, num_type)	                \
    702 	if ((*cur < '0') || (*cur > '9'))			\
    703 	    num_type = -1;					\
    704         else                                                    \
    705 	    while ((*cur >= '0') && (*cur <= '9')) {		\
    706 	        num = num * 10 + (*cur - '0');		        \
    707 	        cur++;                                          \
    708             }
    709 
    710 /**
    711  * PARSE_NUM:
    712  * @num:  the double to fill in
    713  * @cur:  an #xmlChar *
    714  * @num_type: an integer flag
    715  *
    716  * Parses a float or integer and updates @num with the value. @cur is
    717  * updated to point just after the number. If the number is a float,
    718  * then it must have an integer part and a decimal part; @num_type will
    719  * be set to 1. If there is no decimal part, @num_type is set to zero.
    720  * In case of error, @num_type is set to -1, values of @num and
    721  * @cur are undefined.
    722  */
    723 #define PARSE_NUM(num, cur, num_type)				\
    724         num = 0;                                                \
    725 	PARSE_DIGITS(num, cur, num_type);	                \
    726 	if (!num_type && (*cur == '.')) {			\
    727 	    double mult = 1;				        \
    728 	    cur++;						\
    729 	    if ((*cur < '0') || (*cur > '9'))			\
    730 		num_type = -1;					\
    731             else                                                \
    732                 num_type = 1;                                   \
    733 	    while ((*cur >= '0') && (*cur <= '9')) {		\
    734 		mult /= 10;					\
    735 		num += (*cur - '0') * mult;			\
    736 		cur++;						\
    737 	    }							\
    738 	}
    739 
    740 #ifdef WITH_TIME
    741 /**
    742  * exsltDateCurrent:
    743  *
    744  * Returns the current date and time.
    745  */
    746 static exsltDateValPtr
    747 exsltDateCurrent (void)
    748 {
    749     struct tm localTm, gmTm;
    750     time_t secs;
    751     int local_s, gm_s;
    752     exsltDateValPtr ret;
    753 
    754     ret = exsltDateCreateDate(XS_DATETIME);
    755     if (ret == NULL)
    756         return NULL;
    757 
    758     /* get current time */
    759     secs    = time(NULL);
    760 #if HAVE_LOCALTIME_R
    761     localtime_r(&secs, &localTm);
    762 #else
    763     localTm = *localtime(&secs);
    764 #endif
    765 
    766     /* get real year, not years since 1900 */
    767     ret->value.date.year = localTm.tm_year + 1900;
    768 
    769     ret->value.date.mon  = localTm.tm_mon + 1;
    770     ret->value.date.day  = localTm.tm_mday;
    771     ret->value.date.hour = localTm.tm_hour;
    772     ret->value.date.min  = localTm.tm_min;
    773 
    774     /* floating point seconds */
    775     ret->value.date.sec  = (double) localTm.tm_sec;
    776 
    777     /* determine the time zone offset from local to gm time */
    778 #if HAVE_GMTIME_R
    779     gmtime_r(&secs, &gmTm);
    780 #else
    781     gmTm = *gmtime(&secs);
    782 #endif
    783     ret->value.date.tz_flag = 0;
    784 #if 0
    785     ret->value.date.tzo = (((ret->value.date.day * 1440) +
    786                             (ret->value.date.hour * 60) +
    787                              ret->value.date.min) -
    788                            ((gmTm.tm_mday * 1440) + (gmTm.tm_hour * 60) +
    789                              gmTm.tm_min));
    790 #endif
    791     local_s = localTm.tm_hour * SECS_PER_HOUR +
    792         localTm.tm_min * SECS_PER_MIN +
    793         localTm.tm_sec;
    794 
    795     gm_s = gmTm.tm_hour * SECS_PER_HOUR +
    796         gmTm.tm_min * SECS_PER_MIN +
    797         gmTm.tm_sec;
    798 
    799     if (localTm.tm_year < gmTm.tm_year) {
    800  	ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
    801     } else if (localTm.tm_year > gmTm.tm_year) {
    802  	ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
    803     } else if (localTm.tm_mon < gmTm.tm_mon) {
    804  	ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
    805     } else if (localTm.tm_mon > gmTm.tm_mon) {
    806  	ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
    807     } else if (localTm.tm_mday < gmTm.tm_mday) {
    808  	ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
    809     } else if (localTm.tm_mday > gmTm.tm_mday) {
    810  	ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
    811     } else  {
    812  	ret->value.date.tzo = (local_s - gm_s)/60;
    813     }
    814 
    815     return ret;
    816 }
    817 #endif
    818 
    819 /**
    820  * exsltDateParse:
    821  * @dateTime:  string to analyze
    822  *
    823  * Parses a date/time string
    824  *
    825  * Returns a newly built #exsltDateValPtr of NULL in case of error
    826  */
    827 static exsltDateValPtr
    828 exsltDateParse (const xmlChar *dateTime)
    829 {
    830     exsltDateValPtr dt;
    831     int ret;
    832     const xmlChar *cur = dateTime;
    833 
    834 #define RETURN_TYPE_IF_VALID(t)					\
    835     if (IS_TZO_CHAR(*cur)) {					\
    836 	ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);	\
    837 	if (ret == 0) {						\
    838 	    if (*cur != 0)					\
    839 		goto error;					\
    840 	    dt->type = t;					\
    841 	    return dt;						\
    842 	}							\
    843     }
    844 
    845     if (dateTime == NULL)
    846 	return NULL;
    847 
    848     if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
    849 	return NULL;
    850 
    851     dt = exsltDateCreateDate(EXSLT_UNKNOWN);
    852     if (dt == NULL)
    853 	return NULL;
    854 
    855     if ((cur[0] == '-') && (cur[1] == '-')) {
    856 	/*
    857 	 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
    858 	 * xs:gDay)
    859 	 */
    860 	cur += 2;
    861 
    862 	/* is it an xs:gDay? */
    863 	if (*cur == '-') {
    864 	  ++cur;
    865 	    ret = _exsltDateParseGDay(&(dt->value.date), &cur);
    866 	    if (ret != 0)
    867 		goto error;
    868 
    869 	    RETURN_TYPE_IF_VALID(XS_GDAY);
    870 
    871 	    goto error;
    872 	}
    873 
    874 	/*
    875 	 * it should be an xs:gMonthDay or xs:gMonth
    876 	 */
    877 	ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
    878 	if (ret != 0)
    879 	    goto error;
    880 
    881 	if (*cur != '-')
    882 	    goto error;
    883 	cur++;
    884 
    885 	/* is it an xs:gMonth? */
    886 	if (*cur == '-') {
    887 	    cur++;
    888 	    RETURN_TYPE_IF_VALID(XS_GMONTH);
    889 	    goto error;
    890 	}
    891 
    892 	/* it should be an xs:gMonthDay */
    893 	ret = _exsltDateParseGDay(&(dt->value.date), &cur);
    894 	if (ret != 0)
    895 	    goto error;
    896 
    897 	RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
    898 
    899 	goto error;
    900     }
    901 
    902     /*
    903      * It's a right-truncated date or an xs:time.
    904      * Try to parse an xs:time then fallback on right-truncated dates.
    905      */
    906     if ((*cur >= '0') && (*cur <= '9')) {
    907 	ret = _exsltDateParseTime(&(dt->value.date), &cur);
    908 	if (ret == 0) {
    909 	    /* it's an xs:time */
    910 	    RETURN_TYPE_IF_VALID(XS_TIME);
    911 	}
    912     }
    913 
    914     /* fallback on date parsing */
    915     cur = dateTime;
    916 
    917     ret = _exsltDateParseGYear(&(dt->value.date), &cur);
    918     if (ret != 0)
    919 	goto error;
    920 
    921     /* is it an xs:gYear? */
    922     RETURN_TYPE_IF_VALID(XS_GYEAR);
    923 
    924     if (*cur != '-')
    925 	goto error;
    926     cur++;
    927 
    928     ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
    929     if (ret != 0)
    930 	goto error;
    931 
    932     /* is it an xs:gYearMonth? */
    933     RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
    934 
    935     if (*cur != '-')
    936 	goto error;
    937     cur++;
    938 
    939     ret = _exsltDateParseGDay(&(dt->value.date), &cur);
    940     if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
    941 	goto error;
    942 
    943     /* is it an xs:date? */
    944     RETURN_TYPE_IF_VALID(XS_DATE);
    945 
    946     if (*cur != 'T')
    947 	goto error;
    948     cur++;
    949 
    950     /* it should be an xs:dateTime */
    951     ret = _exsltDateParseTime(&(dt->value.date), &cur);
    952     if (ret != 0)
    953 	goto error;
    954 
    955     ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
    956     if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
    957 	goto error;
    958 
    959     dt->type = XS_DATETIME;
    960 
    961     return dt;
    962 
    963 error:
    964     if (dt != NULL)
    965 	exsltDateFreeDate(dt);
    966     return NULL;
    967 }
    968 
    969 /**
    970  * exsltDateParseDuration:
    971  * @duration:  string to analyze
    972  *
    973  * Parses a duration string
    974  *
    975  * Returns a newly built #exsltDateValPtr of NULL in case of error
    976  */
    977 static exsltDateValPtr
    978 exsltDateParseDuration (const xmlChar *duration)
    979 {
    980     const xmlChar  *cur = duration;
    981     exsltDateValPtr dur;
    982     int isneg = 0;
    983     unsigned int seq = 0;
    984 
    985     if (duration == NULL)
    986 	return NULL;
    987 
    988     if (*cur == '-') {
    989         isneg = 1;
    990         cur++;
    991     }
    992 
    993     /* duration must start with 'P' (after sign) */
    994     if (*cur++ != 'P')
    995 	return NULL;
    996 
    997     dur = exsltDateCreateDate(XS_DURATION);
    998     if (dur == NULL)
    999 	return NULL;
   1000 
   1001     while (*cur != 0) {
   1002         double         num;
   1003         int            num_type = 0;  /* -1 = invalid, 0 = int, 1 = floating */
   1004         const xmlChar  desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
   1005         const double   multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
   1006 
   1007         /* input string should be empty or invalid date/time item */
   1008         if (seq >= sizeof(desig))
   1009             goto error;
   1010 
   1011         /* T designator must be present for time items */
   1012         if (*cur == 'T') {
   1013             if (seq <= 3) {
   1014                 seq = 3;
   1015                 cur++;
   1016             } else
   1017                 return NULL;
   1018         } else if (seq == 3)
   1019             goto error;
   1020 
   1021         /* parse the number portion of the item */
   1022         PARSE_NUM(num, cur, num_type);
   1023 
   1024         if ((num_type == -1) || (*cur == 0))
   1025             goto error;
   1026 
   1027         /* update duration based on item type */
   1028         while (seq < sizeof(desig)) {
   1029             if (*cur == desig[seq]) {
   1030 
   1031                 /* verify numeric type; only seconds can be float */
   1032                 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
   1033                     goto error;
   1034 
   1035                 switch (seq) {
   1036                     case 0:
   1037                         dur->value.dur.mon = (long)num * 12;
   1038                         break;
   1039                     case 1:
   1040                         dur->value.dur.mon += (long)num;
   1041                         break;
   1042                     default:
   1043                         /* convert to seconds using multiplier */
   1044                         dur->value.dur.sec += num * multi[seq];
   1045                         seq++;
   1046                         break;
   1047                 }
   1048 
   1049                 break;          /* exit loop */
   1050             }
   1051             /* no date designators found? */
   1052             if (++seq == 3)
   1053                 goto error;
   1054         }
   1055         cur++;
   1056     }
   1057 
   1058     if (isneg) {
   1059         dur->value.dur.mon = -dur->value.dur.mon;
   1060         dur->value.dur.day = -dur->value.dur.day;
   1061         dur->value.dur.sec = -dur->value.dur.sec;
   1062     }
   1063 
   1064 #ifdef DEBUG_EXSLT_DATE
   1065     xsltGenericDebug(xsltGenericDebugContext,
   1066 		     "Parsed duration %f\n", dur->value.dur.sec);
   1067 #endif
   1068 
   1069     return dur;
   1070 
   1071 error:
   1072     if (dur != NULL)
   1073 	exsltDateFreeDate(dur);
   1074     return NULL;
   1075 }
   1076 
   1077 /**
   1078  * FORMAT_ITEM:
   1079  * @num:        number to format
   1080  * @cur:        current location to convert number
   1081  * @limit:      max value
   1082  * @item:       char designator
   1083  *
   1084  */
   1085 #define FORMAT_ITEM(num, cur, limit, item)                      \
   1086         if (num != 0) {                                         \
   1087             long comp = (long)num / limit;                      \
   1088             if (comp != 0) {                                    \
   1089                 FORMAT_FLOAT((double)comp, cur, 0);             \
   1090                 *cur++ = item;                                  \
   1091                 num -= (double)(comp * limit);                  \
   1092             }                                                   \
   1093         }
   1094 
   1095 /**
   1096  * exsltDateFormatDuration:
   1097  * @dt: an #exsltDateValDurationPtr
   1098  *
   1099  * Formats @dt in xs:duration format.
   1100  *
   1101  * Returns a newly allocated string, or NULL in case of error
   1102  */
   1103 static xmlChar *
   1104 exsltDateFormatDuration (const exsltDateValDurationPtr dt)
   1105 {
   1106     xmlChar buf[100], *cur = buf;
   1107     double secs, days;
   1108     double years, months;
   1109 
   1110     if (dt == NULL)
   1111 	return NULL;
   1112 
   1113     /* quick and dirty check */
   1114     if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
   1115         return xmlStrdup((xmlChar*)"P0D");
   1116 
   1117     secs   = dt->sec;
   1118     days   = (double)dt->day;
   1119     years  = (double)(dt->mon / 12);
   1120     months = (double)(dt->mon % 12);
   1121 
   1122     *cur = '\0';
   1123     if (secs < 0.0) {
   1124         secs = -secs;
   1125         *cur = '-';
   1126     }
   1127     if (days < 0) {
   1128         days = -days;
   1129         *cur = '-';
   1130     }
   1131     if (years < 0) {
   1132         years = -years;
   1133         *cur = '-';
   1134     }
   1135     if (months < 0) {
   1136         months = -months;
   1137         *cur = '-';
   1138     }
   1139     if (*cur == '-')
   1140 	cur++;
   1141 
   1142     *cur++ = 'P';
   1143 
   1144     if (years != 0.0) {
   1145         FORMAT_ITEM(years, cur, 1, 'Y');
   1146     }
   1147 
   1148     if (months != 0.0) {
   1149         FORMAT_ITEM(months, cur, 1, 'M');
   1150     }
   1151 
   1152     if (secs >= SECS_PER_DAY) {
   1153         double tmp = floor(secs / SECS_PER_DAY);
   1154         days += tmp;
   1155         secs -= (tmp * SECS_PER_DAY);
   1156     }
   1157 
   1158     FORMAT_ITEM(days, cur, 1, 'D');
   1159     if (secs > 0.0) {
   1160         *cur++ = 'T';
   1161     }
   1162     FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
   1163     FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
   1164     if (secs > 0.0) {
   1165         FORMAT_FLOAT(secs, cur, 0);
   1166         *cur++ = 'S';
   1167     }
   1168 
   1169     *cur = 0;
   1170 
   1171     return xmlStrdup(buf);
   1172 }
   1173 
   1174 /**
   1175  * exsltDateFormatDateTime:
   1176  * @dt: an #exsltDateValDatePtr
   1177  *
   1178  * Formats @dt in xs:dateTime format.
   1179  *
   1180  * Returns a newly allocated string, or NULL in case of error
   1181  */
   1182 static xmlChar *
   1183 exsltDateFormatDateTime (const exsltDateValDatePtr dt)
   1184 {
   1185     xmlChar buf[100], *cur = buf;
   1186 
   1187     if ((dt == NULL) ||	!VALID_DATETIME(dt))
   1188 	return NULL;
   1189 
   1190     FORMAT_DATE(dt, cur);
   1191     *cur = 'T';
   1192     cur++;
   1193     FORMAT_TIME(dt, cur);
   1194     FORMAT_TZ(dt->tzo, cur);
   1195     *cur = 0;
   1196 
   1197     return xmlStrdup(buf);
   1198 }
   1199 
   1200 /**
   1201  * exsltDateFormatDate:
   1202  * @dt: an #exsltDateValDatePtr
   1203  *
   1204  * Formats @dt in xs:date format.
   1205  *
   1206  * Returns a newly allocated string, or NULL in case of error
   1207  */
   1208 static xmlChar *
   1209 exsltDateFormatDate (const exsltDateValDatePtr dt)
   1210 {
   1211     xmlChar buf[100], *cur = buf;
   1212 
   1213     if ((dt == NULL) || !VALID_DATETIME(dt))
   1214 	return NULL;
   1215 
   1216     FORMAT_DATE(dt, cur);
   1217     if (dt->tz_flag || (dt->tzo != 0)) {
   1218 	FORMAT_TZ(dt->tzo, cur);
   1219     }
   1220     *cur = 0;
   1221 
   1222     return xmlStrdup(buf);
   1223 }
   1224 
   1225 /**
   1226  * exsltDateFormatTime:
   1227  * @dt: an #exsltDateValDatePtr
   1228  *
   1229  * Formats @dt in xs:time format.
   1230  *
   1231  * Returns a newly allocated string, or NULL in case of error
   1232  */
   1233 static xmlChar *
   1234 exsltDateFormatTime (const exsltDateValDatePtr dt)
   1235 {
   1236     xmlChar buf[100], *cur = buf;
   1237 
   1238     if ((dt == NULL) || !VALID_TIME(dt))
   1239 	return NULL;
   1240 
   1241     FORMAT_TIME(dt, cur);
   1242     if (dt->tz_flag || (dt->tzo != 0)) {
   1243 	FORMAT_TZ(dt->tzo, cur);
   1244     }
   1245     *cur = 0;
   1246 
   1247     return xmlStrdup(buf);
   1248 }
   1249 
   1250 /**
   1251  * exsltDateFormat:
   1252  * @dt: an #exsltDateValPtr
   1253  *
   1254  * Formats @dt in the proper format.
   1255  * Note: xs:gmonth and xs:gday are not formatted as there are no
   1256  * routines that output them.
   1257  *
   1258  * Returns a newly allocated string, or NULL in case of error
   1259  */
   1260 static xmlChar *
   1261 exsltDateFormat (const exsltDateValPtr dt)
   1262 {
   1263 
   1264     if (dt == NULL)
   1265 	return NULL;
   1266 
   1267     switch (dt->type) {
   1268     case XS_DURATION:
   1269         return exsltDateFormatDuration(&(dt->value.dur));
   1270     case XS_DATETIME:
   1271         return exsltDateFormatDateTime(&(dt->value.date));
   1272     case XS_DATE:
   1273         return exsltDateFormatDate(&(dt->value.date));
   1274     case XS_TIME:
   1275         return exsltDateFormatTime(&(dt->value.date));
   1276     default:
   1277         break;
   1278     }
   1279 
   1280     if (dt->type & XS_GYEAR) {
   1281         xmlChar buf[20], *cur = buf;
   1282 
   1283         FORMAT_GYEAR(dt->value.date.year, cur);
   1284         if (dt->type == XS_GYEARMONTH) {
   1285 	    *cur = '-';
   1286 	    cur++;
   1287 	    FORMAT_GMONTH(dt->value.date.mon, cur);
   1288         }
   1289 
   1290         if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
   1291 	    FORMAT_TZ(dt->value.date.tzo, cur);
   1292         }
   1293         *cur = 0;
   1294         return xmlStrdup(buf);
   1295     }
   1296 
   1297     return NULL;
   1298 }
   1299 
   1300 /**
   1301  * _exsltDateCastYMToDays:
   1302  * @dt: an #exsltDateValPtr
   1303  *
   1304  * Convert mon and year of @dt to total number of days. Take the
   1305  * number of years since (or before) 1 AD and add the number of leap
   1306  * years. This is a function  because negative
   1307  * years must be handled a little differently and there is no zero year.
   1308  *
   1309  * Returns number of days.
   1310  */
   1311 static long
   1312 _exsltDateCastYMToDays (const exsltDateValPtr dt)
   1313 {
   1314     long ret;
   1315 
   1316     if (dt->value.date.year < 0)
   1317         ret = (dt->value.date.year * 365) +
   1318               (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
   1319                ((dt->value.date.year+1)/400)) +
   1320               DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
   1321     else
   1322         ret = ((dt->value.date.year-1) * 365) +
   1323               (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
   1324                ((dt->value.date.year-1)/400)) +
   1325               DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
   1326 
   1327     return ret;
   1328 }
   1329 
   1330 /**
   1331  * TIME_TO_NUMBER:
   1332  * @dt:  an #exsltDateValPtr
   1333  *
   1334  * Calculates the number of seconds in the time portion of @dt.
   1335  *
   1336  * Returns seconds.
   1337  */
   1338 #define TIME_TO_NUMBER(dt)                              \
   1339     ((double)((dt->value.date.hour * SECS_PER_HOUR) +   \
   1340               (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
   1341 
   1342 /**
   1343  * exsltDateCastDateToNumber:
   1344  * @dt:  an #exsltDateValPtr
   1345  *
   1346  * Calculates the number of seconds from year zero.
   1347  *
   1348  * Returns seconds from zero year.
   1349  */
   1350 static double
   1351 exsltDateCastDateToNumber (const exsltDateValPtr dt)
   1352 {
   1353     double ret = 0.0;
   1354 
   1355     if (dt == NULL)
   1356         return 0.0;
   1357 
   1358     if ((dt->type & XS_GYEAR) == XS_GYEAR) {
   1359         ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
   1360     }
   1361 
   1362     /* add in days */
   1363     if (dt->type == XS_DURATION) {
   1364         ret += (double)dt->value.dur.day * SECS_PER_DAY;
   1365         ret += dt->value.dur.sec;
   1366     } else {
   1367         ret += (double)dt->value.date.day * SECS_PER_DAY;
   1368         /* add in time */
   1369         ret += TIME_TO_NUMBER(dt);
   1370     }
   1371 
   1372 
   1373     return ret;
   1374 }
   1375 
   1376 /**
   1377  * _exsltDateTruncateDate:
   1378  * @dt: an #exsltDateValPtr
   1379  * @type: dateTime type to set to
   1380  *
   1381  * Set @dt to truncated @type.
   1382  *
   1383  * Returns 0 success, non-zero otherwise.
   1384  */
   1385 static int
   1386 _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
   1387 {
   1388     if (dt == NULL)
   1389         return 1;
   1390 
   1391     if ((type & XS_TIME) != XS_TIME) {
   1392         dt->value.date.hour = 0;
   1393         dt->value.date.min  = 0;
   1394         dt->value.date.sec  = 0.0;
   1395     }
   1396 
   1397     if ((type & XS_GDAY) != XS_GDAY)
   1398         dt->value.date.day = 0;
   1399 
   1400     if ((type & XS_GMONTH) != XS_GMONTH)
   1401         dt->value.date.mon = 0;
   1402 
   1403     if ((type & XS_GYEAR) != XS_GYEAR)
   1404         dt->value.date.year = 0;
   1405 
   1406     dt->type = type;
   1407 
   1408     return 0;
   1409 }
   1410 
   1411 /**
   1412  * _exsltDayInWeek:
   1413  * @yday: year day (1-366)
   1414  * @yr: year
   1415  *
   1416  * Determine the day-in-week from @yday and @yr. 0001-01-01 was
   1417  * a Monday so all other days are calculated from there. Take the
   1418  * number of years since (or before) add the number of leap years and
   1419  * the day-in-year and mod by 7. This is a function  because negative
   1420  * years must be handled a little differently and there is no zero year.
   1421  *
   1422  * Returns day in week (Sunday = 0).
   1423  */
   1424 static long
   1425 _exsltDateDayInWeek(long yday, long yr)
   1426 {
   1427     long ret;
   1428 
   1429     if (yr < 0) {
   1430         ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
   1431         if (ret < 0)
   1432             ret += 7;
   1433     } else
   1434         ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
   1435 
   1436     return ret;
   1437 }
   1438 
   1439 /*
   1440  * macros for adding date/times and durations
   1441  */
   1442 #define FQUOTIENT(a,b)                  ((floor(((double)a/(double)b))))
   1443 #define MODULO(a,b)                     ((a - FQUOTIENT(a,b) * b))
   1444 #define FQUOTIENT_RANGE(a,low,high)     (FQUOTIENT((a-low),(high-low)))
   1445 #define MODULO_RANGE(a,low,high)        ((MODULO((a-low),(high-low)))+low)
   1446 
   1447 /**
   1448  * _exsltDateAdd:
   1449  * @dt: an #exsltDateValPtr
   1450  * @dur: an #exsltDateValPtr of type #XS_DURATION
   1451  *
   1452  * Compute a new date/time from @dt and @dur. This function assumes @dt
   1453  * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
   1454  *
   1455  * Returns date/time pointer or NULL.
   1456  */
   1457 static exsltDateValPtr
   1458 _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
   1459 {
   1460     exsltDateValPtr ret;
   1461     long carry, tempdays, temp;
   1462     exsltDateValDatePtr r, d;
   1463     exsltDateValDurationPtr u;
   1464 
   1465     if ((dt == NULL) || (dur == NULL))
   1466         return NULL;
   1467 
   1468     ret = exsltDateCreateDate(dt->type);
   1469     if (ret == NULL)
   1470         return NULL;
   1471 
   1472     r = &(ret->value.date);
   1473     d = &(dt->value.date);
   1474     u = &(dur->value.dur);
   1475 
   1476     /* normalization */
   1477     if (d->mon == 0)
   1478         d->mon = 1;
   1479 
   1480     /* normalize for time zone offset */
   1481     u->sec -= (d->tzo * 60);	/* changed from + to - (bug 153000) */
   1482     d->tzo = 0;
   1483 
   1484     /* normalization */
   1485     if (d->day == 0)
   1486         d->day = 1;
   1487 
   1488     /* month */
   1489     carry  = d->mon + u->mon;
   1490     r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
   1491     carry  = (long)FQUOTIENT_RANGE(carry, 1, 13);
   1492 
   1493     /* year (may be modified later) */
   1494     r->year = d->year + carry;
   1495     if (r->year == 0) {
   1496         if (d->year > 0)
   1497             r->year--;
   1498         else
   1499             r->year++;
   1500     }
   1501 
   1502     /* time zone */
   1503     r->tzo     = d->tzo;
   1504     r->tz_flag = d->tz_flag;
   1505 
   1506     /* seconds */
   1507     r->sec = d->sec + u->sec;
   1508     carry  = (long)FQUOTIENT((long)r->sec, 60);
   1509     if (r->sec != 0.0) {
   1510         r->sec = MODULO(r->sec, 60.0);
   1511     }
   1512 
   1513     /* minute */
   1514     carry += d->min;
   1515     r->min = (unsigned int)MODULO(carry, 60);
   1516     carry  = (long)FQUOTIENT(carry, 60);
   1517 
   1518     /* hours */
   1519     carry  += d->hour;
   1520     r->hour = (unsigned int)MODULO(carry, 24);
   1521     carry   = (long)FQUOTIENT(carry, 24);
   1522 
   1523     /*
   1524      * days
   1525      * Note we use tempdays because the temporary values may need more
   1526      * than 5 bits
   1527      */
   1528     if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
   1529                   (d->day > MAX_DAYINMONTH(r->year, r->mon)))
   1530         tempdays = MAX_DAYINMONTH(r->year, r->mon);
   1531     else if (d->day < 1)
   1532         tempdays = 1;
   1533     else
   1534         tempdays = d->day;
   1535 
   1536     tempdays += u->day + carry;
   1537 
   1538     while (1) {
   1539         if (tempdays < 1) {
   1540             long tmon = (long)MODULO_RANGE((int)r->mon-1, 1, 13);
   1541             long tyr  = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13);
   1542             if (tyr == 0)
   1543                 tyr--;
   1544 	    /*
   1545 	     * Coverity detected an overrun in daysInMonth
   1546 	     * of size 12 at position 12 with index variable "((r)->mon - 1)"
   1547 	     */
   1548 	    if (tmon < 0)
   1549 	        tmon = 0;
   1550 	    if (tmon > 12)
   1551 	        tmon = 12;
   1552             tempdays += MAX_DAYINMONTH(tyr, tmon);
   1553             carry = -1;
   1554         } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
   1555             tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
   1556             carry = 1;
   1557         } else
   1558             break;
   1559 
   1560         temp = r->mon + carry;
   1561         r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
   1562         r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
   1563         if (r->year == 0) {
   1564             if (temp < 1)
   1565                 r->year--;
   1566             else
   1567                 r->year++;
   1568 	}
   1569     }
   1570 
   1571     r->day = tempdays;
   1572 
   1573     /*
   1574      * adjust the date/time type to the date values
   1575      */
   1576     if (ret->type != XS_DATETIME) {
   1577         if ((r->hour) || (r->min) || (r->sec))
   1578             ret->type = XS_DATETIME;
   1579         else if (ret->type != XS_DATE) {
   1580             if ((r->mon != 1) && (r->day != 1))
   1581                 ret->type = XS_DATE;
   1582             else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
   1583                 ret->type = XS_GYEARMONTH;
   1584         }
   1585     }
   1586 
   1587     return ret;
   1588 }
   1589 
   1590 /**
   1591  * exsltDateNormalize:
   1592  * @dt: an #exsltDateValPtr
   1593  *
   1594  * Normalize @dt to GMT time.
   1595  *
   1596  */
   1597 static void
   1598 exsltDateNormalize (exsltDateValPtr dt)
   1599 {
   1600     exsltDateValPtr dur, tmp;
   1601 
   1602     if (dt == NULL)
   1603         return;
   1604 
   1605     if (((dt->type & XS_TIME) != XS_TIME) || (dt->value.date.tzo == 0))
   1606         return;
   1607 
   1608     dur = exsltDateCreateDate(XS_DURATION);
   1609     if (dur == NULL)
   1610         return;
   1611 
   1612     tmp = _exsltDateAdd(dt, dur);
   1613     if (tmp == NULL)
   1614         return;
   1615 
   1616     memcpy(dt, tmp, sizeof(exsltDateVal));
   1617 
   1618     exsltDateFreeDate(tmp);
   1619     exsltDateFreeDate(dur);
   1620 
   1621     dt->value.date.tzo = 0;
   1622 }
   1623 
   1624 /**
   1625  * _exsltDateDifference:
   1626  * @x: an #exsltDateValPtr
   1627  * @y: an #exsltDateValPtr
   1628  * @flag: force difference in days
   1629  *
   1630  * Calculate the difference between @x and @y as a duration
   1631  * (i.e. y - x). If the @flag is set then even if the least specific
   1632  * format of @x or @y is xs:gYear or xs:gYearMonth.
   1633  *
   1634  * Returns date/time pointer or NULL.
   1635  */
   1636 static exsltDateValPtr
   1637 _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
   1638 {
   1639     exsltDateValPtr ret;
   1640 
   1641     if ((x == NULL) || (y == NULL))
   1642         return NULL;
   1643 
   1644     if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
   1645         ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
   1646         return NULL;
   1647 
   1648     exsltDateNormalize(x);
   1649     exsltDateNormalize(y);
   1650 
   1651     /*
   1652      * the operand with the most specific format must be converted to
   1653      * the same type as the operand with the least specific format.
   1654      */
   1655     if (x->type != y->type) {
   1656         if (x->type < y->type) {
   1657             _exsltDateTruncateDate(y, x->type);
   1658         } else {
   1659             _exsltDateTruncateDate(x, y->type);
   1660         }
   1661     }
   1662 
   1663     ret = exsltDateCreateDate(XS_DURATION);
   1664     if (ret == NULL)
   1665         return NULL;
   1666 
   1667     if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
   1668         /* compute the difference in months */
   1669         ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
   1670                              ((x->value.date.year * 12) + x->value.date.mon);
   1671 	/* The above will give a wrong result if x and y are on different sides
   1672 	 of the September 1752. Resolution is welcome :-) */
   1673     } else {
   1674         ret->value.dur.day  = _exsltDateCastYMToDays(y) -
   1675                               _exsltDateCastYMToDays(x);
   1676         ret->value.dur.day += y->value.date.day - x->value.date.day;
   1677         ret->value.dur.sec  = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
   1678 	if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
   1679 	    ret->value.dur.day -= 1;
   1680 	    ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
   1681 	} else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
   1682 	    ret->value.dur.day += 1;
   1683 	    ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
   1684 	}
   1685     }
   1686 
   1687     return ret;
   1688 }
   1689 
   1690 /**
   1691  * _exsltDateAddDurCalc
   1692  * @ret: an exsltDateValPtr for the return value:
   1693  * @x: an exsltDateValPtr for the first operand
   1694  * @y: an exsltDateValPtr for the second operand
   1695  *
   1696  * Add two durations, catering for possible negative values.
   1697  * The sum is placed in @ret.
   1698  *
   1699  * Returns 1 for success, 0 if error detected.
   1700  */
   1701 static int
   1702 _exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
   1703 		      exsltDateValPtr y)
   1704 {
   1705     long carry;
   1706 
   1707     /* months */
   1708     ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
   1709 
   1710     /* seconds */
   1711     ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
   1712     carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
   1713     if (ret->value.dur.sec != 0.0) {
   1714         ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
   1715 	/*
   1716 	 * Our function MODULO always gives us a positive value, so
   1717 	 * if we end up with a "-ve" carry we need to adjust it
   1718 	 * appropriately (bug 154021)
   1719 	 */
   1720 	if ((carry < 0) && (ret->value.dur.sec != 0)) {
   1721 	    /* change seconds to equiv negative modulus */
   1722 	    ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
   1723 	    carry++;
   1724 	}
   1725     }
   1726 
   1727     /* days */
   1728     ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
   1729 
   1730     /*
   1731      * are the results indeterminate? i.e. how do you subtract days from
   1732      * months or years?
   1733      */
   1734     if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
   1735          (ret->value.dur.mon < 0)) ||
   1736         (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
   1737          (ret->value.dur.mon > 0))) {
   1738         return 0;
   1739     }
   1740     return 1;
   1741 }
   1742 
   1743 /**
   1744  * _exsltDateAddDuration:
   1745  * @x: an #exsltDateValPtr of type #XS_DURATION
   1746  * @y: an #exsltDateValPtr of type #XS_DURATION
   1747  *
   1748  * Compute a new duration from @x and @y.
   1749  *
   1750  * Returns date/time pointer or NULL.
   1751  */
   1752 static exsltDateValPtr
   1753 _exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
   1754 {
   1755     exsltDateValPtr ret;
   1756 
   1757     if ((x == NULL) || (y == NULL))
   1758         return NULL;
   1759 
   1760     ret = exsltDateCreateDate(XS_DURATION);
   1761     if (ret == NULL)
   1762         return NULL;
   1763 
   1764     if (_exsltDateAddDurCalc(ret, x, y))
   1765         return ret;
   1766 
   1767     exsltDateFreeDate(ret);
   1768     return NULL;
   1769 }
   1770 
   1771 /****************************************************************
   1772  *								*
   1773  *		EXSLT - Dates and Times functions		*
   1774  *								*
   1775  ****************************************************************/
   1776 
   1777 /**
   1778  * exsltDateDateTime:
   1779  *
   1780  * Implements the EXSLT - Dates and Times date-time() function:
   1781  *     string date:date-time()
   1782  *
   1783  * Returns the current date and time as a date/time string.
   1784  */
   1785 static xmlChar *
   1786 exsltDateDateTime (void)
   1787 {
   1788     xmlChar *ret = NULL;
   1789 #ifdef WITH_TIME
   1790     exsltDateValPtr cur;
   1791 
   1792     cur = exsltDateCurrent();
   1793     if (cur != NULL) {
   1794 	ret = exsltDateFormatDateTime(&(cur->value.date));
   1795 	exsltDateFreeDate(cur);
   1796     }
   1797 #endif
   1798 
   1799     return ret;
   1800 }
   1801 
   1802 /**
   1803  * exsltDateDate:
   1804  * @dateTime: a date/time string
   1805  *
   1806  * Implements the EXSLT - Dates and Times date() function:
   1807  *     string date:date (string?)
   1808  *
   1809  * Returns the date specified in the date/time string given as the
   1810  * argument.  If no argument is given, then the current local
   1811  * date/time, as returned by date:date-time is used as a default
   1812  * argument.
   1813  * The date/time string specified as an argument must be a string in
   1814  * the format defined as the lexical representation of either
   1815  * xs:dateTime or xs:date.  If the argument is not in either of these
   1816  * formats, returns NULL.
   1817  */
   1818 static xmlChar *
   1819 exsltDateDate (const xmlChar *dateTime)
   1820 {
   1821     exsltDateValPtr dt = NULL;
   1822     xmlChar *ret = NULL;
   1823 
   1824     if (dateTime == NULL) {
   1825 #ifdef WITH_TIME
   1826 	dt = exsltDateCurrent();
   1827 	if (dt == NULL)
   1828 #endif
   1829 	    return NULL;
   1830     } else {
   1831 	dt = exsltDateParse(dateTime);
   1832 	if (dt == NULL)
   1833 	    return NULL;
   1834 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
   1835 	    exsltDateFreeDate(dt);
   1836 	    return NULL;
   1837 	}
   1838     }
   1839 
   1840     ret = exsltDateFormatDate(&(dt->value.date));
   1841     exsltDateFreeDate(dt);
   1842 
   1843     return ret;
   1844 }
   1845 
   1846 /**
   1847  * exsltDateTime:
   1848  * @dateTime: a date/time string
   1849  *
   1850  * Implements the EXSLT - Dates and Times time() function:
   1851  *     string date:time (string?)
   1852  *
   1853  * Returns the time specified in the date/time string given as the
   1854  * argument.  If no argument is given, then the current local
   1855  * date/time, as returned by date:date-time is used as a default
   1856  * argument.
   1857  * The date/time string specified as an argument must be a string in
   1858  * the format defined as the lexical representation of either
   1859  * xs:dateTime or xs:time.  If the argument is not in either of these
   1860  * formats, returns NULL.
   1861  */
   1862 static xmlChar *
   1863 exsltDateTime (const xmlChar *dateTime)
   1864 {
   1865     exsltDateValPtr dt = NULL;
   1866     xmlChar *ret = NULL;
   1867 
   1868     if (dateTime == NULL) {
   1869 #ifdef WITH_TIME
   1870 	dt = exsltDateCurrent();
   1871 	if (dt == NULL)
   1872 #endif
   1873 	    return NULL;
   1874     } else {
   1875 	dt = exsltDateParse(dateTime);
   1876 	if (dt == NULL)
   1877 	    return NULL;
   1878 	if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
   1879 	    exsltDateFreeDate(dt);
   1880 	    return NULL;
   1881 	}
   1882     }
   1883 
   1884     ret = exsltDateFormatTime(&(dt->value.date));
   1885     exsltDateFreeDate(dt);
   1886 
   1887     return ret;
   1888 }
   1889 
   1890 /**
   1891  * exsltDateYear:
   1892  * @dateTime: a date/time string
   1893  *
   1894  * Implements the EXSLT - Dates and Times year() function
   1895  *    number date:year (string?)
   1896  * Returns the year of a date as a number.  If no argument is given,
   1897  * then the current local date/time, as returned by date:date-time is
   1898  * used as a default argument.
   1899  * The date/time string specified as the first argument must be a
   1900  * right-truncated string in the format defined as the lexical
   1901  * representation of xs:dateTime in one of the formats defined in [XML
   1902  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   1903  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   1904  *  - xs:date (CCYY-MM-DD)
   1905  *  - xs:gYearMonth (CCYY-MM)
   1906  *  - xs:gYear (CCYY)
   1907  * If the date/time string is not in one of these formats, then NaN is
   1908  * returned.
   1909  */
   1910 static double
   1911 exsltDateYear (const xmlChar *dateTime)
   1912 {
   1913     exsltDateValPtr dt;
   1914     double ret;
   1915 
   1916     if (dateTime == NULL) {
   1917 #ifdef WITH_TIME
   1918 	dt = exsltDateCurrent();
   1919 	if (dt == NULL)
   1920 #endif
   1921 	    return xmlXPathNAN;
   1922     } else {
   1923 	dt = exsltDateParse(dateTime);
   1924 	if (dt == NULL)
   1925 	    return xmlXPathNAN;
   1926 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
   1927 	    (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
   1928 	    exsltDateFreeDate(dt);
   1929 	    return xmlXPathNAN;
   1930 	}
   1931     }
   1932 
   1933     ret = (double) dt->value.date.year;
   1934     exsltDateFreeDate(dt);
   1935 
   1936     return ret;
   1937 }
   1938 
   1939 /**
   1940  * exsltDateLeapYear:
   1941  * @dateTime: a date/time string
   1942  *
   1943  * Implements the EXSLT - Dates and Times leap-year() function:
   1944  *    boolean date:leap-yea (string?)
   1945  * Returns true if the year given in a date is a leap year.  If no
   1946  * argument is given, then the current local date/time, as returned by
   1947  * date:date-time is used as a default argument.
   1948  * The date/time string specified as the first argument must be a
   1949  * right-truncated string in the format defined as the lexical
   1950  * representation of xs:dateTime in one of the formats defined in [XML
   1951  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   1952  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   1953  *  - xs:date (CCYY-MM-DD)
   1954  *  - xs:gYearMonth (CCYY-MM)
   1955  *  - xs:gYear (CCYY)
   1956  * If the date/time string is not in one of these formats, then NaN is
   1957  * returned.
   1958  */
   1959 static xmlXPathObjectPtr
   1960 exsltDateLeapYear (const xmlChar *dateTime)
   1961 {
   1962     double year;
   1963 
   1964     year = exsltDateYear(dateTime);
   1965     if (xmlXPathIsNaN(year))
   1966 	return xmlXPathNewFloat(xmlXPathNAN);
   1967 
   1968     if (IS_LEAP((long)year))
   1969 	return xmlXPathNewBoolean(1);
   1970 
   1971     return xmlXPathNewBoolean(0);
   1972 }
   1973 
   1974 /**
   1975  * exsltDateMonthInYear:
   1976  * @dateTime: a date/time string
   1977  *
   1978  * Implements the EXSLT - Dates and Times month-in-year() function:
   1979  *    number date:month-in-year (string?)
   1980  * Returns the month of a date as a number.  If no argument is given,
   1981  * then the current local date/time, as returned by date:date-time is
   1982  * used the default argument.
   1983  * The date/time string specified as the argument is a left or
   1984  * right-truncated string in the format defined as the lexical
   1985  * representation of xs:dateTime in one of the formats defined in [XML
   1986  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   1987  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   1988  *  - xs:date (CCYY-MM-DD)
   1989  *  - xs:gYearMonth (CCYY-MM)
   1990  *  - xs:gMonth (--MM--)
   1991  *  - xs:gMonthDay (--MM-DD)
   1992  * If the date/time string is not in one of these formats, then NaN is
   1993  * returned.
   1994  */
   1995 static double
   1996 exsltDateMonthInYear (const xmlChar *dateTime)
   1997 {
   1998     exsltDateValPtr dt;
   1999     double ret;
   2000 
   2001     if (dateTime == NULL) {
   2002 #ifdef WITH_TIME
   2003 	dt = exsltDateCurrent();
   2004 	if (dt == NULL)
   2005 #endif
   2006 	    return xmlXPathNAN;
   2007     } else {
   2008 	dt = exsltDateParse(dateTime);
   2009 	if (dt == NULL)
   2010 	    return xmlXPathNAN;
   2011 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
   2012 	    (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
   2013 	    (dt->type != XS_GMONTHDAY)) {
   2014 	    exsltDateFreeDate(dt);
   2015 	    return xmlXPathNAN;
   2016 	}
   2017     }
   2018 
   2019     ret = (double) dt->value.date.mon;
   2020     exsltDateFreeDate(dt);
   2021 
   2022     return ret;
   2023 }
   2024 
   2025 /**
   2026  * exsltDateMonthName:
   2027  * @dateTime: a date/time string
   2028  *
   2029  * Implements the EXSLT - Dates and Time month-name() function
   2030  *    string date:month-name (string?)
   2031  * Returns the full name of the month of a date.  If no argument is
   2032  * given, then the current local date/time, as returned by
   2033  * date:date-time is used the default argument.
   2034  * The date/time string specified as the argument is a left or
   2035  * right-truncated string in the format defined as the lexical
   2036  * representation of xs:dateTime in one of the formats defined in [XML
   2037  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2038  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2039  *  - xs:date (CCYY-MM-DD)
   2040  *  - xs:gYearMonth (CCYY-MM)
   2041  *  - xs:gMonth (--MM--)
   2042  * If the date/time string is not in one of these formats, then an
   2043  * empty string ('') is returned.
   2044  * The result is an English month name: one of 'January', 'February',
   2045  * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
   2046  * 'October', 'November' or 'December'.
   2047  */
   2048 static const xmlChar *
   2049 exsltDateMonthName (const xmlChar *dateTime)
   2050 {
   2051     static const xmlChar monthNames[13][10] = {
   2052         { 0 },
   2053 	{ 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
   2054 	{ 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
   2055 	{ 'M', 'a', 'r', 'c', 'h', 0 },
   2056 	{ 'A', 'p', 'r', 'i', 'l', 0 },
   2057 	{ 'M', 'a', 'y', 0 },
   2058 	{ 'J', 'u', 'n', 'e', 0 },
   2059 	{ 'J', 'u', 'l', 'y', 0 },
   2060 	{ 'A', 'u', 'g', 'u', 's', 't', 0 },
   2061 	{ 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
   2062 	{ 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
   2063 	{ 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
   2064 	{ 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
   2065     };
   2066     int month;
   2067     month = (int) exsltDateMonthInYear(dateTime);
   2068     if (!VALID_MONTH(month))
   2069       month = 0;
   2070     return monthNames[month];
   2071 }
   2072 
   2073 /**
   2074  * exsltDateMonthAbbreviation:
   2075  * @dateTime: a date/time string
   2076  *
   2077  * Implements the EXSLT - Dates and Time month-abbreviation() function
   2078  *    string date:month-abbreviation (string?)
   2079  * Returns the abbreviation of the month of a date.  If no argument is
   2080  * given, then the current local date/time, as returned by
   2081  * date:date-time is used the default argument.
   2082  * The date/time string specified as the argument is a left or
   2083  * right-truncated string in the format defined as the lexical
   2084  * representation of xs:dateTime in one of the formats defined in [XML
   2085  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2086  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2087  *  - xs:date (CCYY-MM-DD)
   2088  *  - xs:gYearMonth (CCYY-MM)
   2089  *  - xs:gMonth (--MM--)
   2090  * If the date/time string is not in one of these formats, then an
   2091  * empty string ('') is returned.
   2092  * The result is an English month abbreviation: one of 'Jan', 'Feb',
   2093  * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
   2094  * 'Dec'.
   2095  */
   2096 static const xmlChar *
   2097 exsltDateMonthAbbreviation (const xmlChar *dateTime)
   2098 {
   2099     static const xmlChar monthAbbreviations[13][4] = {
   2100         { 0 },
   2101 	{ 'J', 'a', 'n', 0 },
   2102 	{ 'F', 'e', 'b', 0 },
   2103 	{ 'M', 'a', 'r', 0 },
   2104 	{ 'A', 'p', 'r', 0 },
   2105 	{ 'M', 'a', 'y', 0 },
   2106 	{ 'J', 'u', 'n', 0 },
   2107 	{ 'J', 'u', 'l', 0 },
   2108 	{ 'A', 'u', 'g', 0 },
   2109 	{ 'S', 'e', 'p', 0 },
   2110 	{ 'O', 'c', 't', 0 },
   2111 	{ 'N', 'o', 'v', 0 },
   2112 	{ 'D', 'e', 'c', 0 }
   2113     };
   2114     int month;
   2115     month = (int) exsltDateMonthInYear(dateTime);
   2116     if(!VALID_MONTH(month))
   2117       month = 0;
   2118     return monthAbbreviations[month];
   2119 }
   2120 
   2121 /**
   2122  * exsltDateWeekInYear:
   2123  * @dateTime: a date/time string
   2124  *
   2125  * Implements the EXSLT - Dates and Times week-in-year() function
   2126  *    number date:week-in-year (string?)
   2127  * Returns the week of the year as a number.  If no argument is given,
   2128  * then the current local date/time, as returned by date:date-time is
   2129  * used as the default argument.  For the purposes of numbering,
   2130  * counting follows ISO 8601: week 1 in a year is the week containing
   2131  * the first Thursday of the year, with new weeks beginning on a
   2132  * Monday.
   2133  * The date/time string specified as the argument is a right-truncated
   2134  * string in the format defined as the lexical representation of
   2135  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
   2136  * Datatypes].  The permitted formats are as follows:
   2137  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2138  *  - xs:date (CCYY-MM-DD)
   2139  * If the date/time string is not in one of these formats, then NaN is
   2140  * returned.
   2141  */
   2142 static double
   2143 exsltDateWeekInYear (const xmlChar *dateTime)
   2144 {
   2145     exsltDateValPtr dt;
   2146     long diy, diw, year, ret;
   2147 
   2148     if (dateTime == NULL) {
   2149 #ifdef WITH_TIME
   2150 	dt = exsltDateCurrent();
   2151 	if (dt == NULL)
   2152 #endif
   2153 	    return xmlXPathNAN;
   2154     } else {
   2155 	dt = exsltDateParse(dateTime);
   2156 	if (dt == NULL)
   2157 	    return xmlXPathNAN;
   2158 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
   2159 	    exsltDateFreeDate(dt);
   2160 	    return xmlXPathNAN;
   2161 	}
   2162     }
   2163 
   2164     diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
   2165                       dt->value.date.year);
   2166 
   2167     /*
   2168      * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
   2169      * is the first day-in-week
   2170      */
   2171     diw = (_exsltDateDayInWeek(diy, dt->value.date.year) + 6) % 7;
   2172 
   2173     /* ISO 8601 adjustment, 3 is Thu */
   2174     diy += (3 - diw);
   2175     if(diy < 1) {
   2176 	year = dt->value.date.year - 1;
   2177 	if(year == 0) year--;
   2178 	diy = DAY_IN_YEAR(31, 12, year) + diy;
   2179     } else if (diy > (long)DAY_IN_YEAR(31, 12, dt->value.date.year)) {
   2180 	diy -= DAY_IN_YEAR(31, 12, dt->value.date.year);
   2181     }
   2182 
   2183     ret = ((diy - 1) / 7) + 1;
   2184 
   2185     exsltDateFreeDate(dt);
   2186 
   2187     return (double) ret;
   2188 }
   2189 
   2190 /**
   2191  * exsltDateWeekInMonth:
   2192  * @dateTime: a date/time string
   2193  *
   2194  * Implements the EXSLT - Dates and Times week-in-month() function
   2195  *    number date:week-in-month (string?)
   2196  * The date:week-in-month function returns the week in a month of a
   2197  * date as a number. If no argument is given, then the current local
   2198  * date/time, as returned by date:date-time is used the default
   2199  * argument. For the purposes of numbering, the first day of the month
   2200  * is in week 1 and new weeks begin on a Monday (so the first and last
   2201  * weeks in a month will often have less than 7 days in them).
   2202  * The date/time string specified as the argument is a right-truncated
   2203  * string in the format defined as the lexical representation of
   2204  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
   2205  * Datatypes].  The permitted formats are as follows:
   2206  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2207  *  - xs:date (CCYY-MM-DD)
   2208  * If the date/time string is not in one of these formats, then NaN is
   2209  * returned.
   2210  */
   2211 static double
   2212 exsltDateWeekInMonth (const xmlChar *dateTime)
   2213 {
   2214     exsltDateValPtr dt;
   2215     long fdiy, fdiw, ret;
   2216 
   2217     if (dateTime == NULL) {
   2218 #ifdef WITH_TIME
   2219 	dt = exsltDateCurrent();
   2220 	if (dt == NULL)
   2221 #endif
   2222 	    return xmlXPathNAN;
   2223     } else {
   2224 	dt = exsltDateParse(dateTime);
   2225 	if (dt == NULL)
   2226 	    return xmlXPathNAN;
   2227 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
   2228 	    exsltDateFreeDate(dt);
   2229 	    return xmlXPathNAN;
   2230 	}
   2231     }
   2232 
   2233     fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
   2234     /*
   2235      * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
   2236      * is the first day-in-week
   2237      */
   2238     fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
   2239 
   2240     ret = ((dt->value.date.day + fdiw - 1) / 7) + 1;
   2241 
   2242     exsltDateFreeDate(dt);
   2243 
   2244     return (double) ret;
   2245 }
   2246 
   2247 /**
   2248  * exsltDateDayInYear:
   2249  * @dateTime: a date/time string
   2250  *
   2251  * Implements the EXSLT - Dates and Times day-in-year() function
   2252  *    number date:day-in-year (string?)
   2253  * Returns the day of a date in a year as a number.  If no argument is
   2254  * given, then the current local date/time, as returned by
   2255  * date:date-time is used the default argument.
   2256  * The date/time string specified as the argument is a right-truncated
   2257  * string in the format defined as the lexical representation of
   2258  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
   2259  * Datatypes].  The permitted formats are as follows:
   2260  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2261  *  - xs:date (CCYY-MM-DD)
   2262  * If the date/time string is not in one of these formats, then NaN is
   2263  * returned.
   2264  */
   2265 static double
   2266 exsltDateDayInYear (const xmlChar *dateTime)
   2267 {
   2268     exsltDateValPtr dt;
   2269     long ret;
   2270 
   2271     if (dateTime == NULL) {
   2272 #ifdef WITH_TIME
   2273 	dt = exsltDateCurrent();
   2274 	if (dt == NULL)
   2275 #endif
   2276 	    return xmlXPathNAN;
   2277     } else {
   2278 	dt = exsltDateParse(dateTime);
   2279 	if (dt == NULL)
   2280 	    return xmlXPathNAN;
   2281 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
   2282 	    exsltDateFreeDate(dt);
   2283 	    return xmlXPathNAN;
   2284 	}
   2285     }
   2286 
   2287     ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
   2288                       dt->value.date.year);
   2289 
   2290     exsltDateFreeDate(dt);
   2291 
   2292     return (double) ret;
   2293 }
   2294 
   2295 /**
   2296  * exsltDateDayInMonth:
   2297  * @dateTime: a date/time string
   2298  *
   2299  * Implements the EXSLT - Dates and Times day-in-month() function:
   2300  *    number date:day-in-month (string?)
   2301  * Returns the day of a date as a number.  If no argument is given,
   2302  * then the current local date/time, as returned by date:date-time is
   2303  * used the default argument.
   2304  * The date/time string specified as the argument is a left or
   2305  * right-truncated string in the format defined as the lexical
   2306  * representation of xs:dateTime in one of the formats defined in [XML
   2307  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2308  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2309  *  - xs:date (CCYY-MM-DD)
   2310  *  - xs:gMonthDay (--MM-DD)
   2311  *  - xs:gDay (---DD)
   2312  * If the date/time string is not in one of these formats, then NaN is
   2313  * returned.
   2314  */
   2315 static double
   2316 exsltDateDayInMonth (const xmlChar *dateTime)
   2317 {
   2318     exsltDateValPtr dt;
   2319     double ret;
   2320 
   2321     if (dateTime == NULL) {
   2322 #ifdef WITH_TIME
   2323 	dt = exsltDateCurrent();
   2324 	if (dt == NULL)
   2325 #endif
   2326 	    return xmlXPathNAN;
   2327     } else {
   2328 	dt = exsltDateParse(dateTime);
   2329 	if (dt == NULL)
   2330 	    return xmlXPathNAN;
   2331 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
   2332 	    (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
   2333 	    exsltDateFreeDate(dt);
   2334 	    return xmlXPathNAN;
   2335 	}
   2336     }
   2337 
   2338     ret = (double) dt->value.date.day;
   2339     exsltDateFreeDate(dt);
   2340 
   2341     return ret;
   2342 }
   2343 
   2344 /**
   2345  * exsltDateDayOfWeekInMonth:
   2346  * @dateTime: a date/time string
   2347  *
   2348  * Implements the EXSLT - Dates and Times day-of-week-in-month() function:
   2349  *    number date:day-of-week-in-month (string?)
   2350  * Returns the day-of-the-week in a month of a date as a number
   2351  * (e.g. 3 for the 3rd Tuesday in May).  If no argument is
   2352  * given, then the current local date/time, as returned by
   2353  * date:date-time is used the default argument.
   2354  * The date/time string specified as the argument is a right-truncated
   2355  * string in the format defined as the lexical representation of
   2356  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
   2357  * Datatypes].  The permitted formats are as follows:
   2358  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2359  *  - xs:date (CCYY-MM-DD)
   2360  * If the date/time string is not in one of these formats, then NaN is
   2361  * returned.
   2362  */
   2363 static double
   2364 exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
   2365 {
   2366     exsltDateValPtr dt;
   2367     long ret;
   2368 
   2369     if (dateTime == NULL) {
   2370 #ifdef WITH_TIME
   2371 	dt = exsltDateCurrent();
   2372 	if (dt == NULL)
   2373 #endif
   2374 	    return xmlXPathNAN;
   2375     } else {
   2376 	dt = exsltDateParse(dateTime);
   2377 	if (dt == NULL)
   2378 	    return xmlXPathNAN;
   2379 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
   2380 	    exsltDateFreeDate(dt);
   2381 	    return xmlXPathNAN;
   2382 	}
   2383     }
   2384 
   2385     ret = ((dt->value.date.day -1) / 7) + 1;
   2386 
   2387     exsltDateFreeDate(dt);
   2388 
   2389     return (double) ret;
   2390 }
   2391 
   2392 /**
   2393  * exsltDateDayInWeek:
   2394  * @dateTime: a date/time string
   2395  *
   2396  * Implements the EXSLT - Dates and Times day-in-week() function:
   2397  *    number date:day-in-week (string?)
   2398  * Returns the day of the week given in a date as a number.  If no
   2399  * argument is given, then the current local date/time, as returned by
   2400  * date:date-time is used the default argument.
   2401  * The date/time string specified as the argument is a left or
   2402  * right-truncated string in the format defined as the lexical
   2403  * representation of xs:dateTime in one of the formats defined in [XML
   2404  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2405  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2406  *  - xs:date (CCYY-MM-DD)
   2407  * If the date/time string is not in one of these formats, then NaN is
   2408  * returned.
   2409  * The numbering of days of the week starts at 1 for Sunday, 2 for
   2410  * Monday and so on up to 7 for Saturday.
   2411  */
   2412 static double
   2413 exsltDateDayInWeek (const xmlChar *dateTime)
   2414 {
   2415     exsltDateValPtr dt;
   2416     long diy, ret;
   2417 
   2418     if (dateTime == NULL) {
   2419 #ifdef WITH_TIME
   2420 	dt = exsltDateCurrent();
   2421 	if (dt == NULL)
   2422 #endif
   2423 	    return xmlXPathNAN;
   2424     } else {
   2425 	dt = exsltDateParse(dateTime);
   2426 	if (dt == NULL)
   2427 	    return xmlXPathNAN;
   2428 	if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
   2429 	    exsltDateFreeDate(dt);
   2430 	    return xmlXPathNAN;
   2431 	}
   2432     }
   2433 
   2434     diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
   2435                       dt->value.date.year);
   2436 
   2437     ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
   2438 
   2439     exsltDateFreeDate(dt);
   2440 
   2441     return (double) ret;
   2442 }
   2443 
   2444 /**
   2445  * exsltDateDayName:
   2446  * @dateTime: a date/time string
   2447  *
   2448  * Implements the EXSLT - Dates and Time day-name() function
   2449  *    string date:day-name (string?)
   2450  * Returns the full name of the day of the week of a date.  If no
   2451  * argument is given, then the current local date/time, as returned by
   2452  * date:date-time is used the default argument.
   2453  * The date/time string specified as the argument is a left or
   2454  * right-truncated string in the format defined as the lexical
   2455  * representation of xs:dateTime in one of the formats defined in [XML
   2456  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2457  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2458  *  - xs:date (CCYY-MM-DD)
   2459  * If the date/time string is not in one of these formats, then an
   2460  * empty string ('') is returned.
   2461  * The result is an English day name: one of 'Sunday', 'Monday',
   2462  * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
   2463  */
   2464 static const xmlChar *
   2465 exsltDateDayName (const xmlChar *dateTime)
   2466 {
   2467     static const xmlChar dayNames[8][10] = {
   2468         { 0 },
   2469 	{ 'S', 'u', 'n', 'd', 'a', 'y', 0 },
   2470 	{ 'M', 'o', 'n', 'd', 'a', 'y', 0 },
   2471 	{ 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
   2472 	{ 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
   2473 	{ 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
   2474 	{ 'F', 'r', 'i', 'd', 'a', 'y', 0 },
   2475 	{ 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
   2476     };
   2477     int day;
   2478     day = (int) exsltDateDayInWeek(dateTime);
   2479     if((day < 1) || (day > 7))
   2480       day = 0;
   2481     return dayNames[day];
   2482 }
   2483 
   2484 /**
   2485  * exsltDateDayAbbreviation:
   2486  * @dateTime: a date/time string
   2487  *
   2488  * Implements the EXSLT - Dates and Time day-abbreviation() function
   2489  *    string date:day-abbreviation (string?)
   2490  * Returns the abbreviation of the day of the week of a date.  If no
   2491  * argument is given, then the current local date/time, as returned by
   2492  * date:date-time is used the default argument.
   2493  * The date/time string specified as the argument is a left or
   2494  * right-truncated string in the format defined as the lexical
   2495  * representation of xs:dateTime in one of the formats defined in [XML
   2496  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2497  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2498  *  - xs:date (CCYY-MM-DD)
   2499  * If the date/time string is not in one of these formats, then an
   2500  * empty string ('') is returned.
   2501  * The result is a three-letter English day abbreviation: one of
   2502  * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
   2503  */
   2504 static const xmlChar *
   2505 exsltDateDayAbbreviation (const xmlChar *dateTime)
   2506 {
   2507     static const xmlChar dayAbbreviations[8][4] = {
   2508         { 0 },
   2509 	{ 'S', 'u', 'n', 0 },
   2510 	{ 'M', 'o', 'n', 0 },
   2511 	{ 'T', 'u', 'e', 0 },
   2512 	{ 'W', 'e', 'd', 0 },
   2513 	{ 'T', 'h', 'u', 0 },
   2514 	{ 'F', 'r', 'i', 0 },
   2515 	{ 'S', 'a', 't', 0 }
   2516     };
   2517     int day;
   2518     day = (int) exsltDateDayInWeek(dateTime);
   2519     if((day < 1) || (day > 7))
   2520       day = 0;
   2521     return dayAbbreviations[day];
   2522 }
   2523 
   2524 /**
   2525  * exsltDateHourInDay:
   2526  * @dateTime: a date/time string
   2527  *
   2528  * Implements the EXSLT - Dates and Times day-in-month() function:
   2529  *    number date:day-in-month (string?)
   2530  * Returns the hour of the day as a number.  If no argument is given,
   2531  * then the current local date/time, as returned by date:date-time is
   2532  * used the default argument.
   2533  * The date/time string specified as the argument is a left or
   2534  * right-truncated string in the format defined as the lexical
   2535  * representation of xs:dateTime in one of the formats defined in [XML
   2536  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2537  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2538  *  - xs:time (hh:mm:ss)
   2539  * If the date/time string is not in one of these formats, then NaN is
   2540  * returned.
   2541  */
   2542 static double
   2543 exsltDateHourInDay (const xmlChar *dateTime)
   2544 {
   2545     exsltDateValPtr dt;
   2546     double ret;
   2547 
   2548     if (dateTime == NULL) {
   2549 #ifdef WITH_TIME
   2550 	dt = exsltDateCurrent();
   2551 	if (dt == NULL)
   2552 #endif
   2553 	    return xmlXPathNAN;
   2554     } else {
   2555 	dt = exsltDateParse(dateTime);
   2556 	if (dt == NULL)
   2557 	    return xmlXPathNAN;
   2558 	if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
   2559 	    exsltDateFreeDate(dt);
   2560 	    return xmlXPathNAN;
   2561 	}
   2562     }
   2563 
   2564     ret = (double) dt->value.date.hour;
   2565     exsltDateFreeDate(dt);
   2566 
   2567     return ret;
   2568 }
   2569 
   2570 /**
   2571  * exsltDateMinuteInHour:
   2572  * @dateTime: a date/time string
   2573  *
   2574  * Implements the EXSLT - Dates and Times day-in-month() function:
   2575  *    number date:day-in-month (string?)
   2576  * Returns the minute of the hour as a number.  If no argument is
   2577  * given, then the current local date/time, as returned by
   2578  * date:date-time is used the default argument.
   2579  * The date/time string specified as the argument is a left or
   2580  * right-truncated string in the format defined as the lexical
   2581  * representation of xs:dateTime in one of the formats defined in [XML
   2582  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2583  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2584  *  - xs:time (hh:mm:ss)
   2585  * If the date/time string is not in one of these formats, then NaN is
   2586  * returned.
   2587  */
   2588 static double
   2589 exsltDateMinuteInHour (const xmlChar *dateTime)
   2590 {
   2591     exsltDateValPtr dt;
   2592     double ret;
   2593 
   2594     if (dateTime == NULL) {
   2595 #ifdef WITH_TIME
   2596 	dt = exsltDateCurrent();
   2597 	if (dt == NULL)
   2598 #endif
   2599 	    return xmlXPathNAN;
   2600     } else {
   2601 	dt = exsltDateParse(dateTime);
   2602 	if (dt == NULL)
   2603 	    return xmlXPathNAN;
   2604 	if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
   2605 	    exsltDateFreeDate(dt);
   2606 	    return xmlXPathNAN;
   2607 	}
   2608     }
   2609 
   2610     ret = (double) dt->value.date.min;
   2611     exsltDateFreeDate(dt);
   2612 
   2613     return ret;
   2614 }
   2615 
   2616 /**
   2617  * exsltDateSecondInMinute:
   2618  * @dateTime: a date/time string
   2619  *
   2620  * Implements the EXSLT - Dates and Times second-in-minute() function:
   2621  *    number date:day-in-month (string?)
   2622  * Returns the second of the minute as a number.  If no argument is
   2623  * given, then the current local date/time, as returned by
   2624  * date:date-time is used the default argument.
   2625  * The date/time string specified as the argument is a left or
   2626  * right-truncated string in the format defined as the lexical
   2627  * representation of xs:dateTime in one of the formats defined in [XML
   2628  * Schema Part 2: Datatypes].  The permitted formats are as follows:
   2629  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2630  *  - xs:time (hh:mm:ss)
   2631  * If the date/time string is not in one of these formats, then NaN is
   2632  * returned.
   2633  *
   2634  * Returns the second or NaN.
   2635  */
   2636 static double
   2637 exsltDateSecondInMinute (const xmlChar *dateTime)
   2638 {
   2639     exsltDateValPtr dt;
   2640     double ret;
   2641 
   2642     if (dateTime == NULL) {
   2643 #ifdef WITH_TIME
   2644 	dt = exsltDateCurrent();
   2645 	if (dt == NULL)
   2646 #endif
   2647 	    return xmlXPathNAN;
   2648     } else {
   2649 	dt = exsltDateParse(dateTime);
   2650 	if (dt == NULL)
   2651 	    return xmlXPathNAN;
   2652 	if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
   2653 	    exsltDateFreeDate(dt);
   2654 	    return xmlXPathNAN;
   2655 	}
   2656     }
   2657 
   2658     ret = dt->value.date.sec;
   2659     exsltDateFreeDate(dt);
   2660 
   2661     return ret;
   2662 }
   2663 
   2664 /**
   2665  * exsltDateAdd:
   2666  * @xstr: date/time string
   2667  * @ystr: date/time string
   2668  *
   2669  * Implements the date:add (string,string) function which returns the
   2670  * date/time * resulting from adding a duration to a date/time.
   2671  * The first argument (@xstr) must be right-truncated date/time
   2672  * strings in one of the formats defined in [XML Schema Part 2:
   2673  * Datatypes]. The permitted formats are as follows:
   2674  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2675  *  - xs:date (CCYY-MM-DD)
   2676  *  - xs:gYearMonth (CCYY-MM)
   2677  *  - xs:gYear (CCYY)
   2678  * The second argument (@ystr) is a string in the format defined for
   2679  * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
   2680  * The return value is a right-truncated date/time strings in one of
   2681  * the formats defined in [XML Schema Part 2: Datatypes] and listed
   2682  * above. This value is calculated using the algorithm described in
   2683  * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
   2684  * Datatypes].
   2685 
   2686  * Returns date/time string or NULL.
   2687  */
   2688 static xmlChar *
   2689 exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
   2690 {
   2691     exsltDateValPtr dt, dur, res;
   2692     xmlChar     *ret;
   2693 
   2694     if ((xstr == NULL) || (ystr == NULL))
   2695         return NULL;
   2696 
   2697     dt = exsltDateParse(xstr);
   2698     if (dt == NULL)
   2699         return NULL;
   2700     else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
   2701         exsltDateFreeDate(dt);
   2702         return NULL;
   2703     }
   2704 
   2705     dur = exsltDateParseDuration(ystr);
   2706     if (dur == NULL) {
   2707         exsltDateFreeDate(dt);
   2708         return NULL;
   2709     }
   2710 
   2711     res = _exsltDateAdd(dt, dur);
   2712 
   2713     exsltDateFreeDate(dt);
   2714     exsltDateFreeDate(dur);
   2715 
   2716     if (res == NULL)
   2717         return NULL;
   2718 
   2719     ret = exsltDateFormat(res);
   2720     exsltDateFreeDate(res);
   2721 
   2722     return ret;
   2723 }
   2724 
   2725 /**
   2726  * exsltDateAddDuration:
   2727  * @xstr:      first duration string
   2728  * @ystr:      second duration string
   2729  *
   2730  * Implements the date:add-duration (string,string) function which returns
   2731  * the duration resulting from adding two durations together.
   2732  * Both arguments are strings in the format defined for xs:duration
   2733  * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
   2734  * argument is not in this format, the function returns an empty string
   2735  * ('').
   2736  * The return value is a string in the format defined for xs:duration
   2737  * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
   2738  * The durations can usually be added by summing the numbers given for
   2739  * each of the components in the durations. However, if the durations
   2740  * are differently signed, then this sometimes results in durations
   2741  * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
   2742  * In these cases, the function returns an empty string ('').
   2743  *
   2744  * Returns duration string or NULL.
   2745  */
   2746 static xmlChar *
   2747 exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
   2748 {
   2749     exsltDateValPtr x, y, res;
   2750     xmlChar     *ret;
   2751 
   2752     if ((xstr == NULL) || (ystr == NULL))
   2753         return NULL;
   2754 
   2755     x = exsltDateParseDuration(xstr);
   2756     if (x == NULL)
   2757         return NULL;
   2758 
   2759     y = exsltDateParseDuration(ystr);
   2760     if (y == NULL) {
   2761         exsltDateFreeDate(x);
   2762         return NULL;
   2763     }
   2764 
   2765     res = _exsltDateAddDuration(x, y);
   2766 
   2767     exsltDateFreeDate(x);
   2768     exsltDateFreeDate(y);
   2769 
   2770     if (res == NULL)
   2771         return NULL;
   2772 
   2773     ret = exsltDateFormatDuration(&(res->value.dur));
   2774     exsltDateFreeDate(res);
   2775 
   2776     return ret;
   2777 }
   2778 
   2779 /**
   2780  * exsltDateSumFunction:
   2781  * @ns:      a node set of duration strings
   2782  *
   2783  * The date:sum function adds a set of durations together.
   2784  * The string values of the nodes in the node set passed as an argument
   2785  * are interpreted as durations and added together as if using the
   2786  * date:add-duration function. (from exslt.org)
   2787  *
   2788  * The return value is a string in the format defined for xs:duration
   2789  * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
   2790  * The durations can usually be added by summing the numbers given for
   2791  * each of the components in the durations. However, if the durations
   2792  * are differently signed, then this sometimes results in durations
   2793  * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
   2794  * In these cases, the function returns an empty string ('').
   2795  *
   2796  * Returns duration string or NULL.
   2797  */
   2798 static void
   2799 exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
   2800 {
   2801     xmlNodeSetPtr ns;
   2802     void *user = NULL;
   2803     xmlChar *tmp;
   2804     exsltDateValPtr x, total;
   2805     xmlChar *ret;
   2806     int i;
   2807 
   2808     if (nargs != 1) {
   2809 	xmlXPathSetArityError (ctxt);
   2810 	return;
   2811     }
   2812 
   2813     /* We need to delay the freeing of value->user */
   2814     if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
   2815 	user = ctxt->value->user;
   2816 	ctxt->value->boolval = 0;
   2817 	ctxt->value->user = NULL;
   2818     }
   2819 
   2820     ns = xmlXPathPopNodeSet (ctxt);
   2821     if (xmlXPathCheckError (ctxt))
   2822 	return;
   2823 
   2824     if ((ns == NULL) || (ns->nodeNr == 0)) {
   2825 	xmlXPathReturnEmptyString (ctxt);
   2826 	if (ns != NULL)
   2827 	    xmlXPathFreeNodeSet (ns);
   2828 	return;
   2829     }
   2830 
   2831     total = exsltDateCreateDate (XS_DURATION);
   2832     if (total == NULL) {
   2833         xmlXPathFreeNodeSet (ns);
   2834         return;
   2835     }
   2836 
   2837     for (i = 0; i < ns->nodeNr; i++) {
   2838     	int result;
   2839 	tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
   2840 	if (tmp == NULL) {
   2841 	    xmlXPathFreeNodeSet (ns);
   2842 	    exsltDateFreeDate (total);
   2843 	    return;
   2844 	}
   2845 
   2846 	x = exsltDateParseDuration (tmp);
   2847 	if (x == NULL) {
   2848 	    xmlFree (tmp);
   2849 	    exsltDateFreeDate (total);
   2850 	    xmlXPathFreeNodeSet (ns);
   2851 	    xmlXPathReturnEmptyString (ctxt);
   2852 	    return;
   2853 	}
   2854 
   2855 	result = _exsltDateAddDurCalc(total, total, x);
   2856 
   2857 	exsltDateFreeDate (x);
   2858 	xmlFree (tmp);
   2859 	if (!result) {
   2860 	    exsltDateFreeDate (total);
   2861 	    xmlXPathFreeNodeSet (ns);
   2862 	    xmlXPathReturnEmptyString (ctxt);
   2863 	    return;
   2864 	}
   2865     }
   2866 
   2867     ret = exsltDateFormatDuration (&(total->value.dur));
   2868     exsltDateFreeDate (total);
   2869 
   2870     xmlXPathFreeNodeSet (ns);
   2871     if (user != NULL)
   2872 	xmlFreeNodeList ((xmlNodePtr) user);
   2873 
   2874     if (ret == NULL)
   2875 	xmlXPathReturnEmptyString (ctxt);
   2876     else
   2877 	xmlXPathReturnString (ctxt, ret);
   2878 }
   2879 
   2880 /**
   2881  * exsltDateSeconds:
   2882  * @dateTime: a date/time string
   2883  *
   2884  * Implements the EXSLT - Dates and Times seconds() function:
   2885  *    number date:seconds(string?)
   2886  * The date:seconds function returns the number of seconds specified
   2887  * by the argument string. If no argument is given, then the current
   2888  * local date/time, as returned by exsltDateCurrent() is used as the
   2889  * default argument. If the date/time string is a xs:duration, then the
   2890  * years and months must be zero (or not present). Parsing a duration
   2891  * converts the fields to seconds. If the date/time string is not a
   2892  * duration (and not null), then the legal formats are:
   2893  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2894  *  - xs:date     (CCYY-MM-DD)
   2895  *  - xs:gYearMonth (CCYY-MM)
   2896  *  - xs:gYear      (CCYY)
   2897  * In these cases the difference between the @dateTime and
   2898  * 1970-01-01T00:00:00Z is calculated and converted to seconds.
   2899  *
   2900  * Note that there was some confusion over whether "difference" meant
   2901  * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
   2902  * a negative one.  After correspondence with exslt.org, it was determined
   2903  * that the intent of the specification was to have it positive.  The
   2904  * coding was modified in July 2003 to reflect this.
   2905  *
   2906  * Returns seconds or Nan.
   2907  */
   2908 static double
   2909 exsltDateSeconds (const xmlChar *dateTime)
   2910 {
   2911     exsltDateValPtr dt;
   2912     double ret = xmlXPathNAN;
   2913 
   2914     if (dateTime == NULL) {
   2915 #ifdef WITH_TIME
   2916 	dt = exsltDateCurrent();
   2917 	if (dt == NULL)
   2918 #endif
   2919 	    return xmlXPathNAN;
   2920     } else {
   2921         dt = exsltDateParseDuration(dateTime);
   2922         if (dt == NULL)
   2923             dt = exsltDateParse(dateTime);
   2924     }
   2925 
   2926     if (dt == NULL)
   2927         return xmlXPathNAN;
   2928 
   2929     if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
   2930         exsltDateValPtr y, dur;
   2931 
   2932         /*
   2933          * compute the difference between the given (or current) date
   2934          * and epoch date
   2935          */
   2936         y = exsltDateCreateDate(XS_DATETIME);
   2937         if (y != NULL) {
   2938             y->value.date.year = 1970;
   2939             y->value.date.mon  = 1;
   2940             y->value.date.day  = 1;
   2941             y->value.date.tz_flag = 1;
   2942 
   2943             dur = _exsltDateDifference(y, dt, 1);
   2944             if (dur != NULL) {
   2945                 ret = exsltDateCastDateToNumber(dur);
   2946                 exsltDateFreeDate(dur);
   2947             }
   2948             exsltDateFreeDate(y);
   2949         }
   2950 
   2951     } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
   2952         ret = exsltDateCastDateToNumber(dt);
   2953 
   2954     exsltDateFreeDate(dt);
   2955 
   2956     return ret;
   2957 }
   2958 
   2959 /**
   2960  * exsltDateDifference:
   2961  * @xstr: date/time string
   2962  * @ystr: date/time string
   2963  *
   2964  * Implements the date:difference (string,string) function which returns
   2965  * the duration between the first date and the second date. If the first
   2966  * date occurs before the second date, then the result is a positive
   2967  * duration; if it occurs after the second date, the result is a
   2968  * negative duration.  The two dates must both be right-truncated
   2969  * date/time strings in one of the formats defined in [XML Schema Part
   2970  * 2: Datatypes]. The date/time with the most specific format (i.e. the
   2971  * least truncation) is converted into the same format as the date with
   2972  * the least specific format (i.e. the most truncation). The permitted
   2973  * formats are as follows, from most specific to least specific:
   2974  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
   2975  *  - xs:date (CCYY-MM-DD)
   2976  *  - xs:gYearMonth (CCYY-MM)
   2977  *  - xs:gYear (CCYY)
   2978  * If either of the arguments is not in one of these formats,
   2979  * date:difference returns the empty string ('').
   2980  * The difference between the date/times is returned as a string in the
   2981  * format defined for xs:duration in [3.2.6 duration] of [XML Schema
   2982  * Part 2: Datatypes].
   2983  * If the date/time string with the least specific format is in either
   2984  * xs:gYearMonth or xs:gYear format, then the number of days, hours,
   2985  * minutes and seconds in the duration string must be equal to zero.
   2986  * (The format of the string will be PnYnM.) The number of months
   2987  * specified in the duration must be less than 12.
   2988  * Otherwise, the number of years and months in the duration string
   2989  * must be equal to zero. (The format of the string will be
   2990  * PnDTnHnMnS.) The number of seconds specified in the duration string
   2991  * must be less than 60; the number of minutes must be less than 60;
   2992  * the number of hours must be less than 24.
   2993  *
   2994  * Returns duration string or NULL.
   2995  */
   2996 static xmlChar *
   2997 exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
   2998 {
   2999     exsltDateValPtr x, y, dur;
   3000     xmlChar        *ret = NULL;
   3001 
   3002     if ((xstr == NULL) || (ystr == NULL))
   3003         return NULL;
   3004 
   3005     x = exsltDateParse(xstr);
   3006     if (x == NULL)
   3007         return NULL;
   3008 
   3009     y = exsltDateParse(ystr);
   3010     if (y == NULL) {
   3011         exsltDateFreeDate(x);
   3012         return NULL;
   3013     }
   3014 
   3015     if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
   3016         ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))  {
   3017 	exsltDateFreeDate(x);
   3018 	exsltDateFreeDate(y);
   3019         return NULL;
   3020     }
   3021 
   3022     dur = _exsltDateDifference(x, y, 0);
   3023 
   3024     exsltDateFreeDate(x);
   3025     exsltDateFreeDate(y);
   3026 
   3027     if (dur == NULL)
   3028         return NULL;
   3029 
   3030     ret = exsltDateFormatDuration(&(dur->value.dur));
   3031     exsltDateFreeDate(dur);
   3032 
   3033     return ret;
   3034 }
   3035 
   3036 /**
   3037  * exsltDateDuration:
   3038  * @number: a xmlChar string
   3039  *
   3040  * Implements the The date:duration function returns a duration string
   3041  * representing the number of seconds specified by the argument string.
   3042  * If no argument is given, then the result of calling date:seconds
   3043  * without any arguments is used as a default argument.
   3044  * The duration is returned as a string in the format defined for
   3045  * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
   3046  * The number of years and months in the duration string must be equal
   3047  * to zero. (The format of the string will be PnDTnHnMnS.) The number
   3048  * of seconds specified in the duration string must be less than 60;
   3049  * the number of minutes must be less than 60; the number of hours must
   3050  * be less than 24.
   3051  * If the argument is Infinity, -Infinity or NaN, then date:duration
   3052  * returns an empty string ('').
   3053  *
   3054  * Returns duration string or NULL.
   3055  */
   3056 static xmlChar *
   3057 exsltDateDuration (const xmlChar *number)
   3058 {
   3059     exsltDateValPtr dur;
   3060     double       secs;
   3061     xmlChar     *ret;
   3062 
   3063     if (number == NULL)
   3064         secs = exsltDateSeconds(number);
   3065     else
   3066         secs = xmlXPathCastStringToNumber(number);
   3067 
   3068     if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
   3069         return NULL;
   3070 
   3071     dur = exsltDateCreateDate(XS_DURATION);
   3072     if (dur == NULL)
   3073         return NULL;
   3074 
   3075     dur->value.dur.sec = secs;
   3076 
   3077     ret = exsltDateFormatDuration(&(dur->value.dur));
   3078     exsltDateFreeDate(dur);
   3079 
   3080     return ret;
   3081 }
   3082 
   3083 /****************************************************************
   3084  *								*
   3085  *		Wrappers for use by the XPath engine		*
   3086  *								*
   3087  ****************************************************************/
   3088 
   3089 #ifdef WITH_TIME
   3090 /**
   3091  * exsltDateDateTimeFunction:
   3092  * @ctxt: an XPath parser context
   3093  * @nargs : the number of arguments
   3094  *
   3095  * Wraps exsltDateDateTime() for use by the XPath engine.
   3096  */
   3097 static void
   3098 exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3099 {
   3100     xmlChar *ret;
   3101 
   3102     if (nargs != 0) {
   3103 	xmlXPathSetArityError(ctxt);
   3104 	return;
   3105     }
   3106 
   3107     ret = exsltDateDateTime();
   3108     if (ret == NULL)
   3109         xmlXPathReturnEmptyString(ctxt);
   3110     else
   3111         xmlXPathReturnString(ctxt, ret);
   3112 }
   3113 #endif
   3114 
   3115 /**
   3116  * exsltDateDateFunction:
   3117  * @ctxt: an XPath parser context
   3118  * @nargs : the number of arguments
   3119  *
   3120  * Wraps exsltDateDate() for use by the XPath engine.
   3121  */
   3122 static void
   3123 exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3124 {
   3125     xmlChar *ret, *dt = NULL;
   3126 
   3127     if ((nargs < 0) || (nargs > 1)) {
   3128 	xmlXPathSetArityError(ctxt);
   3129 	return;
   3130     }
   3131     if (nargs == 1) {
   3132 	dt = xmlXPathPopString(ctxt);
   3133 	if (xmlXPathCheckError(ctxt)) {
   3134 	    xmlXPathSetTypeError(ctxt);
   3135 	    return;
   3136 	}
   3137     }
   3138 
   3139     ret = exsltDateDate(dt);
   3140 
   3141     if (ret == NULL) {
   3142 	xsltGenericDebug(xsltGenericDebugContext,
   3143 			 "{http://exslt.org/dates-and-times}date: "
   3144 			 "invalid date or format %s\n", dt);
   3145 	xmlXPathReturnEmptyString(ctxt);
   3146     } else {
   3147 	xmlXPathReturnString(ctxt, ret);
   3148     }
   3149 
   3150     if (dt != NULL)
   3151 	xmlFree(dt);
   3152 }
   3153 
   3154 /**
   3155  * exsltDateTimeFunction:
   3156  * @ctxt: an XPath parser context
   3157  * @nargs : the number of arguments
   3158  *
   3159  * Wraps exsltDateTime() for use by the XPath engine.
   3160  */
   3161 static void
   3162 exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3163 {
   3164     xmlChar *ret, *dt = NULL;
   3165 
   3166     if ((nargs < 0) || (nargs > 1)) {
   3167 	xmlXPathSetArityError(ctxt);
   3168 	return;
   3169     }
   3170     if (nargs == 1) {
   3171 	dt = xmlXPathPopString(ctxt);
   3172 	if (xmlXPathCheckError(ctxt)) {
   3173 	    xmlXPathSetTypeError(ctxt);
   3174 	    return;
   3175 	}
   3176     }
   3177 
   3178     ret = exsltDateTime(dt);
   3179 
   3180     if (ret == NULL) {
   3181 	xsltGenericDebug(xsltGenericDebugContext,
   3182 			 "{http://exslt.org/dates-and-times}time: "
   3183 			 "invalid date or format %s\n", dt);
   3184 	xmlXPathReturnEmptyString(ctxt);
   3185     } else {
   3186 	xmlXPathReturnString(ctxt, ret);
   3187     }
   3188 
   3189     if (dt != NULL)
   3190 	xmlFree(dt);
   3191 }
   3192 
   3193 /**
   3194  * exsltDateYearFunction:
   3195  * @ctxt: an XPath parser context
   3196  * @nargs : the number of arguments
   3197  *
   3198  * Wraps exsltDateYear() for use by the XPath engine.
   3199  */
   3200 static void
   3201 exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3202 {
   3203     xmlChar *dt = NULL;
   3204     double ret;
   3205 
   3206     if ((nargs < 0) || (nargs > 1)) {
   3207 	xmlXPathSetArityError(ctxt);
   3208 	return;
   3209     }
   3210 
   3211     if (nargs == 1) {
   3212 	dt = xmlXPathPopString(ctxt);
   3213 	if (xmlXPathCheckError(ctxt)) {
   3214 	    xmlXPathSetTypeError(ctxt);
   3215 	    return;
   3216 	}
   3217     }
   3218 
   3219     ret = exsltDateYear(dt);
   3220 
   3221     if (dt != NULL)
   3222 	xmlFree(dt);
   3223 
   3224     xmlXPathReturnNumber(ctxt, ret);
   3225 }
   3226 
   3227 /**
   3228  * exsltDateLeapYearFunction:
   3229  * @ctxt: an XPath parser context
   3230  * @nargs : the number of arguments
   3231  *
   3232  * Wraps exsltDateLeapYear() for use by the XPath engine.
   3233  */
   3234 static void
   3235 exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3236 {
   3237     xmlChar *dt = NULL;
   3238     xmlXPathObjectPtr ret;
   3239 
   3240     if ((nargs < 0) || (nargs > 1)) {
   3241 	xmlXPathSetArityError(ctxt);
   3242 	return;
   3243     }
   3244 
   3245     if (nargs == 1) {
   3246 	dt = xmlXPathPopString(ctxt);
   3247 	if (xmlXPathCheckError(ctxt)) {
   3248 	    xmlXPathSetTypeError(ctxt);
   3249 	    return;
   3250 	}
   3251     }
   3252 
   3253     ret = exsltDateLeapYear(dt);
   3254 
   3255     if (dt != NULL)
   3256 	xmlFree(dt);
   3257 
   3258     valuePush(ctxt, ret);
   3259 }
   3260 
   3261 #define X_IN_Y(x, y)						\
   3262 static void							\
   3263 exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt,	\
   3264 			      int nargs) {			\
   3265     xmlChar *dt = NULL;						\
   3266     double ret;							\
   3267 								\
   3268     if ((nargs < 0) || (nargs > 1)) {				\
   3269 	xmlXPathSetArityError(ctxt);				\
   3270 	return;							\
   3271     }								\
   3272 								\
   3273     if (nargs == 1) {						\
   3274 	dt = xmlXPathPopString(ctxt);				\
   3275 	if (xmlXPathCheckError(ctxt)) {				\
   3276 	    xmlXPathSetTypeError(ctxt);				\
   3277 	    return;						\
   3278 	}							\
   3279     }								\
   3280 								\
   3281     ret = exsltDate##x##In##y(dt);				\
   3282 								\
   3283     if (dt != NULL)						\
   3284 	xmlFree(dt);						\
   3285 								\
   3286     xmlXPathReturnNumber(ctxt, ret);				\
   3287 }
   3288 
   3289 /**
   3290  * exsltDateMonthInYearFunction:
   3291  * @ctxt: an XPath parser context
   3292  * @nargs : the number of arguments
   3293  *
   3294  * Wraps exsltDateMonthInYear() for use by the XPath engine.
   3295  */
   3296 X_IN_Y(Month,Year)
   3297 
   3298 /**
   3299  * exsltDateMonthNameFunction:
   3300  * @ctxt: an XPath parser context
   3301  * @nargs : the number of arguments
   3302  *
   3303  * Wraps exsltDateMonthName() for use by the XPath engine.
   3304  */
   3305 static void
   3306 exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3307 {
   3308     xmlChar *dt = NULL;
   3309     const xmlChar *ret;
   3310 
   3311     if ((nargs < 0) || (nargs > 1)) {
   3312 	xmlXPathSetArityError(ctxt);
   3313 	return;
   3314     }
   3315 
   3316     if (nargs == 1) {
   3317 	dt = xmlXPathPopString(ctxt);
   3318 	if (xmlXPathCheckError(ctxt)) {
   3319 	    xmlXPathSetTypeError(ctxt);
   3320 	    return;
   3321 	}
   3322     }
   3323 
   3324     ret = exsltDateMonthName(dt);
   3325 
   3326     if (dt != NULL)
   3327 	xmlFree(dt);
   3328 
   3329     if (ret == NULL)
   3330 	xmlXPathReturnEmptyString(ctxt);
   3331     else
   3332 	xmlXPathReturnString(ctxt, xmlStrdup(ret));
   3333 }
   3334 
   3335 /**
   3336  * exsltDateMonthAbbreviationFunction:
   3337  * @ctxt: an XPath parser context
   3338  * @nargs : the number of arguments
   3339  *
   3340  * Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
   3341  */
   3342 static void
   3343 exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3344 {
   3345     xmlChar *dt = NULL;
   3346     const xmlChar *ret;
   3347 
   3348     if ((nargs < 0) || (nargs > 1)) {
   3349 	xmlXPathSetArityError(ctxt);
   3350 	return;
   3351     }
   3352 
   3353     if (nargs == 1) {
   3354 	dt = xmlXPathPopString(ctxt);
   3355 	if (xmlXPathCheckError(ctxt)) {
   3356 	    xmlXPathSetTypeError(ctxt);
   3357 	    return;
   3358 	}
   3359     }
   3360 
   3361     ret = exsltDateMonthAbbreviation(dt);
   3362 
   3363     if (dt != NULL)
   3364 	xmlFree(dt);
   3365 
   3366     if (ret == NULL)
   3367 	xmlXPathReturnEmptyString(ctxt);
   3368     else
   3369 	xmlXPathReturnString(ctxt, xmlStrdup(ret));
   3370 }
   3371 
   3372 /**
   3373  * exsltDateWeekInYearFunction:
   3374  * @ctxt: an XPath parser context
   3375  * @nargs : the number of arguments
   3376  *
   3377  * Wraps exsltDateWeekInYear() for use by the XPath engine.
   3378  */
   3379 X_IN_Y(Week,Year)
   3380 
   3381 /**
   3382  * exsltDateWeekInMonthFunction:
   3383  * @ctxt: an XPath parser context
   3384  * @nargs : the number of arguments
   3385  *
   3386  * Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
   3387  */
   3388 X_IN_Y(Week,Month)
   3389 
   3390 /**
   3391  * exsltDateDayInYearFunction:
   3392  * @ctxt: an XPath parser context
   3393  * @nargs : the number of arguments
   3394  *
   3395  * Wraps exsltDateDayInYear() for use by the XPath engine.
   3396  */
   3397 X_IN_Y(Day,Year)
   3398 
   3399 /**
   3400  * exsltDateDayInMonthFunction:
   3401  * @ctxt: an XPath parser context
   3402  * @nargs : the number of arguments
   3403  *
   3404  * Wraps exsltDateDayInMonth() for use by the XPath engine.
   3405  */
   3406 X_IN_Y(Day,Month)
   3407 
   3408 /**
   3409  * exsltDateDayOfWeekInMonthFunction:
   3410  * @ctxt: an XPath parser context
   3411  * @nargs : the number of arguments
   3412  *
   3413  * Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
   3414  */
   3415 X_IN_Y(DayOfWeek,Month)
   3416 
   3417 /**
   3418  * exsltDateDayInWeekFunction:
   3419  * @ctxt: an XPath parser context
   3420  * @nargs : the number of arguments
   3421  *
   3422  * Wraps exsltDateDayInWeek() for use by the XPath engine.
   3423  */
   3424 X_IN_Y(Day,Week)
   3425 
   3426 /**
   3427  * exsltDateDayNameFunction:
   3428  * @ctxt: an XPath parser context
   3429  * @nargs : the number of arguments
   3430  *
   3431  * Wraps exsltDateDayName() for use by the XPath engine.
   3432  */
   3433 static void
   3434 exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3435 {
   3436     xmlChar *dt = NULL;
   3437     const xmlChar *ret;
   3438 
   3439     if ((nargs < 0) || (nargs > 1)) {
   3440 	xmlXPathSetArityError(ctxt);
   3441 	return;
   3442     }
   3443 
   3444     if (nargs == 1) {
   3445 	dt = xmlXPathPopString(ctxt);
   3446 	if (xmlXPathCheckError(ctxt)) {
   3447 	    xmlXPathSetTypeError(ctxt);
   3448 	    return;
   3449 	}
   3450     }
   3451 
   3452     ret = exsltDateDayName(dt);
   3453 
   3454     if (dt != NULL)
   3455 	xmlFree(dt);
   3456 
   3457     if (ret == NULL)
   3458 	xmlXPathReturnEmptyString(ctxt);
   3459     else
   3460 	xmlXPathReturnString(ctxt, xmlStrdup(ret));
   3461 }
   3462 
   3463 /**
   3464  * exsltDateMonthDayFunction:
   3465  * @ctxt: an XPath parser context
   3466  * @nargs : the number of arguments
   3467  *
   3468  * Wraps exsltDateDayAbbreviation() for use by the XPath engine.
   3469  */
   3470 static void
   3471 exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3472 {
   3473     xmlChar *dt = NULL;
   3474     const xmlChar *ret;
   3475 
   3476     if ((nargs < 0) || (nargs > 1)) {
   3477 	xmlXPathSetArityError(ctxt);
   3478 	return;
   3479     }
   3480 
   3481     if (nargs == 1) {
   3482 	dt = xmlXPathPopString(ctxt);
   3483 	if (xmlXPathCheckError(ctxt)) {
   3484 	    xmlXPathSetTypeError(ctxt);
   3485 	    return;
   3486 	}
   3487     }
   3488 
   3489     ret = exsltDateDayAbbreviation(dt);
   3490 
   3491     if (dt != NULL)
   3492 	xmlFree(dt);
   3493 
   3494     if (ret == NULL)
   3495 	xmlXPathReturnEmptyString(ctxt);
   3496     else
   3497 	xmlXPathReturnString(ctxt, xmlStrdup(ret));
   3498 }
   3499 
   3500 
   3501 /**
   3502  * exsltDateHourInDayFunction:
   3503  * @ctxt: an XPath parser context
   3504  * @nargs : the number of arguments
   3505  *
   3506  * Wraps exsltDateHourInDay() for use by the XPath engine.
   3507  */
   3508 X_IN_Y(Hour,Day)
   3509 
   3510 /**
   3511  * exsltDateMinuteInHourFunction:
   3512  * @ctxt: an XPath parser context
   3513  * @nargs : the number of arguments
   3514  *
   3515  * Wraps exsltDateMinuteInHour() for use by the XPath engine.
   3516  */
   3517 X_IN_Y(Minute,Hour)
   3518 
   3519 /**
   3520  * exsltDateSecondInMinuteFunction:
   3521  * @ctxt: an XPath parser context
   3522  * @nargs : the number of arguments
   3523  *
   3524  * Wraps exsltDateSecondInMinute() for use by the XPath engine.
   3525  */
   3526 X_IN_Y(Second,Minute)
   3527 
   3528 /**
   3529  * exsltDateSecondsFunction:
   3530  * @ctxt: an XPath parser context
   3531  * @nargs : the number of arguments
   3532  *
   3533  * Wraps exsltDateSeconds() for use by the XPath engine.
   3534  */
   3535 static void
   3536 exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3537 {
   3538     xmlChar *str = NULL;
   3539     double   ret;
   3540 
   3541     if (nargs > 1) {
   3542 	xmlXPathSetArityError(ctxt);
   3543 	return;
   3544     }
   3545 
   3546     if (nargs == 1) {
   3547 	str = xmlXPathPopString(ctxt);
   3548 	if (xmlXPathCheckError(ctxt)) {
   3549 	    xmlXPathSetTypeError(ctxt);
   3550 	    return;
   3551 	}
   3552     }
   3553 
   3554     ret = exsltDateSeconds(str);
   3555     if (str != NULL)
   3556 	xmlFree(str);
   3557 
   3558     xmlXPathReturnNumber(ctxt, ret);
   3559 }
   3560 
   3561 /**
   3562  * exsltDateAddFunction:
   3563  * @ctxt:  an XPath parser context
   3564  * @nargs:  the number of arguments
   3565  *
   3566  * Wraps exsltDateAdd() for use by the XPath processor.
   3567  */
   3568 static void
   3569 exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3570 {
   3571     xmlChar *ret, *xstr, *ystr;
   3572 
   3573     if (nargs != 2) {
   3574 	xmlXPathSetArityError(ctxt);
   3575 	return;
   3576     }
   3577     ystr = xmlXPathPopString(ctxt);
   3578     if (xmlXPathCheckError(ctxt))
   3579 	return;
   3580 
   3581     xstr = xmlXPathPopString(ctxt);
   3582     if (xmlXPathCheckError(ctxt)) {
   3583         xmlFree(ystr);
   3584 	return;
   3585     }
   3586 
   3587     ret = exsltDateAdd(xstr, ystr);
   3588 
   3589     xmlFree(ystr);
   3590     xmlFree(xstr);
   3591 
   3592     if (ret == NULL)
   3593         xmlXPathReturnEmptyString(ctxt);
   3594     else
   3595 	xmlXPathReturnString(ctxt, ret);
   3596 }
   3597 
   3598 /**
   3599  * exsltDateAddDurationFunction:
   3600  * @ctxt:  an XPath parser context
   3601  * @nargs:  the number of arguments
   3602  *
   3603  * Wraps exsltDateAddDuration() for use by the XPath processor.
   3604  */
   3605 static void
   3606 exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3607 {
   3608     xmlChar *ret, *xstr, *ystr;
   3609 
   3610     if (nargs != 2) {
   3611 	xmlXPathSetArityError(ctxt);
   3612 	return;
   3613     }
   3614     ystr = xmlXPathPopString(ctxt);
   3615     if (xmlXPathCheckError(ctxt))
   3616 	return;
   3617 
   3618     xstr = xmlXPathPopString(ctxt);
   3619     if (xmlXPathCheckError(ctxt)) {
   3620         xmlFree(ystr);
   3621 	return;
   3622     }
   3623 
   3624     ret = exsltDateAddDuration(xstr, ystr);
   3625 
   3626     xmlFree(ystr);
   3627     xmlFree(xstr);
   3628 
   3629     if (ret == NULL)
   3630         xmlXPathReturnEmptyString(ctxt);
   3631     else
   3632 	xmlXPathReturnString(ctxt, ret);
   3633 }
   3634 
   3635 /**
   3636  * exsltDateDifferenceFunction:
   3637  * @ctxt:  an XPath parser context
   3638  * @nargs:  the number of arguments
   3639  *
   3640  * Wraps exsltDateDifference() for use by the XPath processor.
   3641  */
   3642 static void
   3643 exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3644 {
   3645     xmlChar *ret, *xstr, *ystr;
   3646 
   3647     if (nargs != 2) {
   3648 	xmlXPathSetArityError(ctxt);
   3649 	return;
   3650     }
   3651     ystr = xmlXPathPopString(ctxt);
   3652     if (xmlXPathCheckError(ctxt))
   3653 	return;
   3654 
   3655     xstr = xmlXPathPopString(ctxt);
   3656     if (xmlXPathCheckError(ctxt)) {
   3657         xmlFree(ystr);
   3658 	return;
   3659     }
   3660 
   3661     ret = exsltDateDifference(xstr, ystr);
   3662 
   3663     xmlFree(ystr);
   3664     xmlFree(xstr);
   3665 
   3666     if (ret == NULL)
   3667         xmlXPathReturnEmptyString(ctxt);
   3668     else
   3669 	xmlXPathReturnString(ctxt, ret);
   3670 }
   3671 
   3672 /**
   3673  * exsltDateDurationFunction:
   3674  * @ctxt: an XPath parser context
   3675  * @nargs : the number of arguments
   3676  *
   3677  * Wraps exsltDateDuration() for use by the XPath engine
   3678  */
   3679 static void
   3680 exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
   3681 {
   3682     xmlChar *ret;
   3683     xmlChar *number = NULL;
   3684 
   3685     if ((nargs < 0) || (nargs > 1)) {
   3686 	xmlXPathSetArityError(ctxt);
   3687 	return;
   3688     }
   3689 
   3690     if (nargs == 1) {
   3691 	number = xmlXPathPopString(ctxt);
   3692 	if (xmlXPathCheckError(ctxt)) {
   3693 	    xmlXPathSetTypeError(ctxt);
   3694 	    return;
   3695 	}
   3696     }
   3697 
   3698     ret = exsltDateDuration(number);
   3699 
   3700     if (number != NULL)
   3701 	xmlFree(number);
   3702 
   3703     if (ret == NULL)
   3704 	xmlXPathReturnEmptyString(ctxt);
   3705     else
   3706 	xmlXPathReturnString(ctxt, ret);
   3707 }
   3708 
   3709 /**
   3710  * exsltDateRegister:
   3711  *
   3712  * Registers the EXSLT - Dates and Times module
   3713  */
   3714 void
   3715 exsltDateRegister (void)
   3716 {
   3717     xsltRegisterExtModuleFunction ((const xmlChar *) "add",
   3718 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3719 				   exsltDateAddFunction);
   3720     xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
   3721 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3722 				   exsltDateAddDurationFunction);
   3723     xsltRegisterExtModuleFunction ((const xmlChar *) "date",
   3724 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3725 				   exsltDateDateFunction);
   3726 #ifdef WITH_TIME
   3727     xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
   3728 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3729 				   exsltDateDateTimeFunction);
   3730 #endif
   3731     xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
   3732 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3733 				   exsltDateDayAbbreviationFunction);
   3734     xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
   3735 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3736 				   exsltDateDayInMonthFunction);
   3737     xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
   3738 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3739 				   exsltDateDayInWeekFunction);
   3740     xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
   3741 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3742 				   exsltDateDayInYearFunction);
   3743     xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
   3744 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3745 				   exsltDateDayNameFunction);
   3746     xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
   3747 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3748 				   exsltDateDayOfWeekInMonthFunction);
   3749     xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
   3750 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3751 				   exsltDateDifferenceFunction);
   3752     xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
   3753 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3754 				   exsltDateDurationFunction);
   3755     xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
   3756 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3757 				   exsltDateHourInDayFunction);
   3758     xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
   3759 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3760 				   exsltDateLeapYearFunction);
   3761     xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
   3762 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3763 				   exsltDateMinuteInHourFunction);
   3764     xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
   3765 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3766 				   exsltDateMonthAbbreviationFunction);
   3767     xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
   3768 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3769 				   exsltDateMonthInYearFunction);
   3770     xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
   3771 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3772 				   exsltDateMonthNameFunction);
   3773     xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
   3774 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3775 				   exsltDateSecondInMinuteFunction);
   3776     xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
   3777 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3778 				   exsltDateSecondsFunction);
   3779     xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
   3780 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3781 				   exsltDateSumFunction);
   3782     xsltRegisterExtModuleFunction ((const xmlChar *) "time",
   3783 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3784 				   exsltDateTimeFunction);
   3785     xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
   3786 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3787 				   exsltDateWeekInMonthFunction);
   3788     xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
   3789 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3790 				   exsltDateWeekInYearFunction);
   3791     xsltRegisterExtModuleFunction ((const xmlChar *) "year",
   3792 				   (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3793 				   exsltDateYearFunction);
   3794 }
   3795 
   3796 /**
   3797  * exsltDateXpathCtxtRegister:
   3798  *
   3799  * Registers the EXSLT - Dates and Times module for use outside XSLT
   3800  */
   3801 int
   3802 exsltDateXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
   3803 {
   3804     if (ctxt
   3805         && prefix
   3806         && !xmlXPathRegisterNs(ctxt,
   3807                                prefix,
   3808                                (const xmlChar *) EXSLT_DATE_NAMESPACE)
   3809         && !xmlXPathRegisterFuncNS(ctxt,
   3810                                    (const xmlChar *) "add",
   3811                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3812                                    exsltDateAddFunction)
   3813         && !xmlXPathRegisterFuncNS(ctxt,
   3814                                    (const xmlChar *) "add-duration",
   3815                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3816                                    exsltDateAddDurationFunction)
   3817         && !xmlXPathRegisterFuncNS(ctxt,
   3818                                    (const xmlChar *) "date",
   3819                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3820                                    exsltDateDateFunction)
   3821 #ifdef WITH_TIME
   3822         && !xmlXPathRegisterFuncNS(ctxt,
   3823                                    (const xmlChar *) "date-time",
   3824                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3825                                    exsltDateDateTimeFunction)
   3826 #endif
   3827         && !xmlXPathRegisterFuncNS(ctxt,
   3828                                    (const xmlChar *) "day-abbreviation",
   3829                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3830                                    exsltDateDayAbbreviationFunction)
   3831         && !xmlXPathRegisterFuncNS(ctxt,
   3832                                    (const xmlChar *) "day-in-month",
   3833                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3834                                    exsltDateDayInMonthFunction)
   3835         && !xmlXPathRegisterFuncNS(ctxt,
   3836                                    (const xmlChar *) "day-in-week",
   3837                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3838                                    exsltDateDayInWeekFunction)
   3839         && !xmlXPathRegisterFuncNS(ctxt,
   3840                                    (const xmlChar *) "day-in-year",
   3841                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3842                                    exsltDateDayInYearFunction)
   3843         && !xmlXPathRegisterFuncNS(ctxt,
   3844                                    (const xmlChar *) "day-name",
   3845                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3846                                    exsltDateDayNameFunction)
   3847         && !xmlXPathRegisterFuncNS(ctxt,
   3848                                    (const xmlChar *) "day-of-week-in-month",
   3849                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3850                                    exsltDateDayOfWeekInMonthFunction)
   3851         && !xmlXPathRegisterFuncNS(ctxt,
   3852                                    (const xmlChar *) "difference",
   3853                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3854                                    exsltDateDifferenceFunction)
   3855         && !xmlXPathRegisterFuncNS(ctxt,
   3856                                    (const xmlChar *) "duration",
   3857                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3858                                    exsltDateDurationFunction)
   3859         && !xmlXPathRegisterFuncNS(ctxt,
   3860                                    (const xmlChar *) "hour-in-day",
   3861                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3862                                    exsltDateHourInDayFunction)
   3863         && !xmlXPathRegisterFuncNS(ctxt,
   3864                                    (const xmlChar *) "leap-year",
   3865                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3866                                    exsltDateLeapYearFunction)
   3867         && !xmlXPathRegisterFuncNS(ctxt,
   3868                                    (const xmlChar *) "minute-in-hour",
   3869                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3870                                    exsltDateMinuteInHourFunction)
   3871         && !xmlXPathRegisterFuncNS(ctxt,
   3872                                    (const xmlChar *) "month-abbreviation",
   3873                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3874                                    exsltDateMonthAbbreviationFunction)
   3875         && !xmlXPathRegisterFuncNS(ctxt,
   3876                                    (const xmlChar *) "month-in-year",
   3877                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3878                                    exsltDateMonthInYearFunction)
   3879         && !xmlXPathRegisterFuncNS(ctxt,
   3880                                    (const xmlChar *) "month-name",
   3881                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3882                                    exsltDateMonthNameFunction)
   3883         && !xmlXPathRegisterFuncNS(ctxt,
   3884                                    (const xmlChar *) "second-in-minute",
   3885                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3886                                    exsltDateSecondInMinuteFunction)
   3887         && !xmlXPathRegisterFuncNS(ctxt,
   3888                                    (const xmlChar *) "seconds",
   3889                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3890                                    exsltDateSecondsFunction)
   3891         && !xmlXPathRegisterFuncNS(ctxt,
   3892                                    (const xmlChar *) "sum",
   3893                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3894                                    exsltDateSumFunction)
   3895         && !xmlXPathRegisterFuncNS(ctxt,
   3896                                    (const xmlChar *) "time",
   3897                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3898                                    exsltDateTimeFunction)
   3899         && !xmlXPathRegisterFuncNS(ctxt,
   3900                                    (const xmlChar *) "week-in-month",
   3901                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3902                                    exsltDateWeekInMonthFunction)
   3903         && !xmlXPathRegisterFuncNS(ctxt,
   3904                                    (const xmlChar *) "week-in-year",
   3905                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3906                                    exsltDateWeekInYearFunction)
   3907         && !xmlXPathRegisterFuncNS(ctxt,
   3908                                    (const xmlChar *) "year",
   3909                                    (const xmlChar *) EXSLT_DATE_NAMESPACE,
   3910                                    exsltDateYearFunction)) {
   3911         return 0;
   3912     }
   3913     return -1;
   3914 }
   3915