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