Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Side by Side Diff: third_party/libxslt/libexslt/date.c

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

Powered by Google App Engine
This is Rietveld 408576698