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