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@HiWAAY.net>
11 *   Thomas Broyer <tbroyer@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 */
70typedef 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 */
84typedef struct _exsltDateValDate exsltDateValDate;
85typedef exsltDateValDate *exsltDateValDatePtr;
86struct _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 */
98typedef struct _exsltDateValDuration exsltDateValDuration;
99typedef exsltDateValDuration *exsltDateValDurationPtr;
100struct _exsltDateValDuration {
101    long	        mon;		/* mon stores years also */
102    long        	day;
103    double		sec;            /* sec stores min and hour also */
104};
105
106typedef struct _exsltDateVal exsltDateVal;
107typedef exsltDateVal *exsltDateValPtr;
108struct _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
150static const unsigned long daysInMonth[12] =
151	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
152static 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
181static const unsigned long dayInYearByMonth[12] =
182	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
183static 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 */
203static 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 */
380static 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 */
422static 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 */
482static 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 */
554static 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 */
657static exsltDateValPtr
658exsltDateCreateDate (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 */
682static void
683exsltDateFreeDate (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 */
746static exsltDateValPtr
747exsltDateCurrent (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 */
827static exsltDateValPtr
828exsltDateParse (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
963error:
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 */
977static exsltDateValPtr
978exsltDateParseDuration (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
1071error:
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 */
1103static xmlChar *
1104exsltDateFormatDuration (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 */
1182static xmlChar *
1183exsltDateFormatDateTime (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 */
1208static xmlChar *
1209exsltDateFormatDate (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 */
1233static xmlChar *
1234exsltDateFormatTime (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 */
1260static xmlChar *
1261exsltDateFormat (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 */
1311static 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 */
1350static double
1351exsltDateCastDateToNumber (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 */
1385static 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 */
1424static 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 */
1457static 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 */
1597static void
1598exsltDateNormalize (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 */
1636static 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 */
1701static 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 */
1752static 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 */
1785static xmlChar *
1786exsltDateDateTime (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 */
1818static xmlChar *
1819exsltDateDate (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 */
1862static xmlChar *
1863exsltDateTime (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 */
1910static double
1911exsltDateYear (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 */
1959static xmlXPathObjectPtr
1960exsltDateLeapYear (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 */
1995static double
1996exsltDateMonthInYear (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 */
2048static const xmlChar *
2049exsltDateMonthName (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 */
2096static const xmlChar *
2097exsltDateMonthAbbreviation (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 */
2142static double
2143exsltDateWeekInYear (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 */
2211static double
2212exsltDateWeekInMonth (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 */
2265static double
2266exsltDateDayInYear (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 */
2315static double
2316exsltDateDayInMonth (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 */
2363static double
2364exsltDateDayOfWeekInMonth (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 */
2412static double
2413exsltDateDayInWeek (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 */
2464static const xmlChar *
2465exsltDateDayName (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 */
2504static const xmlChar *
2505exsltDateDayAbbreviation (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 */
2542static double
2543exsltDateHourInDay (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 */
2588static double
2589exsltDateMinuteInHour (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 */
2636static double
2637exsltDateSecondInMinute (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 */
2688static xmlChar *
2689exsltDateAdd (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 */
2746static xmlChar *
2747exsltDateAddDuration (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 */
2798static void
2799exsltDateSumFunction (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 */
2908static double
2909exsltDateSeconds (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 */
2996static xmlChar *
2997exsltDateDifference (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 */
3056static xmlChar *
3057exsltDateDuration (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 */
3097static void
3098exsltDateDateTimeFunction (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 */
3122static void
3123exsltDateDateFunction (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 */
3161static void
3162exsltDateTimeFunction (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 */
3200static void
3201exsltDateYearFunction (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 */
3234static void
3235exsltDateLeapYearFunction (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)						\
3262static void							\
3263exsltDate##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 */
3296X_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 */
3305static void
3306exsltDateMonthNameFunction (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 */
3342static void
3343exsltDateMonthAbbreviationFunction (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 */
3379X_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 */
3388X_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 */
3397X_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 */
3406X_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 */
3415X_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 */
3424X_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 */
3433static void
3434exsltDateDayNameFunction (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 */
3470static void
3471exsltDateDayAbbreviationFunction (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 */
3508X_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 */
3517X_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 */
3526X_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 */
3535static void
3536exsltDateSecondsFunction (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 */
3568static void
3569exsltDateAddFunction (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 */
3605static void
3606exsltDateAddDurationFunction (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 */
3642static void
3643exsltDateDifferenceFunction (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 */
3679static void
3680exsltDateDurationFunction (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 */
3714void
3715exsltDateRegister (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 */
3801int
3802exsltDateXpathCtxtRegister (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