VirtualBox

source: vbox/trunk/src/libs/libxslt-1.1.22/libexslt/date.c@ 22621

Last change on this file since 22621 was 7299, checked in by vboxsync, 17 years ago

Added vboxconfig.h for linux builds.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette