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