15ba3bacc225e9cf637e085c715657ee2ed48e0c
[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  * handle duration
15  * implement "other" date/time extension functions
16  */
17
18 #include "libexslt/libexslt.h"
19
20 #include <stdlib.h>
21 #include <string.h>
22
23 #if defined(WIN32) && !defined (__CYGWIN__)
24 #include <win32config.h>
25 #else
26 #include "config.h"
27 #endif
28
29 #include <libxml/tree.h>
30 #include <libxml/xpath.h>
31 #include <libxml/xpathInternals.h>
32
33 #include <libxslt/xsltconfig.h>
34 #include <libxslt/xsltutils.h>
35 #include <libxslt/xsltInternals.h>
36 #include <libxslt/extensions.h>
37
38 #include "exslt.h"
39
40 #ifdef HAVE_TIME_H
41 #include <time.h>
42 #endif
43
44 #if 0
45 #define DEBUG_EXSLT_DATE
46 #endif
47
48 /* types of date and/or time (from schema datatypes) */
49 typedef enum {
50     XS_DATETIME = 1,
51     XS_DATE,
52     XS_TIME,
53     XS_GYEARMONTH,
54     XS_GYEAR,
55     XS_GMONTHDAY,
56     XS_GMONTH,
57     XS_GDAY,
58     XS_DURATION
59 } exsltDateType;
60
61 /* date object */
62 typedef struct _exsltDate exsltDate;
63 typedef exsltDate *exsltDatePtr;
64 struct _exsltDate {
65     exsltDateType       type;
66     long                year;
67     unsigned int        mon     :4;     /* 1 <=  mon    <= 12   */
68     unsigned int        day     :5;     /* 1 <=  day    <= 31   */
69     unsigned int        hour    :5;     /* 0 <=  hour   <= 23   */
70     unsigned int        min     :6;     /* 0 <=  min    <= 59   */
71     double              sec;
72     int                 tz_flag :1;     /* is tzo explicitely set? */
73     int                 tzo     :11;    /* -1440 <= tzo <= 1440 */
74 };
75
76 /****************************************************************
77  *                                                              *
78  *                      Compat./Port. macros                    *
79  *                                                              *
80  ****************************************************************/
81
82 #if defined(HAVE_TIME_H) && defined(HAVE_LOCALTIME)             \
83     && defined(HAVE_TIME) && defined(HAVE_GMTIME)               \
84     && defined(HAVE_MKTIME)
85 #define WITH_TIME
86 #endif
87
88 /****************************************************************
89  *                                                              *
90  *              Convenience macros and functions                *
91  *                                                              *
92  ****************************************************************/
93
94 #define IS_TZO_CHAR(c)                                          \
95         ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
96
97 #define VALID_YEAR(yr)          (yr != 0)
98 #define VALID_MONTH(mon)        ((mon >= 1) && (mon <= 12))
99 /* VALID_DAY should only be used when month is unknown */
100 #define VALID_DAY(day)          ((day >= 1) && (day <= 31))
101 #define VALID_HOUR(hr)          ((hr >= 0) && (hr <= 23))
102 #define VALID_MIN(min)          ((min >= 0) && (min <= 59))
103 #define VALID_SEC(sec)          ((sec >= 0) && (sec < 60))
104 #define VALID_TZO(tzo)          ((tzo > -1440) && (tzo < 1440))
105 #define IS_LEAP(y)                                              \
106         (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
107
108 static const int daysInMonth[12] =
109         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
110 static const int daysInMonthLeap[12] =
111         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
112
113 #define VALID_MDAY(dt)                                          \
114         (IS_LEAP(dt->year) ?                                    \
115                    (dt->day <= daysInMonthLeap[dt->mon - 1]) :  \
116                    (dt->day <= daysInMonth[dt->mon - 1]))
117
118 #define VALID_DATE(dt)                                          \
119         (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
120
121 #define VALID_TIME(dt)                                          \
122         (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) &&          \
123          VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
124
125 #define VALID_DATETIME(dt)                                      \
126         (VALID_DATE(dt) && VALID_TIME(dt))
127
128
129 static const int dayInYearByMonth[12] =
130         { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
131 static const int dayInLeapYearByMonth[12] =
132         { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
133
134 #define DAY_IN_YEAR(day, month, year)                           \
135         ((IS_LEAP(year) ?                                       \
136                 dayInLeapYearByMonth[month - 1] :               \
137                 dayInYearByMonth[month - 1]) + day)
138
139 /**
140  * _exsltDateParseGYear:
141  * @dt:  pointer to a date structure
142  * @str: pointer to the string to analyze
143  *
144  * Parses a xs:gYear without time zone and fills in the appropriate
145  * field of the @dt structure. @str is updated to point just after the
146  * xs:gYear. It is supposed that @dt->year is big enough to contain
147  * the year.
148  *
149  * Returns 0 or the error code
150  */
151 static int
152 _exsltDateParseGYear (exsltDatePtr dt, const xmlChar **str) {
153     const xmlChar *cur = *str, *firstChar;
154     int isneg = 0, digcnt = 0;
155
156     if (((*cur < '0') || (*cur > '9')) &&
157         (*cur != '-') && (*cur != '+'))
158         return -1;
159
160     if (*cur == '-') {
161         isneg = 1;
162         cur++;
163     }
164
165     firstChar = cur;
166
167     while ((*cur >= '0') && (*cur <= '9')) {
168         dt->year = dt->year * 10 + (*cur - '0');
169         cur++;
170         digcnt++;
171     }
172
173     /* year must be at least 4 digits (CCYY); over 4
174      * digits cannot have a leading zero. */
175     if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
176         return 1;
177
178     if (isneg)
179         dt->year = - dt->year;
180
181     if (!VALID_YEAR(dt->year))
182         return 2;
183
184     *str = cur;
185
186 #ifdef DEBUG_EXSLT_DATE
187     xsltGenericDebug(xsltGenericDebugContext,
188                      "Parsed year %04i\n", dt->year);
189 #endif
190
191     return 0;
192 }
193
194 /**
195  * FORMAT_GYEAR:
196  * @dt:  the #exsltDate to format
197  * @cur: a pointer to an allocated buffer
198  *
199  * Formats @dt in xsl:gYear format. Result is appended to @cur and
200  * @cur is updated to point after the xsl:gYear.
201  */
202 #define FORMAT_GYEAR(dt, cur)                                   \
203         if (dt->year < 0) {                                     \
204             *cur = '-';                                         \
205             cur++;                                              \
206         }                                                       \
207         {                                                       \
208             int year = (dt->year < 0) ? - dt->year : dt->year;  \
209             xmlChar tmp_buf[100], *tmp = tmp_buf;               \
210             /* virtually adds leading zeros */                  \
211             while (year < 1000)                                 \
212                 year *= 10;                                     \
213             /* result is in reverse-order */                    \
214             while (year > 0) {                                  \
215                 *tmp = '0' + (year % 10);                       \
216                 year /= 10;                                     \
217                 tmp++;                                          \
218             }                                                   \
219             /* restore the correct order */                     \
220             while (tmp > tmp_buf) {                             \
221                 tmp--;                                          \
222                 *cur = *tmp;                                    \
223                 cur++;                                          \
224             }                                                   \
225         }
226
227 /**
228  * PARSE_2_DIGITS:
229  * @num:  the integer to fill in
230  * @cur:  an #xmlChar *
231  * @invalid: an integer
232  *
233  * Parses a 2-digits integer and updates @num with the value. @cur is
234  * updated to point just after the integer.
235  * In case of error, @invalid is set to %TRUE, values of @num and
236  * @cur are undefined.
237  */
238 #define PARSE_2_DIGITS(num, cur, invalid)                       \
239         if ((cur[0] < '0') || (cur[0] > '9') ||                 \
240             (cur[1] < '0') || (cur[1] > '9'))                   \
241             invalid = 1;                                        \
242         else                                                    \
243             num = (cur[0] - '0') * 10 + (cur[1] - '0');         \
244         cur += 2;
245
246 /**
247  * FORMAT_2_DIGITS:
248  * @num:  the integer to format
249  * @cur: a pointer to an allocated buffer
250  *
251  * Formats a 2-digits integer. Result is appended to @cur and
252  * @cur is updated to point after the integer.
253  */
254 #define FORMAT_2_DIGITS(num, cur)                               \
255         *cur = '0' + ((num / 10) % 10);                         \
256         cur++;                                                  \
257         *cur = '0' + (num % 10);                                \
258         cur++;
259
260 /**
261  * PARSE_FLOAT:
262  * @num:  the double to fill in
263  * @cur:  an #xmlChar *
264  * @invalid: an integer
265  *
266  * Parses a float and updates @num with the value. @cur is
267  * updated to point just after the float. The float must have a
268  * 2-digits integer part and may or may not have a decimal part.
269  * In case of error, @invalid is set to %TRUE, values of @num and
270  * @cur are undefined.
271  */
272 #define PARSE_FLOAT(num, cur, invalid)                          \
273         PARSE_2_DIGITS(num, cur, invalid);                      \
274         if (!invalid && (*cur == '.')) {                        \
275             double mult = 1;                                    \
276             cur++;                                              \
277             if ((*cur < '0') || (*cur > '9'))                   \
278                 invalid = 1;                                    \
279             while ((*cur >= '0') && (*cur <= '9')) {            \
280                 mult /= 10;                                     \
281                 num += (*cur - '0') * mult;                     \
282                 cur++;                                          \
283             }                                                   \
284         }
285
286 /**
287  * FORMAT_FLOAT:
288  * @num:  the double to format
289  * @cur: a pointer to an allocated buffer
290  *
291  * Formats a float. Result is appended to @cur and @cur is updated to
292  * point after the integer. The float representation has a 2-digits
293  * integer part and may or may not have a decimal part.
294  */
295 #define FORMAT_FLOAT(num, cur)                                  \
296         {                                                       \
297             xmlChar *sav, *str;                                 \
298             if (num < 10.0)                                     \
299                 *cur++ = '0';                                   \
300             str = xmlXPathCastNumberToString(num);              \
301             sav = str;                                          \
302             while (*str != 0)                                   \
303                 *cur++ = *str++;                                \
304             xmlFree(sav);                                       \
305         }
306
307 /**
308  * _exsltDateParseGMonth:
309  * @dt:  pointer to a date structure
310  * @str: pointer to the string to analyze
311  *
312  * Parses a xs:gMonth without time zone and fills in the appropriate
313  * field of the @dt structure. @str is updated to point just after the
314  * xs:gMonth.
315  *
316  * Returns 0 or the error code
317  */
318 static int
319 _exsltDateParseGMonth (exsltDatePtr dt, const xmlChar **str) {
320     const xmlChar *cur = *str;
321     int ret = 0;
322
323     PARSE_2_DIGITS(dt->mon, cur, ret);
324     if (ret != 0)
325         return ret;
326
327     if (!VALID_MONTH(dt->mon))
328         return 2;
329
330     *str = cur;
331
332 #ifdef DEBUG_EXSLT_DATE
333     xsltGenericDebug(xsltGenericDebugContext,
334                      "Parsed month %02i\n", dt->mon);
335 #endif
336
337     return 0;
338 }
339
340 /**
341  * FORMAT_GMONTH:
342  * @dt:  the #exsltDate to format
343  * @cur: a pointer to an allocated buffer
344  *
345  * Formats @dt in xsl:gMonth format. Result is appended to @cur and
346  * @cur is updated to point after the xsl:gMonth.
347  */
348 #define FORMAT_GMONTH(dt, cur)                                  \
349         FORMAT_2_DIGITS(dt->mon, cur)
350
351 /**
352  * _exsltDateParseGDay:
353  * @dt:  pointer to a date structure
354  * @str: pointer to the string to analyze
355  *
356  * Parses a xs:gDay without time zone and fills in the appropriate
357  * field of the @dt structure. @str is updated to point just after the
358  * xs:gDay.
359  *
360  * Returns 0 or the error code
361  */
362 static int
363 _exsltDateParseGDay (exsltDatePtr dt, const xmlChar **str) {
364     const xmlChar *cur = *str;
365     int ret = 0;
366
367     PARSE_2_DIGITS(dt->day, cur, ret);
368     if (ret != 0)
369         return ret;
370
371     if (!VALID_DAY(dt->day))
372         return 2;
373
374     *str = cur;
375
376 #ifdef DEBUG_EXSLT_DATE
377     xsltGenericDebug(xsltGenericDebugContext,
378                      "Parsed day %02i\n", dt->day);
379 #endif
380
381     return 0;
382 }
383
384 /**
385  * FORMAT_GDAY:
386  * @dt:  the #exsltDate to format
387  * @cur: a pointer to an allocated buffer
388  *
389  * Formats @dt in xsl:gDay format. Result is appended to @cur and
390  * @cur is updated to point after the xsl:gDay.
391  */
392 #define FORMAT_GDAY(dt, cur)                                    \
393         FORMAT_2_DIGITS(dt->day, cur)
394
395 /**
396  * FORMAT_DATE:
397  * @dt:  the #exsltDate to format
398  * @cur: a pointer to an allocated buffer
399  *
400  * Formats @dt in xsl:date format. Result is appended to @cur and
401  * @cur is updated to point after the xsl:date.
402  */
403 #define FORMAT_DATE(dt, cur)                                    \
404         FORMAT_GYEAR(dt, cur);                                  \
405         *cur = '-';                                             \
406         cur++;                                                  \
407         FORMAT_GMONTH(dt, cur);                                 \
408         *cur = '-';                                             \
409         cur++;                                                  \
410         FORMAT_GDAY(dt, cur);
411
412 /**
413  * _exsltDateParseTime:
414  * @dt:  pointer to a date structure
415  * @str: pointer to the string to analyze
416  *
417  * Parses a xs:time without time zone and fills in the appropriate
418  * fields of the @dt structure. @str is updated to point just after the
419  * xs:time.
420  * In case of error, values of @dt fields are undefined.
421  *
422  * Returns 0 or the error code
423  */
424 static int
425 _exsltDateParseTime (exsltDatePtr dt, const xmlChar **str) {
426     const xmlChar *cur = *str;
427     int ret = 0;
428
429     PARSE_2_DIGITS(dt->hour, cur, ret);
430     if (ret != 0)
431         return ret;
432
433     if (*cur != ':')
434         return 1;
435     cur++;
436
437     PARSE_2_DIGITS(dt->min, cur, ret);
438     if (ret != 0)
439         return ret;
440
441     if (*cur != ':')
442         return 1;
443     cur++;
444
445     PARSE_FLOAT(dt->sec, cur, ret);
446     if (ret != 0)
447         return ret;
448
449     if (!VALID_TIME(dt))
450         return 2;
451
452     *str = cur;
453
454 #ifdef DEBUG_EXSLT_DATE
455     xsltGenericDebug(xsltGenericDebugContext,
456                      "Parsed time %02i:%02i:%02.f\n",
457                      dt->hour, dt->min, dt->sec);
458 #endif
459
460     return 0;
461 }
462
463 /**
464  * FORMAT_TIME:
465  * @dt:  the #exsltDate to format
466  * @cur: a pointer to an allocated buffer
467  *
468  * Formats @dt in xsl:time format. Result is appended to @cur and
469  * @cur is updated to point after the xsl:time.
470  */
471 #define FORMAT_TIME(dt, cur)                                    \
472         FORMAT_2_DIGITS(dt->hour, cur);                         \
473         *cur = ':';                                             \
474         cur++;                                                  \
475         FORMAT_2_DIGITS(dt->min, cur);                          \
476         *cur = ':';                                             \
477         cur++;                                                  \
478         FORMAT_FLOAT(dt->sec, cur);
479
480 /**
481  * _exsltDateParseTimeZone:
482  * @dt:  pointer to a date structure
483  * @str: pointer to the string to analyze
484  *
485  * Parses a time zone without time zone and fills in the appropriate
486  * field of the @dt structure. @str is updated to point just after the
487  * time zone.
488  *
489  * Returns 0 or the error code
490  */
491 static int
492 _exsltDateParseTimeZone (exsltDatePtr dt, const xmlChar **str) {
493     const xmlChar *cur = *str;
494     int ret = 0;
495
496     if (str == NULL)
497         return -1;
498
499     switch (*cur) {
500     case 0:
501         dt->tz_flag = 0;
502         dt->tzo = 0;
503
504         break;
505
506     case 'Z':
507         dt->tz_flag = 1;
508         dt->tzo = 0;
509
510         cur++;
511         break;
512
513     case '+':
514     case '-': {
515         int isneg = 0, tmp = 0;
516         isneg = (*cur == '-');
517
518         cur++;
519
520         PARSE_2_DIGITS(tmp, cur, ret);
521         if (ret != 0)
522             return ret;
523         if (!VALID_HOUR(tmp))
524             return 2;
525
526         if (*cur != ':')
527             return 1;
528         cur++;
529
530         dt->tzo = tmp * 60;
531
532         PARSE_2_DIGITS(tmp, cur, ret);
533         if (ret != 0)
534             return ret;
535         if (!VALID_MIN(tmp))
536             return 2;
537
538         dt->tzo += tmp;
539         if (isneg)
540             dt->tzo = - dt->tzo;
541
542         if (!VALID_TZO(dt->tzo))
543             return 2;
544
545         break;
546       }
547     default:
548         return 1;
549     }
550
551     *str = cur;
552
553 #ifdef DEBUG_EXSLT_DATE
554     xsltGenericDebug(xsltGenericDebugContext,
555                      "Parsed time zone offset (%s) %i\n",
556                      dt->tz_flag ? "explicit" : "implicit", dt->tzo);
557 #endif
558
559     return 0;
560 }
561
562 /**
563  * FORMAT_TZ:
564  * @dt:  the #exsltDate to format
565  * @cur: a pointer to an allocated buffer
566  *
567  * Formats @dt timezone. Result is appended to @cur and
568  * @cur is updated to point after the timezone.
569  */
570 #define FORMAT_TZ(dt, cur)                                      \
571         if (dt->tzo == 0) {                                     \
572             *cur = 'Z';                                         \
573             cur++;                                              \
574         } else {                                                \
575             int aTzo = (dt->tzo < 0) ? - dt->tzo : dt->tzo;     \
576             int tzHh = aTzo / 60, tzMm = aTzo % 60;             \
577             *cur = (dt->tzo < 0) ? '-' : '+' ;                  \
578             cur++;                                              \
579             FORMAT_2_DIGITS(tzHh, cur);                         \
580             *cur = ':';                                         \
581             cur++;                                              \
582             FORMAT_2_DIGITS(tzMm, cur);                         \
583         }
584
585 /****************************************************************
586  *                                                              *
587  *      XML Schema Dates/Times Datatypes Handling               *
588  *                                                              *
589  ****************************************************************/
590
591 /**
592  * exsltDateCreateDate:
593  *
594  * Creates a new #exsltDate, uninitialized.
595  *
596  * Returns the #exsltDate
597  */
598 static exsltDatePtr
599 exsltDateCreateDate (void) {
600     exsltDatePtr ret;
601
602     ret = (exsltDatePtr) xmlMalloc(sizeof(exsltDate));
603     if (ret == NULL) {
604         xsltGenericError(xsltGenericErrorContext,
605                          "exsltDateCreateDate: out of memory\n");
606         return (NULL);
607     }
608     memset (ret, 0, sizeof(exsltDate));
609
610     return ret;
611 }
612
613 /**
614  * exsltDateFreeDate:
615  * @date: an #exsltDatePtr
616  *
617  * Frees up the @date
618  */
619 static void
620 exsltDateFreeDate (exsltDatePtr date) {
621     if (date == NULL)
622         return;
623
624     xmlFree(date);
625 }
626
627 #ifdef WITH_TIME
628 /**
629  * exsltDateCurrent:
630  *
631  * Returns the current date and time.
632  */
633 static exsltDatePtr
634 exsltDateCurrent (void) {
635     struct tm *localTm, *gmTm;
636     time_t secs;
637     exsltDatePtr ret;
638
639     ret = exsltDateCreateDate();
640     if (ret == NULL)
641         return NULL;
642
643     /* get current time */
644     secs    = time(NULL);
645     localTm = localtime(&secs);
646
647     ret->type = XS_DATETIME;
648
649     /* get real year, not years since 1900 */
650     ret->year = localTm->tm_year + 1900;
651
652     ret->mon  = localTm->tm_mon + 1;
653     ret->day  = localTm->tm_mday;
654     ret->hour = localTm->tm_hour;
655     ret->min  = localTm->tm_min;
656
657     /* floating point seconds */
658     ret->sec  = (double) localTm->tm_sec;
659
660     /* determine the time zone offset from local to gm time */
661     gmTm         = gmtime(&secs);
662     ret->tz_flag = 0;
663     ret->tzo     = (((ret->day * 1440) + (ret->hour * 60) + ret->min) -
664                     ((gmTm->tm_mday * 1440) + (gmTm->tm_hour * 60) +
665                       gmTm->tm_min));
666
667     return ret;
668 }
669 #endif
670
671 /**
672  * exsltDateParse:
673  * @dateTime:  string to analyse
674  *
675  * Parses a date/time string
676  *
677  * Returns a newly built #exsltDatePtr of NULL in case of error
678  */
679 static exsltDatePtr
680 exsltDateParse (const xmlChar *dateTime) {
681     exsltDatePtr dt;
682     int ret;
683     const xmlChar *cur = dateTime;
684
685 #define RETURN_TYPE_IF_VALID(t)                                 \
686     if (IS_TZO_CHAR(*cur)) {                                    \
687         ret = _exsltDateParseTimeZone(dt, &cur);                \
688         if (ret == 0) {                                         \
689             if (*cur != 0)                                      \
690                 goto error;                                     \
691             dt->type = t;                                       \
692             return dt;                                          \
693         }                                                       \
694     }
695
696     if (dateTime == NULL)
697         return NULL;
698
699     if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
700         return NULL;
701
702     dt = exsltDateCreateDate();
703     if (dt == NULL)
704         return NULL;
705
706     if ((cur[0] == '-') && (cur[1] == '-')) {
707         /*
708          * It's an incomplete date (xs:gMonthDay, xs:gMonth or
709          * xs:gDay)
710          */
711         cur += 2;
712
713         /* is it an xs:gDay? */
714         if (*cur == '-') {
715           ++cur;
716             ret = _exsltDateParseGDay(dt, &cur);
717             if (ret != 0)
718                 goto error;
719
720             RETURN_TYPE_IF_VALID(XS_GDAY);
721
722             goto error;
723         }
724
725         /*
726          * it should be an xs:gMonthDay or xs:gMonth
727          */
728         ret = _exsltDateParseGMonth(dt, &cur);
729         if (ret != 0)
730             goto error;
731
732         if (*cur != '-')
733             goto error;
734         cur++;
735
736         /* is it an xs:gMonth? */
737         if (*cur == '-') {
738             cur++;
739             RETURN_TYPE_IF_VALID(XS_GMONTH);
740             goto error;
741         }
742
743         /* it should be an xs:gMonthDay */
744         ret = _exsltDateParseGDay(dt, &cur);
745         if (ret != 0)
746             goto error;
747
748         RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
749
750         goto error;
751     }
752
753     /*
754      * It's a right-truncated date or an xs:time.
755      * Try to parse an xs:time then fallback on right-truncated dates.
756      */
757     if ((*cur >= '0') && (*cur <= '9')) {
758         ret = _exsltDateParseTime(dt, &cur);
759         if (ret == 0) {
760             /* it's an xs:time */
761             RETURN_TYPE_IF_VALID(XS_TIME);
762         }
763     }
764
765     /* fallback on date parsing */
766     cur = dateTime;
767
768     ret = _exsltDateParseGYear(dt, &cur);
769     if (ret != 0)
770         goto error;
771
772     /* is it an xs:gYear? */
773     RETURN_TYPE_IF_VALID(XS_GYEAR);
774
775     if (*cur != '-')
776         goto error;
777     cur++;
778
779     ret = _exsltDateParseGMonth(dt, &cur);
780     if (ret != 0)
781         goto error;
782
783     /* is it an xs:gYearMonth? */
784     RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
785
786     if (*cur != '-')
787         goto error;
788     cur++;
789
790     ret = _exsltDateParseGDay(dt, &cur);
791     if ((ret != 0) || !VALID_DATE(dt))
792         goto error;
793
794     /* is it an xs:date? */
795     RETURN_TYPE_IF_VALID(XS_DATE);
796
797     if (*cur != 'T')
798         goto error;
799     cur++;
800
801     /* it should be an xs:dateTime */
802     ret = _exsltDateParseTime(dt, &cur);
803     if (ret != 0)
804         goto error;
805
806     ret = _exsltDateParseTimeZone(dt, &cur);
807     if ((ret != 0) || (*cur != 0) || !VALID_DATETIME(dt))
808         goto error;
809
810     dt->type = XS_DATETIME;
811
812     return dt;
813
814 error:
815     if (dt != NULL)
816         exsltDateFreeDate(dt);
817     return NULL;
818 }
819
820 /**
821  * exsltDateFormatDateTime:
822  * @dt: an #exsltDate
823  *
824  * Formats @dt in xs:dateTime format.
825  *
826  * Returns a newly allocated string, or NULL in case of error
827  */
828 static xmlChar *
829 exsltDateFormatDateTime (const exsltDatePtr dt) {
830     xmlChar buf[100], *cur = buf;
831
832     if ((dt == NULL) || !VALID_DATETIME(dt))
833         return NULL;
834
835     FORMAT_DATE(dt, cur);
836     *cur = 'T';
837     cur++;
838     FORMAT_TIME(dt, cur);
839     FORMAT_TZ(dt, cur);
840     *cur = 0;
841
842     return xmlStrdup(buf);
843 }
844
845 /**
846  * exsltDateFormatDate:
847  * @dt: an #exsltDate
848  *
849  * Formats @dt in xs:date format.
850  *
851  * Returns a newly allocated string, or NULL in case of error
852  */
853 static xmlChar *
854 exsltDateFormatDate (const exsltDatePtr dt) {
855     xmlChar buf[100], *cur = buf;
856
857     if ((dt == NULL) || !VALID_DATETIME(dt))
858         return NULL;
859
860     FORMAT_DATE(dt, cur);
861     if (dt->tz_flag || (dt->tzo != 0)) {
862         FORMAT_TZ(dt, cur);
863     }
864     *cur = 0;
865
866     return xmlStrdup(buf);
867 }
868
869 /**
870  * exsltDateFormatTime:
871  * @dt: an #exsltDate
872  *
873  * Formats @dt in xs:time format.
874  *
875  * Returns a newly allocated string, or NULL in case of error
876  */
877 static xmlChar *
878 exsltDateFormatTime (const exsltDatePtr dt) {
879     xmlChar buf[100], *cur = buf;
880
881     if ((dt == NULL) || !VALID_TIME(dt))
882         return NULL;
883
884     FORMAT_TIME(dt, cur);
885     if (dt->tz_flag || (dt->tzo != 0)) {
886         FORMAT_TZ(dt, cur);
887     }
888     *cur = 0;
889
890     return xmlStrdup(buf);
891 }
892
893 /****************************************************************
894  *                                                              *
895  *              EXSLT - Dates and Times functions               *
896  *                                                              *
897  ****************************************************************/
898
899 /**
900  * exsltDateDateTime:
901  *
902  * Implements the EXSLT - Dates and Times date-time() function:
903  *     string date:date-time()
904  * 
905  * Returns the current date and time as a date/time string.
906  */
907 static xmlChar *
908 exsltDateDateTime (void) {
909     xmlChar *ret = NULL;
910 #ifdef WITH_TIME
911     exsltDatePtr cur;
912
913     cur = exsltDateCurrent();
914     if (cur != NULL) {
915         ret = exsltDateFormatDateTime(cur);
916         exsltDateFreeDate(cur);
917     }
918 #endif
919
920     return ret;
921 }
922
923 /**
924  * exsltDateDate:
925  * @dateTime: a date/time string
926  *
927  * Implements the EXSLT - Dates and Times date() function:
928  *     string date:date (string?)
929  * 
930  * Returns the date specified in the date/time string given as the
931  * argument.  If no argument is given, then the current local
932  * date/time, as returned by date:date-time is used as a default
933  * argument.
934  * The date/time string specified as an argument must be a string in
935  * the format defined as the lexical representation of either
936  * xs:dateTime or xs:date.  If the argument is not in either of these
937  * formats, returns NULL.
938  */
939 static xmlChar *
940 exsltDateDate (const xmlChar *dateTime) {
941     exsltDatePtr dt = NULL;
942     xmlChar *ret = NULL;
943
944     if (dateTime == NULL) {
945 #ifdef WITH_TIME
946         dt = exsltDateCurrent();
947         if (dt == NULL)
948 #endif
949             return NULL;
950     } else {
951         dt = exsltDateParse(dateTime);
952         if (dt == NULL)
953             return NULL;
954         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
955             exsltDateFreeDate(dt);
956             return NULL;
957         }
958     }
959
960     ret = exsltDateFormatDate(dt);
961     exsltDateFreeDate(dt);
962
963     return ret;
964 }
965
966 /**
967  * exsltDateTime:
968  * @dateTime: a date/time string
969  *
970  * Implements the EXSLT - Dates and Times time() function:
971  *     string date:time (string?)
972  * 
973  * Returns the time specified in the date/time string given as the
974  * argument.  If no argument is given, then the current local
975  * date/time, as returned by date:date-time is used as a default
976  * argument.
977  * The date/time string specified as an argument must be a string in
978  * the format defined as the lexical representation of either
979  * xs:dateTime or xs:time.  If the argument is not in either of these
980  * formats, returns NULL.
981  */
982 static xmlChar *
983 exsltDateTime (const xmlChar *dateTime) {
984     exsltDatePtr dt = NULL;
985     xmlChar *ret = NULL;
986
987     if (dateTime == NULL) {
988 #ifdef WITH_TIME
989         dt = exsltDateCurrent();
990         if (dt == NULL)
991 #endif
992             return NULL;
993     } else {
994         dt = exsltDateParse(dateTime);
995         if (dt == NULL)
996             return NULL;
997         if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
998             exsltDateFreeDate(dt);
999             return NULL;
1000         }
1001     }
1002
1003     ret = exsltDateFormatTime(dt);
1004     exsltDateFreeDate(dt);
1005
1006     return ret;
1007 }
1008
1009 /**
1010  * exsltDateYear:
1011  * @dateTime: a date/time string
1012  *
1013  * Implements the EXSLT - Dates and Times year() function
1014  *    number date:year (string?)
1015  * Returns the year of a date as a number.  If no argument is given,
1016  * then the current local date/time, as returned by date:date-time is
1017  * used as a default argument.
1018  * The date/time string specified as the first argument must be a
1019  * right-truncated string in the format defined as the lexical
1020  * representation of xs:dateTime in one of the formats defined in [XML
1021  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1022  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1023  *  - xs:date (CCYY-MM-DD)
1024  *  - xs:gYearMonth (CCYY-MM)
1025  *  - xs:gYear (CCYY)
1026  * If the date/time string is not in one of these formats, then NaN is
1027  * returned.
1028  */
1029 static double
1030 exsltDateYear (const xmlChar *dateTime) {
1031     exsltDatePtr dt;
1032     double ret;
1033
1034     if (dateTime == NULL) {
1035 #ifdef WITH_TIME
1036         dt = exsltDateCurrent();
1037         if (dt == NULL)
1038 #endif
1039             return xmlXPathNAN;
1040     } else {
1041         dt = exsltDateParse(dateTime);
1042         if (dt == NULL)
1043             return xmlXPathNAN;
1044         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1045             (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
1046             exsltDateFreeDate(dt);
1047             return xmlXPathNAN;
1048         }
1049     }
1050
1051     ret = (double) dt->year;
1052     exsltDateFreeDate(dt);
1053
1054     return ret;
1055 }
1056
1057 /**
1058  * exsltDateLeapYear:
1059  * @dateTime: a date/time string
1060  *
1061  * Implements the EXSLT - Dates and Times leap-year() function:
1062  *    boolean date:leap-yea (string?)
1063  * Returns true if the year given in a date is a leap year.  If no
1064  * argument is given, then the current local date/time, as returned by
1065  * date:date-time is used as a default argument.
1066  * The date/time string specified as the first argument must be a
1067  * right-truncated string in the format defined as the lexical
1068  * representation of xs:dateTime in one of the formats defined in [XML
1069  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1070  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1071  *  - xs:date (CCYY-MM-DD)
1072  *  - xs:gYearMonth (CCYY-MM)
1073  *  - xs:gYear (CCYY)
1074  * If the date/time string is not in one of these formats, then NaN is
1075  * returned.
1076  */
1077 static xmlXPathObjectPtr
1078 exsltDateLeapYear (const xmlChar *dateTime) {
1079     double year;
1080     int yr;
1081
1082     year =  exsltDateYear(dateTime);
1083     if (xmlXPathIsNaN(year))
1084         return xmlXPathNewFloat(xmlXPathNAN);
1085
1086     yr = (int) year;
1087     if (IS_LEAP(yr))
1088         return xmlXPathNewBoolean(1);
1089
1090     return xmlXPathNewBoolean(0);
1091 }
1092
1093 /**
1094  * exsltDateMonthInYear:
1095  * @dateTime: a date/time string
1096  *
1097  * Implements the EXSLT - Dates and Times month-in-year() function:
1098  *    number date:month-in-year (string?)
1099  * Returns the month of a date as a number.  If no argument is given,
1100  * then the current local date/time, as returned by date:date-time is
1101  * used the default argument.
1102  * The date/time string specified as the argument is a left or
1103  * right-truncated string in the format defined as the lexical
1104  * representation of xs:dateTime in one of the formats defined in [XML
1105  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1106  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1107  *  - xs:date (CCYY-MM-DD)
1108  *  - xs:gYearMonth (CCYY-MM)
1109  *  - xs:gMonth (--MM--)
1110  *  - xs:gMonthDay (--MM-DD)
1111  * If the date/time string is not in one of these formats, then NaN is
1112  * returned.
1113  */
1114 static double
1115 exsltDateMonthInYear (const xmlChar *dateTime) {
1116     exsltDatePtr dt;
1117     double ret;
1118
1119     if (dateTime == NULL) {
1120 #ifdef WITH_TIME
1121         dt = exsltDateCurrent();
1122         if (dt == NULL)
1123 #endif
1124             return xmlXPathNAN;
1125     } else {
1126         dt = exsltDateParse(dateTime);
1127         if (dt == NULL)
1128             return xmlXPathNAN;
1129         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1130             (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
1131             (dt->type != XS_GMONTHDAY)) {
1132             exsltDateFreeDate(dt);
1133             return xmlXPathNAN;
1134         }
1135     }
1136
1137     ret = (double) dt->mon;
1138     exsltDateFreeDate(dt);
1139
1140     return ret;
1141 }
1142
1143 /**
1144  * exsltDateMonthName:
1145  * @dateTime: a date/time string
1146  *
1147  * Implements the EXSLT - Dates and Time month-name() function
1148  *    string date:month-name (string?)
1149  * Returns the full name of the month of a date.  If no argument is
1150  * given, then the current local date/time, as returned by
1151  * date:date-time is used the default argument.
1152  * The date/time string specified as the argument is a left or
1153  * right-truncated string in the format defined as the lexical
1154  * representation of xs:dateTime in one of the formats defined in [XML
1155  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1156  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1157  *  - xs:date (CCYY-MM-DD)
1158  *  - xs:gYearMonth (CCYY-MM)
1159  *  - xs:gMonth (--MM--)
1160  * If the date/time string is not in one of these formats, then an
1161  * empty string ('') is returned.
1162  * The result is an English month name: one of 'January', 'February',
1163  * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
1164  * 'October', 'November' or 'December'.
1165  */
1166 static const xmlChar *
1167 exsltDateMonthName (const xmlChar *dateTime) {
1168     static const xmlChar monthNames[13][10] = {
1169         { 0 },
1170         { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
1171         { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
1172         { 'M', 'a', 'r', 'c', 'h', 0 },
1173         { 'A', 'p', 'r', 'i', 'l', 0 },
1174         { 'M', 'a', 'y', 0 },
1175         { 'J', 'u', 'n', 'e', 0 },
1176         { 'J', 'u', 'l', 'y', 0 },
1177         { 'A', 'u', 'g', 'u', 's', 't', 0 },
1178         { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
1179         { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
1180         { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
1181         { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
1182     };
1183     int month;
1184     month = exsltDateMonthInYear(dateTime);
1185     if (!VALID_MONTH(month))
1186       month = 0;
1187     return monthNames[month];
1188 }
1189
1190 /**
1191  * exsltDateMonthAbbreviation:
1192  * @dateTime: a date/time string
1193  *
1194  * Implements the EXSLT - Dates and Time month-abbreviation() function
1195  *    string date:month-abbreviation (string?)
1196  * Returns the abbreviation of the month of a date.  If no argument is
1197  * given, then the current local date/time, as returned by
1198  * date:date-time is used the default argument.
1199  * The date/time string specified as the argument is a left or
1200  * right-truncated string in the format defined as the lexical
1201  * representation of xs:dateTime in one of the formats defined in [XML
1202  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1203  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1204  *  - xs:date (CCYY-MM-DD)
1205  *  - xs:gYearMonth (CCYY-MM)
1206  *  - xs:gMonth (--MM--)
1207  * If the date/time string is not in one of these formats, then an
1208  * empty string ('') is returned.
1209  * The result is an English month abbreviation: one of 'Jan', 'Feb',
1210  * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
1211  * 'Dec'.
1212  */
1213 static const xmlChar *
1214 exsltDateMonthAbbreviation (const xmlChar *dateTime) {
1215     static const xmlChar monthAbbreviations[13][4] = {
1216         { 0 },
1217         { 'J', 'a', 'n', 0 },
1218         { 'F', 'e', 'b', 0 },
1219         { 'M', 'a', 'r', 0 },
1220         { 'A', 'p', 'r', 0 },
1221         { 'M', 'a', 'y', 0 },
1222         { 'J', 'u', 'n', 0 },
1223         { 'J', 'u', 'l', 0 },
1224         { 'A', 'u', 'g', 0 },
1225         { 'S', 'e', 'p', 0 },
1226         { 'O', 'c', 't', 0 },
1227         { 'N', 'o', 'v', 0 },
1228         { 'D', 'e', 'c', 0 }
1229     };
1230     int month;
1231     month = exsltDateMonthInYear(dateTime);
1232     if(!VALID_MONTH(month))
1233       month = 0;
1234     return monthAbbreviations[month];
1235 }
1236
1237 /**
1238  * _exsltDayInWeek:
1239  * @yday: year day (1-366)
1240  * @yr: year
1241  *
1242  * Determine the day-in-week from @yday and @yr. 0001-01-01 was
1243  * a Monday so all other days are calculated from there. Take the 
1244  * number of years since (or before) add the number of leap years and
1245  * the day-in-year and mod by 7. This is a function  because negative
1246  * years must be handled a little differently and there is no zero year.
1247  *
1248  * Returns day in week (Sunday = 0)
1249  */
1250 static int
1251 _exsltDateDayInWeek(int yday, long yr)
1252 {
1253     int ret;
1254
1255     if (yr < 0) {
1256         ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
1257         if (ret < 0) 
1258             ret += 7;
1259     } else
1260         ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
1261
1262     return ret;
1263 }
1264
1265 /**
1266  * exsltDateWeekInYear:
1267  * @dateTime: a date/time string
1268  *
1269  * Implements the EXSLT - Dates and Times week-in-year() function
1270  *    number date:week-in-year (string?)
1271  * Returns the week of the year as a number.  If no argument is given,
1272  * then the current local date/time, as returned by date:date-time is
1273  * used as the default argument.  For the purposes of numbering,
1274  * counting follows ISO 8601: week 1 in a year is the week containing
1275  * the first Thursday of the year, with new weeks beginning on a
1276  * Monday.
1277  * The date/time string specified as the argument is a right-truncated
1278  * string in the format defined as the lexical representation of
1279  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
1280  * Datatypes].  The permitted formats are as follows:
1281  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1282  *  - xs:date (CCYY-MM-DD)
1283  * If the date/time string is not in one of these formats, then NaN is
1284  * returned.
1285  */
1286 static double
1287 exsltDateWeekInYear (const xmlChar *dateTime) {
1288     exsltDatePtr dt;
1289     int fdiy, fdiw, ret;
1290
1291     if (dateTime == NULL) {
1292 #ifdef WITH_TIME
1293         dt = exsltDateCurrent();
1294         if (dt == NULL)
1295 #endif
1296             return xmlXPathNAN;
1297     } else {
1298         dt = exsltDateParse(dateTime);
1299         if (dt == NULL)
1300             return xmlXPathNAN;
1301         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1302             exsltDateFreeDate(dt);
1303             return xmlXPathNAN;
1304         }
1305     }
1306
1307     fdiy = DAY_IN_YEAR(1, 1, dt->year);
1308     
1309     /*
1310      * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
1311      * is the first day-in-week
1312      */
1313     fdiw = (_exsltDateDayInWeek(fdiy, dt->year) + 6) % 7;
1314
1315     ret = DAY_IN_YEAR(dt->day, dt->mon, dt->year) / 7;
1316
1317     /* ISO 8601 adjustment, 3 is Thu */
1318     if (fdiw <= 3)
1319         ret += 1;
1320
1321     exsltDateFreeDate(dt);
1322
1323     return (double) ret;
1324 }
1325
1326 /**
1327  * exsltDateWeekInMonth:
1328  * @dateTime: a date/time string
1329  *
1330  * Implements the EXSLT - Dates and Times week-in-month() function
1331  *    number date:week-in-month (string?)
1332  * The date:week-in-month function returns the week in a month of a
1333  * date as a number. If no argument is given, then the current local
1334  * date/time, as returned by date:date-time is used the default
1335  * argument. For the purposes of numbering, the first day of the month
1336  * is in week 1 and new weeks begin on a Monday (so the first and last
1337  * weeks in a month will often have less than 7 days in them).
1338  * The date/time string specified as the argument is a right-truncated
1339  * string in the format defined as the lexical representation of
1340  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
1341  * Datatypes].  The permitted formats are as follows:
1342  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1343  *  - xs:date (CCYY-MM-DD)
1344  * If the date/time string is not in one of these formats, then NaN is
1345  * returned.
1346  */
1347 static double
1348 exsltDateWeekInMonth (const xmlChar *dateTime) {
1349     exsltDatePtr dt;
1350     int fdiy, fdiw, ret;
1351
1352     if (dateTime == NULL) {
1353 #ifdef WITH_TIME
1354         dt = exsltDateCurrent();
1355         if (dt == NULL)
1356 #endif
1357             return xmlXPathNAN;
1358     } else {
1359         dt = exsltDateParse(dateTime);
1360         if (dt == NULL)
1361             return xmlXPathNAN;
1362         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1363             exsltDateFreeDate(dt);
1364             return xmlXPathNAN;
1365         }
1366     }
1367
1368     fdiy = DAY_IN_YEAR(1, dt->mon, dt->year);
1369     /*
1370      * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
1371      * is the first day-in-week
1372      */
1373     fdiw = (_exsltDateDayInWeek(fdiy, dt->year) + 6) % 7;
1374
1375     ret = ((dt->day + fdiw) / 7) + 1;
1376
1377     exsltDateFreeDate(dt);
1378
1379     return (double) ret;
1380 }
1381
1382 /**
1383  * exsltDateDayInYear:
1384  * @dateTime: a date/time string
1385  *
1386  * Implements the EXSLT - Dates and Times day-in-year() function
1387  *    number date:day-in-year (string?)
1388  * Returns the day of a date in a year as a number.  If no argument is
1389  * given, then the current local date/time, as returned by
1390  * date:date-time is used the default argument.
1391  * The date/time string specified as the argument is a right-truncated
1392  * string in the format defined as the lexical representation of
1393  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
1394  * Datatypes].  The permitted formats are as follows:
1395  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1396  *  - xs:date (CCYY-MM-DD)
1397  * If the date/time string is not in one of these formats, then NaN is
1398  * returned.
1399  */
1400 static double
1401 exsltDateDayInYear (const xmlChar *dateTime) {
1402     exsltDatePtr dt;
1403     int ret;
1404
1405     if (dateTime == NULL) {
1406 #ifdef WITH_TIME
1407         dt = exsltDateCurrent();
1408         if (dt == NULL)
1409 #endif
1410             return xmlXPathNAN;
1411     } else {
1412         dt = exsltDateParse(dateTime);
1413         if (dt == NULL)
1414             return xmlXPathNAN;
1415         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1416             exsltDateFreeDate(dt);
1417             return xmlXPathNAN;
1418         }
1419     }
1420
1421     ret = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
1422
1423     exsltDateFreeDate(dt);
1424
1425     return (double) ret;
1426 }
1427
1428 /**
1429  * exsltDateDayInMonth:
1430  * @dateTime: a date/time string
1431  *
1432  * Implements the EXSLT - Dates and Times day-in-month() function:
1433  *    number date:day-in-month (string?)
1434  * Returns the day of a date as a number.  If no argument is given,
1435  * then the current local date/time, as returned by date:date-time is
1436  * used the default argument.
1437  * The date/time string specified as the argument is a left or
1438  * right-truncated string in the format defined as the lexical
1439  * representation of xs:dateTime in one of the formats defined in [XML
1440  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1441  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1442  *  - xs:date (CCYY-MM-DD)
1443  *  - xs:gMonthDay (--MM-DD)
1444  *  - xs:gDay (---DD)
1445  * If the date/time string is not in one of these formats, then NaN is
1446  * returned.
1447  */
1448 static double
1449 exsltDateDayInMonth (const xmlChar *dateTime) {
1450     exsltDatePtr dt;
1451     double ret;
1452
1453     if (dateTime == NULL) {
1454 #ifdef WITH_TIME
1455         dt = exsltDateCurrent();
1456         if (dt == NULL)
1457 #endif
1458             return xmlXPathNAN;
1459     } else {
1460         dt = exsltDateParse(dateTime);
1461         if (dt == NULL)
1462             return xmlXPathNAN;
1463         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1464             (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
1465             exsltDateFreeDate(dt);
1466             return xmlXPathNAN;
1467         }
1468     }
1469
1470     ret = (double) dt->day;
1471     exsltDateFreeDate(dt);
1472
1473     return ret;
1474 }
1475
1476 /**
1477  * exsltDateDayOfWeekInMonth:
1478  * @dateTime: a date/time string
1479  *
1480  * Implements the EXSLT - Dates and Times day-of-week-in-month()
1481  * function
1482  *    number date:day-of-week-in-month (string?)
1483  * Returns the day-of-the-week in a month of a date as a number
1484  * (e.g. 3 for the 3rd Tuesday in May).  If no argument is
1485  * given, then the current local date/time, as returned by
1486  * date:date-time is used the default argument.
1487  * The date/time string specified as the argument is a right-truncated
1488  * string in the format defined as the lexical representation of
1489  * xs:dateTime in one of the formats defined in [XML Schema Part 2:
1490  * Datatypes].  The permitted formats are as follows:
1491  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1492  *  - xs:date (CCYY-MM-DD)
1493  * If the date/time string is not in one of these formats, then NaN is
1494  * returned.
1495  */
1496 static double
1497 exsltDateDayOfWeekInMonth (const xmlChar *dateTime) {
1498     exsltDatePtr dt;
1499     int ret;
1500
1501     if (dateTime == NULL) {
1502 #ifdef WITH_TIME
1503         dt = exsltDateCurrent();
1504         if (dt == NULL)
1505 #endif
1506             return xmlXPathNAN;
1507     } else {
1508         dt = exsltDateParse(dateTime);
1509         if (dt == NULL)
1510             return xmlXPathNAN;
1511         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1512             exsltDateFreeDate(dt);
1513             return xmlXPathNAN;
1514         }
1515     }
1516
1517     ret = (dt->day / 7) + 1;
1518
1519     exsltDateFreeDate(dt);
1520
1521     return (double) ret;
1522 }
1523
1524 /**
1525  * exsltDateDayInWeek:
1526  * @dateTime: a date/time string
1527  *
1528  * Implements the EXSLT - Dates and Times day-in-week() function:
1529  *    number date:day-in-week (string?)
1530  * Returns the day of the week given in a date as a number.  If no
1531  * argument is given, then the current local date/time, as returned by
1532  * date:date-time is used the default argument.
1533  * The date/time string specified as the argument is a left or
1534  * right-truncated string in the format defined as the lexical
1535  * representation of xs:dateTime in one of the formats defined in [XML
1536  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1537  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1538  *  - xs:date (CCYY-MM-DD)
1539  * If the date/time string is not in one of these formats, then NaN is
1540  * returned.
1541  * The numbering of days of the week starts at 1 for Sunday, 2 for
1542  * Monday and so on up to 7 for Saturday.
1543  */
1544 static double
1545 exsltDateDayInWeek (const xmlChar *dateTime) {
1546     exsltDatePtr dt;
1547     int diy;
1548     double ret;
1549
1550     if (dateTime == NULL) {
1551 #ifdef WITH_TIME
1552         dt = exsltDateCurrent();
1553         if (dt == NULL)
1554 #endif
1555             return xmlXPathNAN;
1556     } else {
1557         dt = exsltDateParse(dateTime);
1558         if (dt == NULL)
1559             return xmlXPathNAN;
1560         if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1561             exsltDateFreeDate(dt);
1562             return xmlXPathNAN;
1563         }
1564     }
1565
1566     diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
1567
1568     ret = (double) _exsltDateDayInWeek(diy, dt->year) + 1;
1569
1570     exsltDateFreeDate(dt);
1571
1572     return ret;
1573 }
1574
1575 /**
1576  * exsltDateDayName:
1577  * @dateTime: a date/time string
1578  *
1579  * Implements the EXSLT - Dates and Time day-name() function
1580  *    string date:day-name (string?)
1581  * Returns the full name of the day of the week of a date.  If no
1582  * argument is given, then the current local date/time, as returned by
1583  * date:date-time is used the default argument.
1584  * The date/time string specified as the argument is a left or
1585  * right-truncated string in the format defined as the lexical
1586  * representation of xs:dateTime in one of the formats defined in [XML
1587  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1588  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1589  *  - xs:date (CCYY-MM-DD)
1590  * If the date/time string is not in one of these formats, then an
1591  * empty string ('') is returned.
1592  * The result is an English day name: one of 'Sunday', 'Monday',
1593  * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
1594  */
1595 static const xmlChar *
1596 exsltDateDayName (const xmlChar *dateTime) {
1597     static const xmlChar dayNames[8][10] = {
1598         { 0 },
1599         { 'S', 'u', 'n', 'd', 'a', 'y', 0 },
1600         { 'M', 'o', 'n', 'd', 'a', 'y', 0 },
1601         { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
1602         { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
1603         { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
1604         { 'F', 'r', 'i', 'd', 'a', 'y', 0 },
1605         { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
1606     };
1607     int day;
1608     day = exsltDateDayInWeek(dateTime);
1609     if((day < 1) || (day > 7))
1610       day = 0;
1611     return dayNames[day];
1612 }
1613
1614 /**
1615  * exsltDateDayAbbreviation:
1616  * @dateTime: a date/time string
1617  *
1618  * Implements the EXSLT - Dates and Time day-abbreviation() function
1619  *    string date:day-abbreviation (string?)
1620  * Returns the abbreviation of the day of the week of a date.  If no
1621  * argument is given, then the current local date/time, as returned by
1622  * date:date-time is used the default argument.
1623  * The date/time string specified as the argument is a left or
1624  * right-truncated string in the format defined as the lexical
1625  * representation of xs:dateTime in one of the formats defined in [XML
1626  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1627  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1628  *  - xs:date (CCYY-MM-DD)
1629  * If the date/time string is not in one of these formats, then an
1630  * empty string ('') is returned.
1631  * The result is a three-letter English day abbreviation: one of
1632  * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
1633  */
1634 static const xmlChar *
1635 exsltDateDayAbbreviation (const xmlChar *dateTime) {
1636     static const xmlChar dayAbbreviations[8][4] = {
1637         { 0 },
1638         { 'S', 'u', 'n', 0 },
1639         { 'M', 'o', 'n', 0 },
1640         { 'T', 'u', 'e', 0 },
1641         { 'W', 'e', 'd', 0 },
1642         { 'T', 'h', 'u', 0 },
1643         { 'F', 'r', 'i', 0 },
1644         { 'S', 'a', 't', 0 }
1645     };
1646     int day;
1647     day = exsltDateDayInWeek(dateTime);
1648     if((day < 1) || (day > 7))
1649       day = 0;
1650     return dayAbbreviations[day];
1651 }
1652
1653 /**
1654  * exsltDateHourInDay:
1655  * @dateTime: a date/time string
1656  *
1657  * Implements the EXSLT - Dates and Times day-in-month() function:
1658  *    number date:day-in-month (string?)
1659  * Returns the hour of the day as a number.  If no argument is given,
1660  * then the current local date/time, as returned by date:date-time is
1661  * used the default argument.
1662  * The date/time string specified as the argument is a left or
1663  * right-truncated string in the format defined as the lexical
1664  * representation of xs:dateTime in one of the formats defined in [XML
1665  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1666  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1667  *  - xs:time (hh:mm:ss)
1668  * If the date/time string is not in one of these formats, then NaN is
1669  * returned.
1670  */
1671 static double
1672 exsltDateHourInDay (const xmlChar *dateTime) {
1673     exsltDatePtr dt;
1674     double ret;
1675
1676     if (dateTime == NULL) {
1677 #ifdef WITH_TIME
1678         dt = exsltDateCurrent();
1679         if (dt == NULL)
1680 #endif
1681             return xmlXPathNAN;
1682     } else {
1683         dt = exsltDateParse(dateTime);
1684         if (dt == NULL)
1685             return xmlXPathNAN;
1686         if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1687             exsltDateFreeDate(dt);
1688             return xmlXPathNAN;
1689         }
1690     }
1691
1692     ret = (double) dt->hour;
1693     exsltDateFreeDate(dt);
1694
1695     return ret;
1696 }
1697
1698 /**
1699  * exsltDateMinuteInHour:
1700  * @dateTime: a date/time string
1701  *
1702  * Implements the EXSLT - Dates and Times day-in-month() function:
1703  *    number date:day-in-month (string?)
1704  * Returns the minute of the hour as a number.  If no argument is
1705  * given, then the current local date/time, as returned by
1706  * date:date-time is used the default argument.
1707  * The date/time string specified as the argument is a left or
1708  * right-truncated string in the format defined as the lexical
1709  * representation of xs:dateTime in one of the formats defined in [XML
1710  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1711  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1712  *  - xs:time (hh:mm:ss)
1713  * If the date/time string is not in one of these formats, then NaN is
1714  * returned.
1715  */
1716 static double
1717 exsltDateMinuteInHour (const xmlChar *dateTime) {
1718     exsltDatePtr dt;
1719     double ret;
1720
1721     if (dateTime == NULL) {
1722 #ifdef WITH_TIME
1723         dt = exsltDateCurrent();
1724         if (dt == NULL)
1725 #endif
1726             return xmlXPathNAN;
1727     } else {
1728         dt = exsltDateParse(dateTime);
1729         if (dt == NULL)
1730             return xmlXPathNAN;
1731         if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1732             exsltDateFreeDate(dt);
1733             return xmlXPathNAN;
1734         }
1735     }
1736
1737     ret = (double) dt->min;
1738     exsltDateFreeDate(dt);
1739
1740     return ret;
1741 }
1742
1743 /**
1744  * exsltDateSecondInMinute:
1745  * @dateTime: a date/time string
1746  *
1747  * Implements the EXSLT - Dates and Times second-in-minute() function:
1748  *    number date:day-in-month (string?)
1749  * Returns the second of the minute as a number.  If no argument is
1750  * given, then the current local date/time, as returned by
1751  * date:date-time is used the default argument.
1752  * The date/time string specified as the argument is a left or
1753  * right-truncated string in the format defined as the lexical
1754  * representation of xs:dateTime in one of the formats defined in [XML
1755  * Schema Part 2: Datatypes].  The permitted formats are as follows:
1756  *  - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1757  *  - xs:time (hh:mm:ss)
1758  * If the date/time string is not in one of these formats, then NaN is
1759  * returned.
1760  */
1761 static double
1762 exsltDateSecondInMinute (const xmlChar *dateTime) {
1763     exsltDatePtr dt;
1764     double ret;
1765
1766     if (dateTime == NULL) {
1767 #ifdef WITH_TIME
1768         dt = exsltDateCurrent();
1769         if (dt == NULL)
1770 #endif
1771             return xmlXPathNAN;
1772     } else {
1773         dt = exsltDateParse(dateTime);
1774         if (dt == NULL)
1775             return xmlXPathNAN;
1776         if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1777             exsltDateFreeDate(dt);
1778             return xmlXPathNAN;
1779         }
1780     }
1781
1782     ret = dt->sec;
1783     exsltDateFreeDate(dt);
1784
1785     return ret;
1786 }
1787
1788
1789 /****************************************************************
1790  *                                                              *
1791  *              Wrappers for use by the XPath engine            *
1792  *                                                              *
1793  ****************************************************************/
1794
1795 #ifdef WITH_TIME
1796 /**
1797  * exsltDateDateTimeFunction:
1798  * @ctxt: an XPath parser context
1799  * @nargs : the number of arguments
1800  *
1801  * Wraps #exsltDateDateTime for use by the XPath engine
1802  */
1803 static void
1804 exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs) {
1805     xmlChar *ret;
1806
1807     if (nargs != 0) {
1808         xmlXPathSetArityError(ctxt);
1809         return;
1810     }
1811
1812     ret = exsltDateDateTime();
1813     xmlXPathReturnString(ctxt, ret);
1814 }
1815 #endif
1816
1817 /**
1818  * exsltDateDateFunction:
1819  * @ctxt: an XPath parser context
1820  * @nargs : the number of arguments
1821  *
1822  * Wraps #exsltDateDate for use by the XPath engine
1823  */
1824 static void
1825 exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs) {
1826     xmlChar *ret, *dt = NULL;
1827
1828     if ((nargs < 0) || (nargs > 1)) {
1829         xmlXPathSetArityError(ctxt);
1830         return;
1831     }
1832     if (nargs == 1) {
1833         dt = xmlXPathPopString(ctxt);
1834         if (xmlXPathCheckError(ctxt)) {
1835             xmlXPathSetTypeError(ctxt);
1836             return;
1837         }
1838     }
1839
1840     ret = exsltDateDate(dt);
1841
1842     if (ret == NULL) {
1843         xsltGenericDebug(xsltGenericDebugContext,
1844                          "{http://exslt.org/dates-and-times}date: "
1845                          "invalid date or format %s\n", dt);
1846         xmlXPathReturnEmptyString(ctxt);
1847     } else {
1848         xmlXPathReturnString(ctxt, ret);
1849     }
1850
1851     if (dt != NULL)
1852         xmlFree(dt);
1853 }
1854
1855 /**
1856  * exsltDateTimeFunction:
1857  * @ctxt: an XPath parser context
1858  * @nargs : the number of arguments
1859  *
1860  * Wraps #exsltDateTime for use by the XPath engine
1861  */
1862 static void
1863 exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs) {
1864     xmlChar *ret, *dt = NULL;
1865
1866     if ((nargs < 0) || (nargs > 1)) {
1867         xmlXPathSetArityError(ctxt);
1868         return;
1869     }
1870     if (nargs == 1) {
1871         dt = xmlXPathPopString(ctxt);
1872         if (xmlXPathCheckError(ctxt)) {
1873             xmlXPathSetTypeError(ctxt);
1874             return;
1875         }
1876     }
1877
1878     ret = exsltDateTime(dt);
1879
1880     if (ret == NULL) {
1881         xsltGenericDebug(xsltGenericDebugContext,
1882                          "{http://exslt.org/dates-and-times}time: "
1883                          "invalid date or format %s\n", dt);
1884         xmlXPathReturnEmptyString(ctxt);
1885     } else {
1886         xmlXPathReturnString(ctxt, ret);
1887     }
1888
1889     if (dt != NULL)
1890         xmlFree(dt);
1891 }
1892
1893 /**
1894  * exsltDateYearFunction:
1895  * @ctxt: an XPath parser context
1896  * @nargs : the number of arguments
1897  *
1898  * Wraps #exsltDateYear for use by the XPath engine
1899  */
1900 static void
1901 exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs) {
1902     xmlChar *dt = NULL;
1903     double ret;
1904
1905     if ((nargs < 0) || (nargs > 1)) {
1906         xmlXPathSetArityError(ctxt);
1907         return;
1908     }
1909
1910     if (nargs == 1) {
1911         dt = xmlXPathPopString(ctxt);
1912         if (xmlXPathCheckError(ctxt)) {
1913             xmlXPathSetTypeError(ctxt);
1914             return;
1915         }
1916     }
1917
1918     ret = exsltDateYear(dt);
1919
1920     if (dt != NULL)
1921         xmlFree(dt);
1922
1923     xmlXPathReturnNumber(ctxt, ret);
1924 }
1925
1926 /**
1927  * exsltDateLeapYearFunction:
1928  * @ctxt: an XPath parser context
1929  * @nargs : the number of arguments
1930  *
1931  * Wraps #exsltDateLeapYear for use by the XPath engine
1932  */
1933 static void
1934 exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt,
1935                            int nargs) {
1936     xmlChar *dt = NULL;
1937     xmlXPathObjectPtr ret;
1938
1939     if ((nargs < 0) || (nargs > 1)) {
1940         xmlXPathSetArityError(ctxt);
1941         return;
1942     }
1943
1944     if (nargs == 1) {
1945         dt = xmlXPathPopString(ctxt);
1946         if (xmlXPathCheckError(ctxt)) {
1947             xmlXPathSetTypeError(ctxt);
1948             return;
1949         }
1950     }
1951
1952     ret = exsltDateLeapYear(dt);
1953
1954     if (dt != NULL)
1955         xmlFree(dt);
1956
1957     valuePush(ctxt, ret);
1958 }
1959
1960 #define X_IN_Y(x, y)                                            \
1961 static void                                                     \
1962 exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt,   \
1963                               int nargs) {                      \
1964     xmlChar *dt = NULL;                                         \
1965     double ret;                                                 \
1966                                                                 \
1967     if ((nargs < 0) || (nargs > 1)) {                           \
1968         xmlXPathSetArityError(ctxt);                            \
1969         return;                                                 \
1970     }                                                           \
1971                                                                 \
1972     if (nargs == 1) {                                           \
1973         dt = xmlXPathPopString(ctxt);                           \
1974         if (xmlXPathCheckError(ctxt)) {                         \
1975             xmlXPathSetTypeError(ctxt);                         \
1976             return;                                             \
1977         }                                                       \
1978     }                                                           \
1979                                                                 \
1980     ret = exsltDate##x##In##y(dt);                              \
1981                                                                 \
1982     if (dt != NULL)                                             \
1983         xmlFree(dt);                                            \
1984                                                                 \
1985     xmlXPathReturnNumber(ctxt, ret);                            \
1986 }
1987
1988 /**
1989  * exsltDateMonthInYearFunction:
1990  * @ctxt: an XPath parser context
1991  * @nargs : the number of arguments
1992  *
1993  * Wraps #exsltDateMonthInYear for use by the XPath engine
1994  */
1995 X_IN_Y(Month,Year)
1996
1997 /**
1998  * exsltDateMonthNameFunction:
1999  * @ctxt: an XPath parser context
2000  * @nargs : the number of arguments
2001  *
2002  * Wraps #exsltDateMonthName for use by the XPath engine
2003  */
2004 static void
2005 exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt,
2006                             int nargs) {
2007     xmlChar *dt = NULL;
2008     const xmlChar *ret;
2009
2010     if ((nargs < 0) || (nargs > 1)) {
2011         xmlXPathSetArityError(ctxt);
2012         return;
2013     }
2014
2015     if (nargs == 1) {
2016         dt = xmlXPathPopString(ctxt);
2017         if (xmlXPathCheckError(ctxt)) {
2018             xmlXPathSetTypeError(ctxt);
2019             return;
2020         }
2021     }
2022
2023     ret = exsltDateMonthName(dt);
2024
2025     if (dt != NULL)
2026         xmlFree(dt);
2027
2028     if (ret == NULL)
2029         xmlXPathReturnEmptyString(ctxt);
2030     else
2031         xmlXPathReturnString(ctxt, xmlStrdup(ret));
2032 }
2033
2034 /**
2035  * exsltDateMonthAbbreviationFunction:
2036  * @ctxt: an XPath parser context
2037  * @nargs : the number of arguments
2038  *
2039  * Wraps #exsltDateMonthAbbreviation for use by the XPath engine
2040  */
2041 static void
2042 exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt,
2043                             int nargs) {
2044     xmlChar *dt = NULL;
2045     const xmlChar *ret;
2046
2047     if ((nargs < 0) || (nargs > 1)) {
2048         xmlXPathSetArityError(ctxt);
2049         return;
2050     }
2051
2052     if (nargs == 1) {
2053         dt = xmlXPathPopString(ctxt);
2054         if (xmlXPathCheckError(ctxt)) {
2055             xmlXPathSetTypeError(ctxt);
2056             return;
2057         }
2058     }
2059
2060     ret = exsltDateMonthAbbreviation(dt);
2061
2062     if (dt != NULL)
2063         xmlFree(dt);
2064
2065     if (ret == NULL)
2066         xmlXPathReturnEmptyString(ctxt);
2067     else
2068         xmlXPathReturnString(ctxt, xmlStrdup(ret));
2069 }
2070
2071 /**
2072  * exsltDateWeekInYearFunction:
2073  * @ctxt: an XPath parser context
2074  * @nargs : the number of arguments
2075  *
2076  * Wraps #exsltDateWeekInYear for use by the XPath engine
2077  */
2078 X_IN_Y(Week,Year)
2079
2080 /**
2081  * exsltDateWeekInMonthFunction:
2082  * @ctxt: an XPath parser context
2083  * @nargs : the number of arguments
2084  *
2085  * Wraps #exsltDateWeekInMonthYear for use by the XPath engine
2086  */
2087 X_IN_Y(Week,Month)
2088
2089 /**
2090  * exsltDateDayInYearFunction:
2091  * @ctxt: an XPath parser context
2092  * @nargs : the number of arguments
2093  *
2094  * Wraps #exsltDateDayInYear for use by the XPath engine
2095  */
2096 X_IN_Y(Day,Year)
2097
2098 /**
2099  * exsltDateDayInMonthFunction:
2100  * @ctxt: an XPath parser context
2101  * @nargs : the number of arguments
2102  *
2103  * Wraps #exsltDateDayInMonth for use by the XPath engine
2104  */
2105 X_IN_Y(Day,Month)
2106
2107 /**
2108  * exsltDateDayOfWeekInMonthFunction:
2109  * @ctxt: an XPath parser context
2110  * @nargs : the number of arguments
2111  *
2112  * Wraps #exsltDayOfWeekInMonth for use by the XPath engine
2113  */
2114 X_IN_Y(DayOfWeek,Month)
2115
2116 /**
2117  * exsltDateDayInWeekFunction:
2118  * @ctxt: an XPath parser context
2119  * @nargs : the number of arguments
2120  *
2121  * Wraps #exsltDateDayInWeek for use by the XPath engine
2122  */
2123 X_IN_Y(Day,Week)
2124
2125 /**
2126  * exsltDateDayNameFunction:
2127  * @ctxt: an XPath parser context
2128  * @nargs : the number of arguments
2129  *
2130  * Wraps #exsltDateDayName for use by the XPath engine
2131  */
2132 static void
2133 exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt,
2134                             int nargs) {
2135     xmlChar *dt = NULL;
2136     const xmlChar *ret;
2137
2138     if ((nargs < 0) || (nargs > 1)) {
2139         xmlXPathSetArityError(ctxt);
2140         return;
2141     }
2142
2143     if (nargs == 1) {
2144         dt = xmlXPathPopString(ctxt);
2145         if (xmlXPathCheckError(ctxt)) {
2146             xmlXPathSetTypeError(ctxt);
2147             return;
2148         }
2149     }
2150
2151     ret = exsltDateDayName(dt);
2152
2153     if (dt != NULL)
2154         xmlFree(dt);
2155
2156     if (ret == NULL)
2157         xmlXPathReturnEmptyString(ctxt);
2158     else
2159         xmlXPathReturnString(ctxt, xmlStrdup(ret));
2160 }
2161
2162 /**
2163  * exsltDateMonthDayFunction:
2164  * @ctxt: an XPath parser context
2165  * @nargs : the number of arguments
2166  *
2167  * Wraps #exsltDateDayAbbreviation for use by the XPath engine
2168  */
2169 static void
2170 exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt,
2171                             int nargs) {
2172     xmlChar *dt = NULL;
2173     const xmlChar *ret;
2174
2175     if ((nargs < 0) || (nargs > 1)) {
2176         xmlXPathSetArityError(ctxt);
2177         return;
2178     }
2179
2180     if (nargs == 1) {
2181         dt = xmlXPathPopString(ctxt);
2182         if (xmlXPathCheckError(ctxt)) {
2183             xmlXPathSetTypeError(ctxt);
2184             return;
2185         }
2186     }
2187
2188     ret = exsltDateDayAbbreviation(dt);
2189
2190     if (dt != NULL)
2191         xmlFree(dt);
2192
2193     if (ret == NULL)
2194         xmlXPathReturnEmptyString(ctxt);
2195     else
2196         xmlXPathReturnString(ctxt, xmlStrdup(ret));
2197 }
2198
2199
2200 /**
2201  * exsltDateHourInDayFunction:
2202  * @ctxt: an XPath parser context
2203  * @nargs : the number of arguments
2204  *
2205  * Wraps #exsltDateHourInDay for use by the XPath engine
2206  */
2207 X_IN_Y(Hour,Day)
2208
2209 /**
2210  * exsltDateMinuteInHourFunction:
2211  * @ctxt: an XPath parser context
2212  * @nargs : the number of arguments
2213  *
2214  * Wraps #exsltDateMinuteInHour for use by the XPath engine
2215  */
2216 X_IN_Y(Minute,Hour)
2217
2218 /**
2219  * exsltDateSecondInMinuteFunction:
2220  * @ctxt: an XPath parser context
2221  * @nargs : the number of arguments
2222  *
2223  * Wraps #exsltDateSecondInMinute for use by the XPath engine
2224  */
2225 X_IN_Y(Second,Minute)
2226
2227 /**
2228  * exsltDateRegister:
2229  *
2230  * Registers the EXSLT - Dates and Times module
2231  */
2232 void
2233 exsltDateRegister(void)
2234 {
2235 #ifdef WITH_TIME
2236     xsltRegisterExtModuleFunction((const xmlChar *) "date-time",
2237                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2238                           exsltDateDateTimeFunction);
2239 #endif
2240     xsltRegisterExtModuleFunction((const xmlChar *) "date",
2241                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2242                           exsltDateDateFunction);
2243     xsltRegisterExtModuleFunction((const xmlChar *) "time",
2244                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2245                           exsltDateTimeFunction);
2246     xsltRegisterExtModuleFunction((const xmlChar *) "year",
2247                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2248                           exsltDateYearFunction);
2249     xsltRegisterExtModuleFunction((const xmlChar *) "leap-year",
2250                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2251                           exsltDateLeapYearFunction);
2252     xsltRegisterExtModuleFunction((const xmlChar *) "month-in-year",
2253                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2254                           exsltDateMonthInYearFunction);
2255     xsltRegisterExtModuleFunction((const xmlChar *) "month-name",
2256                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2257                           exsltDateMonthNameFunction);
2258     xsltRegisterExtModuleFunction((const xmlChar *) "month-abbreviation",
2259                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2260                           exsltDateMonthAbbreviationFunction);
2261     xsltRegisterExtModuleFunction((const xmlChar *) "week-in-year",
2262                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2263                           exsltDateWeekInYearFunction);
2264     xsltRegisterExtModuleFunction((const xmlChar *) "week-in-month",
2265                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2266                           exsltDateWeekInMonthFunction);
2267     xsltRegisterExtModuleFunction((const xmlChar *) "day-in-year",
2268                           (const xmlChar *) EXSLT_DATE_NAMESPACE,
2269                           exsltDateDayInYearFunction);
2270     xsltRegisterExtModuleFunction((const xmlChar *) "day-in-month",
2271                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2272                             exsltDateDayInMonthFunction);
2273     xsltRegisterExtModuleFunction((const xmlChar *) "day-of-week-in-month",
2274                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2275                             exsltDateDayOfWeekInMonthFunction);
2276     xsltRegisterExtModuleFunction((const xmlChar *) "day-in-week",
2277                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2278                             exsltDateDayInWeekFunction);
2279     xsltRegisterExtModuleFunction((const xmlChar *) "day-name",
2280                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2281                             exsltDateDayNameFunction);
2282     xsltRegisterExtModuleFunction((const xmlChar *) "day-abbreviation",
2283                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2284                             exsltDateDayAbbreviationFunction);
2285     xsltRegisterExtModuleFunction((const xmlChar *) "hour-in-day",
2286                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2287                             exsltDateHourInDayFunction);
2288     xsltRegisterExtModuleFunction((const xmlChar *) "minute-in-hour",
2289                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2290                             exsltDateMinuteInHourFunction);
2291     xsltRegisterExtModuleFunction((const xmlChar *) "second-in-minute",
2292                             (const xmlChar *) EXSLT_DATE_NAMESPACE,
2293                             exsltDateSecondInMinuteFunction);
2294 }