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