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