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