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