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