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