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