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