2 * date.c: Implementation of the EXSLT -- Dates and Times module
5 * http://www.exslt.org/date/date.html
7 * See Copyright for the status of this software.
10 * Charlie Bozeman <cbozeman@HiWAAY.net>
11 * Thomas Broyer <tbroyer@ltgt.net>
23 #include "libexslt/libexslt.h"
25 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
26 #include <win32config.h>
31 #if HAVE_LOCALTIME_R /* _POSIX_SOURCE required by gnu libc */
35 #include <libxml/tree.h>
36 #include <libxml/xpath.h>
37 #include <libxml/xpathInternals.h>
39 #include <libxslt/xsltconfig.h>
40 #include <libxslt/xsltutils.h>
41 #include <libxslt/xsltInternals.h>
42 #include <libxslt/extensions.h>
52 /* needed to get localtime_r on Solaris */
54 #ifndef __EXTENSIONS__
55 #define __EXTENSIONS__
64 * types of date and/or time (from schema datatypes)
65 * somewhat ordered from least specific to most specific (i.e.
66 * most truncated to least truncated).
70 XS_TIME = 1, /* time is left-truncated */
71 XS_GDAY = (XS_TIME << 1),
72 XS_GMONTH = (XS_GDAY << 1),
73 XS_GMONTHDAY = (XS_GMONTH | XS_GDAY),
74 XS_GYEAR = (XS_GMONTH << 1),
75 XS_GYEARMONTH = (XS_GYEAR | XS_GMONTH),
76 XS_DATE = (XS_GYEAR | XS_GMONTH | XS_GDAY),
77 XS_DATETIME = (XS_DATE | XS_TIME),
78 XS_DURATION = (XS_GYEAR << 1)
82 typedef struct _exsltDateValDate exsltDateValDate;
83 typedef exsltDateValDate *exsltDateValDatePtr;
84 struct _exsltDateValDate {
86 unsigned int mon :4; /* 1 <= mon <= 12 */
87 unsigned int day :5; /* 1 <= day <= 31 */
88 unsigned int hour :5; /* 0 <= hour <= 23 */
89 unsigned int min :6; /* 0 <= min <= 59 */
91 unsigned int tz_flag :1; /* is tzo explicitely set? */
92 int tzo :11; /* -1440 <= tzo <= 1440 */
96 typedef struct _exsltDateValDuration exsltDateValDuration;
97 typedef exsltDateValDuration *exsltDateValDurationPtr;
98 struct _exsltDateValDuration {
99 long mon; /* mon stores years also */
101 double sec; /* sec stores min and hour also */
104 typedef struct _exsltDateVal exsltDateVal;
105 typedef exsltDateVal *exsltDateValPtr;
106 struct _exsltDateVal {
109 exsltDateValDate date;
110 exsltDateValDuration dur;
114 /****************************************************************
116 * Compat./Port. macros *
118 ****************************************************************/
120 #if defined(HAVE_TIME_H) \
121 && (defined(HAVE_LOCALTIME) || defined(HAVE_LOCALTIME_R)) \
122 && defined(HAVE_TIME) && defined(HAVE_GMTIME)
126 /****************************************************************
128 * Convenience macros and functions *
130 ****************************************************************/
132 #define IS_TZO_CHAR(c) \
133 ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
135 #define VALID_ALWAYS(num) (num >= 0)
136 #define VALID_YEAR(yr) (yr != 0)
137 #define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
138 /* VALID_DAY should only be used when month is unknown */
139 #define VALID_DAY(day) ((day >= 1) && (day <= 31))
140 #define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
141 #define VALID_MIN(min) ((min >= 0) && (min <= 59))
142 #define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
143 #define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
145 (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
147 static const unsigned long daysInMonth[12] =
148 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
149 static const unsigned long daysInMonthLeap[12] =
150 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
152 #define MAX_DAYINMONTH(yr,mon) \
153 (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
155 #define VALID_MDAY(dt) \
156 (IS_LEAP(dt->year) ? \
157 (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
158 (dt->day <= daysInMonth[dt->mon - 1]))
160 #define VALID_DATE(dt) \
161 (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
164 hour and min structure vals are unsigned, so normal macros give
165 warnings on some compilers.
167 #define VALID_TIME(dt) \
168 ((dt->hour <=23 ) && (dt->min <= 59) && \
169 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
171 #define VALID_DATETIME(dt) \
172 (VALID_DATE(dt) && VALID_TIME(dt))
174 #define SECS_PER_MIN (60)
175 #define SECS_PER_HOUR (60 * SECS_PER_MIN)
176 #define SECS_PER_DAY (24 * SECS_PER_HOUR)
178 static const unsigned long dayInYearByMonth[12] =
179 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
180 static const unsigned long dayInLeapYearByMonth[12] =
181 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
183 #define DAY_IN_YEAR(day, month, year) \
185 dayInLeapYearByMonth[month - 1] : \
186 dayInYearByMonth[month - 1]) + day)
189 * _exsltDateParseGYear:
190 * @dt: pointer to a date structure
191 * @str: pointer to the string to analyze
193 * Parses a xs:gYear without time zone and fills in the appropriate
194 * field of the @dt structure. @str is updated to point just after the
195 * xs:gYear. It is supposed that @dt->year is big enough to contain
198 * Returns 0 or the error code
201 _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
203 const xmlChar *cur = *str, *firstChar;
204 int isneg = 0, digcnt = 0;
206 if (((*cur < '0') || (*cur > '9')) &&
207 (*cur != '-') && (*cur != '+'))
217 while ((*cur >= '0') && (*cur <= '9')) {
218 dt->year = dt->year * 10 + (*cur - '0');
223 /* year must be at least 4 digits (CCYY); over 4
224 * digits cannot have a leading zero. */
225 if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
229 dt->year = - dt->year;
231 if (!VALID_YEAR(dt->year))
236 #ifdef DEBUG_EXSLT_DATE
237 xsltGenericDebug(xsltGenericDebugContext,
238 "Parsed year %04i\n", dt->year);
246 * @yr: the year to format
247 * @cur: a pointer to an allocated buffer
249 * Formats @yr in xsl:gYear format. Result is appended to @cur and
250 * @cur is updated to point after the xsl:gYear.
252 #define FORMAT_GYEAR(yr, cur) \
258 long year = (yr < 0) ? - yr : yr; \
259 xmlChar tmp_buf[100], *tmp = tmp_buf; \
260 /* result is in reverse-order */ \
262 *tmp = '0' + (xmlChar)(year % 10); \
266 /* virtually adds leading zeros */ \
267 while ((tmp - tmp_buf) < 4) \
269 /* restore the correct order */ \
270 while (tmp > tmp_buf) { \
279 * @num: the integer to fill in
280 * @cur: an #xmlChar *
281 * @func: validation function for the number
282 * @invalid: an integer
284 * Parses a 2-digits integer and updates @num with the value. @cur is
285 * updated to point just after the integer.
286 * In case of error, @invalid is set to %TRUE, values of @num and
287 * @cur are undefined.
289 #define PARSE_2_DIGITS(num, cur, func, invalid) \
290 if ((cur[0] < '0') || (cur[0] > '9') || \
291 (cur[1] < '0') || (cur[1] > '9')) \
295 val = (cur[0] - '0') * 10 + (cur[1] - '0'); \
305 * @num: the integer to format
306 * @cur: a pointer to an allocated buffer
308 * Formats a 2-digits integer. Result is appended to @cur and
309 * @cur is updated to point after the integer.
311 #define FORMAT_2_DIGITS(num, cur) \
312 *cur = '0' + ((num / 10) % 10); \
314 *cur = '0' + (num % 10); \
319 * @num: the double to fill in
320 * @cur: an #xmlChar *
321 * @invalid: an integer
323 * Parses a float and updates @num with the value. @cur is
324 * updated to point just after the float. The float must have a
325 * 2-digits integer part and may or may not have a decimal part.
326 * In case of error, @invalid is set to %TRUE, values of @num and
327 * @cur are undefined.
329 #define PARSE_FLOAT(num, cur, invalid) \
330 PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid); \
331 if (!invalid && (*cur == '.')) { \
334 if ((*cur < '0') || (*cur > '9')) \
336 while ((*cur >= '0') && (*cur <= '9')) { \
338 num += (*cur - '0') * mult; \
345 * @num: the double to format
346 * @cur: a pointer to an allocated buffer
347 * @pad: a flag for padding to 2 integer digits
349 * Formats a float. Result is appended to @cur and @cur is updated to
350 * point after the integer. If the @pad flag is non-zero, then the
351 * float representation has a minimum 2-digits integer part. The
352 * fractional part is formatted if @num has a fractional value.
354 #define FORMAT_FLOAT(num, cur, pad) \
356 xmlChar *sav, *str; \
357 if ((pad) && (num < 10.0)) \
359 str = xmlXPathCastNumberToString(num); \
367 * _exsltDateParseGMonth:
368 * @dt: pointer to a date structure
369 * @str: pointer to the string to analyze
371 * Parses a xs:gMonth without time zone and fills in the appropriate
372 * field of the @dt structure. @str is updated to point just after the
375 * Returns 0 or the error code
378 _exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
380 const xmlChar *cur = *str;
383 PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret);
389 #ifdef DEBUG_EXSLT_DATE
390 xsltGenericDebug(xsltGenericDebugContext,
391 "Parsed month %02i\n", dt->mon);
399 * @mon: the month to format
400 * @cur: a pointer to an allocated buffer
402 * Formats @mon in xsl:gMonth format. Result is appended to @cur and
403 * @cur is updated to point after the xsl:gMonth.
405 #define FORMAT_GMONTH(mon, cur) \
406 FORMAT_2_DIGITS(mon, cur)
409 * _exsltDateParseGDay:
410 * @dt: pointer to a date structure
411 * @str: pointer to the string to analyze
413 * Parses a xs:gDay without time zone and fills in the appropriate
414 * field of the @dt structure. @str is updated to point just after the
417 * Returns 0 or the error code
420 _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
422 const xmlChar *cur = *str;
425 PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret);
431 #ifdef DEBUG_EXSLT_DATE
432 xsltGenericDebug(xsltGenericDebugContext,
433 "Parsed day %02i\n", dt->day);
441 * @dt: the #exsltDateValDate to format
442 * @cur: a pointer to an allocated buffer
444 * Formats @dt in xsl:gDay format. Result is appended to @cur and
445 * @cur is updated to point after the xsl:gDay.
447 #define FORMAT_GDAY(dt, cur) \
448 FORMAT_2_DIGITS(dt->day, cur)
452 * @dt: the #exsltDateValDate to format
453 * @cur: a pointer to an allocated buffer
455 * Formats @dt in xsl:date format. Result is appended to @cur and
456 * @cur is updated to point after the xsl:date.
458 #define FORMAT_DATE(dt, cur) \
459 FORMAT_GYEAR(dt->year, cur); \
462 FORMAT_GMONTH(dt->mon, cur); \
465 FORMAT_GDAY(dt, cur);
468 * _exsltDateParseTime:
469 * @dt: pointer to a date structure
470 * @str: pointer to the string to analyze
472 * Parses a xs:time without time zone and fills in the appropriate
473 * fields of the @dt structure. @str is updated to point just after the
475 * In case of error, values of @dt fields are undefined.
477 * Returns 0 or the error code
480 _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
482 const xmlChar *cur = *str;
483 unsigned int hour = 0; /* use temp var in case str is not xs:time */
486 PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret);
494 /* the ':' insures this string is xs:time */
497 PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret);
505 PARSE_FLOAT(dt->sec, cur, ret);
514 #ifdef DEBUG_EXSLT_DATE
515 xsltGenericDebug(xsltGenericDebugContext,
516 "Parsed time %02i:%02i:%02.f\n",
517 dt->hour, dt->min, dt->sec);
525 * @dt: the #exsltDateValDate to format
526 * @cur: a pointer to an allocated buffer
528 * Formats @dt in xsl:time format. Result is appended to @cur and
529 * @cur is updated to point after the xsl:time.
531 #define FORMAT_TIME(dt, cur) \
532 FORMAT_2_DIGITS(dt->hour, cur); \
535 FORMAT_2_DIGITS(dt->min, cur); \
538 FORMAT_FLOAT(dt->sec, cur, 1);
541 * _exsltDateParseTimeZone:
542 * @dt: pointer to a date structure
543 * @str: pointer to the string to analyze
545 * Parses a time zone without time zone and fills in the appropriate
546 * field of the @dt structure. @str is updated to point just after the
549 * Returns 0 or the error code
552 _exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str)
554 const xmlChar *cur = *str;
574 int isneg = 0, tmp = 0;
575 isneg = (*cur == '-');
579 PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret);
589 PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret);
597 if (!VALID_TZO(dt->tzo))
608 #ifdef DEBUG_EXSLT_DATE
609 xsltGenericDebug(xsltGenericDebugContext,
610 "Parsed time zone offset (%s) %i\n",
611 dt->tz_flag ? "explicit" : "implicit", dt->tzo);
619 * @tzo: the timezone offset to format
620 * @cur: a pointer to an allocated buffer
622 * Formats @tzo timezone. Result is appended to @cur and
623 * @cur is updated to point after the timezone.
625 #define FORMAT_TZ(tzo, cur) \
630 int aTzo = (tzo < 0) ? - tzo : tzo; \
631 int tzHh = aTzo / 60, tzMm = aTzo % 60; \
632 *cur = (tzo < 0) ? '-' : '+' ; \
634 FORMAT_2_DIGITS(tzHh, cur); \
637 FORMAT_2_DIGITS(tzMm, cur); \
640 /****************************************************************
642 * XML Schema Dates/Times Datatypes Handling *
644 ****************************************************************/
647 * exsltDateCreateDate:
648 * @type: type to create
650 * Creates a new #exsltDateVal, uninitialized.
652 * Returns the #exsltDateValPtr
654 static exsltDateValPtr
655 exsltDateCreateDate (exsltDateType type)
659 ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal));
661 xsltGenericError(xsltGenericErrorContext,
662 "exsltDateCreateDate: out of memory\n");
665 memset (ret, 0, sizeof(exsltDateVal));
667 if (type != EXSLT_UNKNOWN)
675 * @date: an #exsltDateValPtr
680 exsltDateFreeDate (exsltDateValPtr date) {
689 * @num: the integer to fill in
690 * @cur: an #xmlChar *
691 * @num_type: an integer flag
693 * Parses a digits integer and updates @num with the value. @cur is
694 * updated to point just after the integer.
695 * In case of error, @num_type is set to -1, values of @num and
696 * @cur are undefined.
698 #define PARSE_DIGITS(num, cur, num_type) \
699 if ((*cur < '0') || (*cur > '9')) \
702 while ((*cur >= '0') && (*cur <= '9')) { \
703 num = num * 10 + (*cur - '0'); \
709 * @num: the double to fill in
710 * @cur: an #xmlChar *
711 * @num_type: an integer flag
713 * Parses a float or integer and updates @num with the value. @cur is
714 * updated to point just after the number. If the number is a float,
715 * then it must have an integer part and a decimal part; @num_type will
716 * be set to 1. If there is no decimal part, @num_type is set to zero.
717 * In case of error, @num_type is set to -1, values of @num and
718 * @cur are undefined.
720 #define PARSE_NUM(num, cur, num_type) \
722 PARSE_DIGITS(num, cur, num_type); \
723 if (!num_type && (*cur == '.')) { \
726 if ((*cur < '0') || (*cur > '9')) \
730 while ((*cur >= '0') && (*cur <= '9')) { \
732 num += (*cur - '0') * mult; \
741 * Returns the current date and time.
743 static exsltDateValPtr
744 exsltDateCurrent (void)
746 struct tm *localTm, *gmTm;
753 ret = exsltDateCreateDate(XS_DATETIME);
757 /* get current time */
760 localtime_r(&secs, &localTmS);
763 localTm = localtime(&secs);
766 /* get real year, not years since 1900 */
767 ret->value.date.year = localTm->tm_year + 1900;
769 ret->value.date.mon = localTm->tm_mon + 1;
770 ret->value.date.day = localTm->tm_mday;
771 ret->value.date.hour = localTm->tm_hour;
772 ret->value.date.min = localTm->tm_min;
774 /* floating point seconds */
775 ret->value.date.sec = (double) localTm->tm_sec;
777 /* determine the time zone offset from local to gm time */
778 gmTm = gmtime(&secs);
779 ret->value.date.tz_flag = 0;
780 ret->value.date.tzo = (((ret->value.date.day * 1440) +
781 (ret->value.date.hour * 60) +
782 ret->value.date.min) -
783 ((gmTm->tm_mday * 1440) + (gmTm->tm_hour * 60) +
792 * @dateTime: string to analyze
794 * Parses a date/time string
796 * Returns a newly built #exsltDateValPtr of NULL in case of error
798 static exsltDateValPtr
799 exsltDateParse (const xmlChar *dateTime)
803 const xmlChar *cur = dateTime;
805 #define RETURN_TYPE_IF_VALID(t) \
806 if (IS_TZO_CHAR(*cur)) { \
807 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \
816 if (dateTime == NULL)
819 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
822 dt = exsltDateCreateDate(EXSLT_UNKNOWN);
826 if ((cur[0] == '-') && (cur[1] == '-')) {
828 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
833 /* is it an xs:gDay? */
836 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
840 RETURN_TYPE_IF_VALID(XS_GDAY);
846 * it should be an xs:gMonthDay or xs:gMonth
848 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
856 /* is it an xs:gMonth? */
859 RETURN_TYPE_IF_VALID(XS_GMONTH);
863 /* it should be an xs:gMonthDay */
864 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
868 RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
874 * It's a right-truncated date or an xs:time.
875 * Try to parse an xs:time then fallback on right-truncated dates.
877 if ((*cur >= '0') && (*cur <= '9')) {
878 ret = _exsltDateParseTime(&(dt->value.date), &cur);
880 /* it's an xs:time */
881 RETURN_TYPE_IF_VALID(XS_TIME);
885 /* fallback on date parsing */
888 ret = _exsltDateParseGYear(&(dt->value.date), &cur);
892 /* is it an xs:gYear? */
893 RETURN_TYPE_IF_VALID(XS_GYEAR);
899 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
903 /* is it an xs:gYearMonth? */
904 RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
910 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
911 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
914 /* is it an xs:date? */
915 RETURN_TYPE_IF_VALID(XS_DATE);
921 /* it should be an xs:dateTime */
922 ret = _exsltDateParseTime(&(dt->value.date), &cur);
926 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
927 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
930 dt->type = XS_DATETIME;
936 exsltDateFreeDate(dt);
941 * exsltDateParseDuration:
942 * @duration: string to analyze
944 * Parses a duration string
946 * Returns a newly built #exsltDateValPtr of NULL in case of error
948 static exsltDateValPtr
949 exsltDateParseDuration (const xmlChar *duration)
951 const xmlChar *cur = duration;
954 unsigned int seq = 0;
956 if (duration == NULL)
964 /* duration must start with 'P' (after sign) */
968 dur = exsltDateCreateDate(XS_DURATION);
974 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
975 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
976 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
978 /* input string should be empty or invalid date/time item */
979 if (seq >= sizeof(desig))
982 /* T designator must be present for time items */
992 /* parse the number portion of the item */
993 PARSE_NUM(num, cur, num_type);
995 if ((num_type == -1) || (*cur == 0))
998 /* update duration based on item type */
999 while (seq < sizeof(desig)) {
1000 if (*cur == desig[seq]) {
1002 /* verify numeric type; only seconds can be float */
1003 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
1008 dur->value.dur.mon = (long)num * 12;
1011 dur->value.dur.mon += (long)num;
1014 /* convert to seconds using multiplier */
1015 dur->value.dur.sec += num * multi[seq];
1020 break; /* exit loop */
1022 /* no date designators found? */
1030 dur->value.dur.mon = -dur->value.dur.mon;
1031 dur->value.dur.day = -dur->value.dur.day;
1032 dur->value.dur.sec = -dur->value.dur.sec;
1035 #ifdef DEBUG_EXSLT_DATE
1036 xsltGenericDebug(xsltGenericDebugContext,
1037 "Parsed duration %f\n", dur->value.dur.sec);
1044 exsltDateFreeDate(dur);
1050 * @num: number to format
1051 * @cur: current location to convert number
1053 * @item: char designator
1056 #define FORMAT_ITEM(num, cur, limit, item) \
1058 long comp = (long)num / limit; \
1060 FORMAT_FLOAT((double)comp, cur, 0); \
1062 num -= (double)(comp * limit); \
1067 * exsltDateFormatDuration:
1068 * @dt: an #exsltDateValDurationPtr
1070 * Formats @dt in xs:duration format.
1072 * Returns a newly allocated string, or NULL in case of error
1075 exsltDateFormatDuration (const exsltDateValDurationPtr dt)
1077 xmlChar buf[100], *cur = buf;
1079 double years, months;
1084 /* quick and dirty check */
1085 if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
1086 return xmlStrdup((xmlChar*)"P0D");
1089 days = (double)dt->day;
1090 years = (double)(dt->mon / 12);
1091 months = (double)(dt->mon % 12);
1116 FORMAT_ITEM(years, cur, 1, 'Y');
1119 if (months != 0.0) {
1120 FORMAT_ITEM(months, cur, 1, 'M');
1123 if (secs >= SECS_PER_DAY) {
1124 double tmp = floor(secs / SECS_PER_DAY);
1126 secs -= (tmp * SECS_PER_DAY);
1129 FORMAT_ITEM(days, cur, 1, 'D');
1133 FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
1134 FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
1136 FORMAT_FLOAT(secs, cur, 0);
1142 return xmlStrdup(buf);
1146 * exsltDateFormatDateTime:
1147 * @dt: an #exsltDateValDatePtr
1149 * Formats @dt in xs:dateTime format.
1151 * Returns a newly allocated string, or NULL in case of error
1154 exsltDateFormatDateTime (const exsltDateValDatePtr dt)
1156 xmlChar buf[100], *cur = buf;
1158 if ((dt == NULL) || !VALID_DATETIME(dt))
1161 FORMAT_DATE(dt, cur);
1164 FORMAT_TIME(dt, cur);
1165 FORMAT_TZ(dt->tzo, cur);
1168 return xmlStrdup(buf);
1172 * exsltDateFormatDate:
1173 * @dt: an #exsltDateValDatePtr
1175 * Formats @dt in xs:date format.
1177 * Returns a newly allocated string, or NULL in case of error
1180 exsltDateFormatDate (const exsltDateValDatePtr dt)
1182 xmlChar buf[100], *cur = buf;
1184 if ((dt == NULL) || !VALID_DATETIME(dt))
1187 FORMAT_DATE(dt, cur);
1188 if (dt->tz_flag || (dt->tzo != 0)) {
1189 FORMAT_TZ(dt->tzo, cur);
1193 return xmlStrdup(buf);
1197 * exsltDateFormatTime:
1198 * @dt: an #exsltDateValDatePtr
1200 * Formats @dt in xs:time format.
1202 * Returns a newly allocated string, or NULL in case of error
1205 exsltDateFormatTime (const exsltDateValDatePtr dt)
1207 xmlChar buf[100], *cur = buf;
1209 if ((dt == NULL) || !VALID_TIME(dt))
1212 FORMAT_TIME(dt, cur);
1213 if (dt->tz_flag || (dt->tzo != 0)) {
1214 FORMAT_TZ(dt->tzo, cur);
1218 return xmlStrdup(buf);
1223 * @dt: an #exsltDateValPtr
1225 * Formats @dt in the proper format.
1226 * Note: xs:gmonth and xs:gday are not formatted as there are no
1227 * routines that output them.
1229 * Returns a newly allocated string, or NULL in case of error
1232 exsltDateFormat (const exsltDateValPtr dt)
1240 return exsltDateFormatDuration(&(dt->value.dur));
1242 return exsltDateFormatDateTime(&(dt->value.date));
1244 return exsltDateFormatDate(&(dt->value.date));
1246 return exsltDateFormatTime(&(dt->value.date));
1251 if (dt->type & XS_GYEAR) {
1252 xmlChar buf[20], *cur = buf;
1254 FORMAT_GYEAR(dt->value.date.year, cur);
1255 if (dt->type == XS_GYEARMONTH) {
1258 FORMAT_GMONTH(dt->value.date.mon, cur);
1261 if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
1262 FORMAT_TZ(dt->value.date.tzo, cur);
1265 return xmlStrdup(buf);
1272 * _exsltDateCastYMToDays:
1273 * @dt: an #exsltDateValPtr
1275 * Convert mon and year of @dt to total number of days. Take the
1276 * number of years since (or before) 1 AD and add the number of leap
1277 * years. This is a function because negative
1278 * years must be handled a little differently and there is no zero year.
1280 * Returns number of days.
1283 _exsltDateCastYMToDays (const exsltDateValPtr dt)
1287 if (dt->value.date.year < 0)
1288 ret = (dt->value.date.year * 365) +
1289 (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
1290 ((dt->value.date.year+1)/400)) +
1291 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1293 ret = ((dt->value.date.year-1) * 365) +
1294 (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
1295 ((dt->value.date.year-1)/400)) +
1296 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1303 * @dt: an #exsltDateValPtr
1305 * Calculates the number of seconds in the time portion of @dt.
1309 #define TIME_TO_NUMBER(dt) \
1310 ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
1311 (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
1314 * exsltDateCastDateToNumber:
1315 * @dt: an #exsltDateValPtr
1317 * Calculates the number of seconds from year zero.
1319 * Returns seconds from zero year.
1322 exsltDateCastDateToNumber (const exsltDateValPtr dt)
1329 if ((dt->type & XS_GYEAR) == XS_GYEAR) {
1330 ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
1334 if (dt->type == XS_DURATION) {
1335 ret += (double)dt->value.dur.day * SECS_PER_DAY;
1336 ret += dt->value.dur.sec;
1338 ret += (double)dt->value.date.day * SECS_PER_DAY;
1340 ret += TIME_TO_NUMBER(dt);
1348 * _exsltDateTruncateDate:
1349 * @dt: an #exsltDateValPtr
1350 * @type: dateTime type to set to
1352 * Set @dt to truncated @type.
1354 * Returns 0 success, non-zero otherwise.
1357 _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
1362 if ((type & XS_TIME) != XS_TIME) {
1363 dt->value.date.hour = 0;
1364 dt->value.date.min = 0;
1365 dt->value.date.sec = 0.0;
1368 if ((type & XS_GDAY) != XS_GDAY)
1369 dt->value.date.day = 0;
1371 if ((type & XS_GMONTH) != XS_GMONTH)
1372 dt->value.date.mon = 0;
1374 if ((type & XS_GYEAR) != XS_GYEAR)
1375 dt->value.date.year = 0;
1384 * @yday: year day (1-366)
1387 * Determine the day-in-week from @yday and @yr. 0001-01-01 was
1388 * a Monday so all other days are calculated from there. Take the
1389 * number of years since (or before) add the number of leap years and
1390 * the day-in-year and mod by 7. This is a function because negative
1391 * years must be handled a little differently and there is no zero year.
1393 * Returns day in week (Sunday = 0).
1396 _exsltDateDayInWeek(long yday, long yr)
1401 ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
1405 ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
1411 * macros for adding date/times and durations
1413 #define FQUOTIENT(a,b) ((floor(((double)a/(double)b))))
1414 #define MODULO(a,b) ((a - FQUOTIENT(a,b) * b))
1415 #define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
1416 #define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
1420 * @dt: an #exsltDateValPtr
1421 * @dur: an #exsltDateValPtr of type #XS_DURATION
1423 * Compute a new date/time from @dt and @dur. This function assumes @dt
1424 * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
1426 * Returns date/time pointer or NULL.
1428 static exsltDateValPtr
1429 _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
1431 exsltDateValPtr ret;
1432 long carry, tempdays, temp;
1433 exsltDateValDatePtr r, d;
1434 exsltDateValDurationPtr u;
1436 if ((dt == NULL) || (dur == NULL))
1439 ret = exsltDateCreateDate(dt->type);
1443 r = &(ret->value.date);
1444 d = &(dt->value.date);
1445 u = &(dur->value.dur);
1451 /* normalize for time zone offset */
1452 u->sec -= (d->tzo * 60); /* changed from + to - (bug 153000) */
1460 carry = d->mon + u->mon;
1461 r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
1462 carry = (long)FQUOTIENT_RANGE(carry, 1, 13);
1464 /* year (may be modified later) */
1465 r->year = d->year + carry;
1475 r->tz_flag = d->tz_flag;
1478 r->sec = d->sec + u->sec;
1479 carry = (long)FQUOTIENT((long)r->sec, 60);
1480 if (r->sec != 0.0) {
1481 r->sec = MODULO(r->sec, 60.0);
1486 r->min = (unsigned int)MODULO(carry, 60);
1487 carry = (long)FQUOTIENT(carry, 60);
1491 r->hour = (unsigned int)MODULO(carry, 24);
1492 carry = (long)FQUOTIENT(carry, 24);
1496 * Note we use tempdays because the temporary values may need more
1499 if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
1500 (d->day > MAX_DAYINMONTH(r->year, r->mon)))
1501 tempdays = MAX_DAYINMONTH(r->year, r->mon);
1502 else if (d->day < 1)
1507 tempdays += u->day + carry;
1511 long tmon = (long)MODULO_RANGE(r->mon-1, 1, 13);
1512 long tyr = r->year + (long)FQUOTIENT_RANGE(r->mon-1, 1, 13);
1515 tempdays += MAX_DAYINMONTH(tyr, tmon);
1517 } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
1518 tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
1523 temp = r->mon + carry;
1524 r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
1525 r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
1537 * adjust the date/time type to the date values
1539 if (ret->type != XS_DATETIME) {
1540 if ((r->hour) || (r->min) || (r->sec))
1541 ret->type = XS_DATETIME;
1542 else if (ret->type != XS_DATE) {
1543 if ((r->mon != 1) && (r->day != 1))
1544 ret->type = XS_DATE;
1545 else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
1546 ret->type = XS_GYEARMONTH;
1554 * exsltDateNormalize:
1555 * @dt: an #exsltDateValPtr
1557 * Normalize @dt to GMT time.
1561 exsltDateNormalize (exsltDateValPtr dt)
1563 exsltDateValPtr dur, tmp;
1568 if (((dt->type & XS_TIME) != XS_TIME) || (dt->value.date.tzo == 0))
1571 dur = exsltDateCreateDate(XS_DURATION);
1575 tmp = _exsltDateAdd(dt, dur);
1579 memcpy(dt, tmp, sizeof(exsltDateVal));
1581 exsltDateFreeDate(tmp);
1582 exsltDateFreeDate(dur);
1584 dt->value.date.tzo = 0;
1588 * _exsltDateDifference:
1589 * @x: an #exsltDateValPtr
1590 * @y: an #exsltDateValPtr
1591 * @flag: force difference in days
1593 * Calculate the difference between @x and @y as a duration
1594 * (i.e. y - x). If the @flag is set then even if the least specific
1595 * format of @x or @y is xs:gYear or xs:gYearMonth.
1597 * Returns date/time pointer or NULL.
1599 static exsltDateValPtr
1600 _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
1602 exsltDateValPtr ret;
1604 if ((x == NULL) || (y == NULL))
1607 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
1608 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
1611 exsltDateNormalize(x);
1612 exsltDateNormalize(y);
1615 * the operand with the most specific format must be converted to
1616 * the same type as the operand with the least specific format.
1618 if (x->type != y->type) {
1619 if (x->type < y->type) {
1620 _exsltDateTruncateDate(y, x->type);
1622 _exsltDateTruncateDate(x, y->type);
1626 ret = exsltDateCreateDate(XS_DURATION);
1630 if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
1631 /* compute the difference in months */
1632 ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
1633 ((x->value.date.year * 12) + x->value.date.mon);
1634 /* The above will give a wrong result if x and y are on different sides
1635 of the September 1752. Resolution is welcome :-) */
1637 ret->value.dur.day = _exsltDateCastYMToDays(y) -
1638 _exsltDateCastYMToDays(x);
1639 ret->value.dur.day += y->value.date.day - x->value.date.day;
1640 ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
1641 if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
1642 ret->value.dur.day -= 1;
1643 ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
1644 } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
1645 ret->value.dur.day += 1;
1646 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1654 * _exsltDateAddDurCalc
1655 * @ret: an exsltDateValPtr for the return value:
1656 * @x: an exsltDateValPtr for the first operand
1657 * @y: an exsltDateValPtr for the second operand
1659 * Add two durations, catering for possible negative values.
1660 * The sum is placed in @ret.
1662 * Returns 1 for success, 0 if error detected.
1665 _exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
1671 ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
1674 ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
1675 carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
1676 if (ret->value.dur.sec != 0.0) {
1677 ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
1679 * Our function MODULO always gives us a positive value, so
1680 * if we end up with a "-ve" carry we need to adjust it
1681 * appropriately (bug 154021)
1683 if ((carry < 0) && (ret->value.dur.sec != 0)) {
1684 /* change seconds to equiv negative modulus */
1685 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1691 ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
1694 * are the results indeterminate? i.e. how do you subtract days from
1697 if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
1698 (ret->value.dur.mon < 0)) ||
1699 (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
1700 (ret->value.dur.mon > 0))) {
1707 * _exsltDateAddDuration:
1708 * @x: an #exsltDateValPtr of type #XS_DURATION
1709 * @y: an #exsltDateValPtr of type #XS_DURATION
1711 * Compute a new duration from @x and @y.
1713 * Returns date/time pointer or NULL.
1715 static exsltDateValPtr
1716 _exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
1718 exsltDateValPtr ret;
1720 if ((x == NULL) || (y == NULL))
1723 ret = exsltDateCreateDate(XS_DURATION);
1727 if (_exsltDateAddDurCalc(ret, x, y))
1730 exsltDateFreeDate(ret);
1734 /****************************************************************
1736 * EXSLT - Dates and Times functions *
1738 ****************************************************************/
1741 * exsltDateDateTime:
1743 * Implements the EXSLT - Dates and Times date-time() function:
1744 * string date:date-time()
1746 * Returns the current date and time as a date/time string.
1749 exsltDateDateTime (void)
1751 xmlChar *ret = NULL;
1753 exsltDateValPtr cur;
1755 cur = exsltDateCurrent();
1757 ret = exsltDateFormatDateTime(&(cur->value.date));
1758 exsltDateFreeDate(cur);
1767 * @dateTime: a date/time string
1769 * Implements the EXSLT - Dates and Times date() function:
1770 * string date:date (string?)
1772 * Returns the date specified in the date/time string given as the
1773 * argument. If no argument is given, then the current local
1774 * date/time, as returned by date:date-time is used as a default
1776 * The date/time string specified as an argument must be a string in
1777 * the format defined as the lexical representation of either
1778 * xs:dateTime or xs:date. If the argument is not in either of these
1779 * formats, returns NULL.
1782 exsltDateDate (const xmlChar *dateTime)
1784 exsltDateValPtr dt = NULL;
1785 xmlChar *ret = NULL;
1787 if (dateTime == NULL) {
1789 dt = exsltDateCurrent();
1794 dt = exsltDateParse(dateTime);
1797 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1798 exsltDateFreeDate(dt);
1803 ret = exsltDateFormatDate(&(dt->value.date));
1804 exsltDateFreeDate(dt);
1811 * @dateTime: a date/time string
1813 * Implements the EXSLT - Dates and Times time() function:
1814 * string date:time (string?)
1816 * Returns the time specified in the date/time string given as the
1817 * argument. If no argument is given, then the current local
1818 * date/time, as returned by date:date-time is used as a default
1820 * The date/time string specified as an argument must be a string in
1821 * the format defined as the lexical representation of either
1822 * xs:dateTime or xs:time. If the argument is not in either of these
1823 * formats, returns NULL.
1826 exsltDateTime (const xmlChar *dateTime)
1828 exsltDateValPtr dt = NULL;
1829 xmlChar *ret = NULL;
1831 if (dateTime == NULL) {
1833 dt = exsltDateCurrent();
1838 dt = exsltDateParse(dateTime);
1841 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1842 exsltDateFreeDate(dt);
1847 ret = exsltDateFormatTime(&(dt->value.date));
1848 exsltDateFreeDate(dt);
1855 * @dateTime: a date/time string
1857 * Implements the EXSLT - Dates and Times year() function
1858 * number date:year (string?)
1859 * Returns the year of a date as a number. If no argument is given,
1860 * then the current local date/time, as returned by date:date-time is
1861 * used as a default argument.
1862 * The date/time string specified as the first argument must be a
1863 * right-truncated string in the format defined as the lexical
1864 * representation of xs:dateTime in one of the formats defined in [XML
1865 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1866 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1867 * - xs:date (CCYY-MM-DD)
1868 * - xs:gYearMonth (CCYY-MM)
1870 * If the date/time string is not in one of these formats, then NaN is
1874 exsltDateYear (const xmlChar *dateTime)
1879 if (dateTime == NULL) {
1881 dt = exsltDateCurrent();
1886 dt = exsltDateParse(dateTime);
1889 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1890 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
1891 exsltDateFreeDate(dt);
1896 ret = (double) dt->value.date.year;
1897 exsltDateFreeDate(dt);
1903 * exsltDateLeapYear:
1904 * @dateTime: a date/time string
1906 * Implements the EXSLT - Dates and Times leap-year() function:
1907 * boolean date:leap-yea (string?)
1908 * Returns true if the year given in a date is a leap year. If no
1909 * argument is given, then the current local date/time, as returned by
1910 * date:date-time is used as a default argument.
1911 * The date/time string specified as the first argument must be a
1912 * right-truncated string in the format defined as the lexical
1913 * representation of xs:dateTime in one of the formats defined in [XML
1914 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1915 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1916 * - xs:date (CCYY-MM-DD)
1917 * - xs:gYearMonth (CCYY-MM)
1919 * If the date/time string is not in one of these formats, then NaN is
1922 static xmlXPathObjectPtr
1923 exsltDateLeapYear (const xmlChar *dateTime)
1927 year = exsltDateYear(dateTime);
1928 if (xmlXPathIsNaN(year))
1929 return xmlXPathNewFloat(xmlXPathNAN);
1931 if (IS_LEAP((long)year))
1932 return xmlXPathNewBoolean(1);
1934 return xmlXPathNewBoolean(0);
1938 * exsltDateMonthInYear:
1939 * @dateTime: a date/time string
1941 * Implements the EXSLT - Dates and Times month-in-year() function:
1942 * number date:month-in-year (string?)
1943 * Returns the month of a date as a number. If no argument is given,
1944 * then the current local date/time, as returned by date:date-time is
1945 * used the default argument.
1946 * The date/time string specified as the argument is a left or
1947 * right-truncated string in the format defined as the lexical
1948 * representation of xs:dateTime in one of the formats defined in [XML
1949 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1950 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1951 * - xs:date (CCYY-MM-DD)
1952 * - xs:gYearMonth (CCYY-MM)
1953 * - xs:gMonth (--MM--)
1954 * - xs:gMonthDay (--MM-DD)
1955 * If the date/time string is not in one of these formats, then NaN is
1959 exsltDateMonthInYear (const xmlChar *dateTime)
1964 if (dateTime == NULL) {
1966 dt = exsltDateCurrent();
1971 dt = exsltDateParse(dateTime);
1974 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1975 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
1976 (dt->type != XS_GMONTHDAY)) {
1977 exsltDateFreeDate(dt);
1982 ret = (double) dt->value.date.mon;
1983 exsltDateFreeDate(dt);
1989 * exsltDateMonthName:
1990 * @dateTime: a date/time string
1992 * Implements the EXSLT - Dates and Time month-name() function
1993 * string date:month-name (string?)
1994 * Returns the full name of the month of a date. If no argument is
1995 * given, then the current local date/time, as returned by
1996 * date:date-time is used the default argument.
1997 * The date/time string specified as the argument is a left or
1998 * right-truncated string in the format defined as the lexical
1999 * representation of xs:dateTime in one of the formats defined in [XML
2000 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2001 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2002 * - xs:date (CCYY-MM-DD)
2003 * - xs:gYearMonth (CCYY-MM)
2004 * - xs:gMonth (--MM--)
2005 * If the date/time string is not in one of these formats, then an
2006 * empty string ('') is returned.
2007 * The result is an English month name: one of 'January', 'February',
2008 * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
2009 * 'October', 'November' or 'December'.
2011 static const xmlChar *
2012 exsltDateMonthName (const xmlChar *dateTime)
2014 static const xmlChar monthNames[13][10] = {
2016 { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
2017 { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
2018 { 'M', 'a', 'r', 'c', 'h', 0 },
2019 { 'A', 'p', 'r', 'i', 'l', 0 },
2020 { 'M', 'a', 'y', 0 },
2021 { 'J', 'u', 'n', 'e', 0 },
2022 { 'J', 'u', 'l', 'y', 0 },
2023 { 'A', 'u', 'g', 'u', 's', 't', 0 },
2024 { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
2025 { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
2026 { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
2027 { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
2030 month = (int) exsltDateMonthInYear(dateTime);
2031 if (!VALID_MONTH(month))
2033 return monthNames[month];
2037 * exsltDateMonthAbbreviation:
2038 * @dateTime: a date/time string
2040 * Implements the EXSLT - Dates and Time month-abbreviation() function
2041 * string date:month-abbreviation (string?)
2042 * Returns the abbreviation of the month of a date. If no argument is
2043 * given, then the current local date/time, as returned by
2044 * date:date-time is used the default argument.
2045 * The date/time string specified as the argument is a left or
2046 * right-truncated string in the format defined as the lexical
2047 * representation of xs:dateTime in one of the formats defined in [XML
2048 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2049 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2050 * - xs:date (CCYY-MM-DD)
2051 * - xs:gYearMonth (CCYY-MM)
2052 * - xs:gMonth (--MM--)
2053 * If the date/time string is not in one of these formats, then an
2054 * empty string ('') is returned.
2055 * The result is an English month abbreviation: one of 'Jan', 'Feb',
2056 * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
2059 static const xmlChar *
2060 exsltDateMonthAbbreviation (const xmlChar *dateTime)
2062 static const xmlChar monthAbbreviations[13][4] = {
2064 { 'J', 'a', 'n', 0 },
2065 { 'F', 'e', 'b', 0 },
2066 { 'M', 'a', 'r', 0 },
2067 { 'A', 'p', 'r', 0 },
2068 { 'M', 'a', 'y', 0 },
2069 { 'J', 'u', 'n', 0 },
2070 { 'J', 'u', 'l', 0 },
2071 { 'A', 'u', 'g', 0 },
2072 { 'S', 'e', 'p', 0 },
2073 { 'O', 'c', 't', 0 },
2074 { 'N', 'o', 'v', 0 },
2075 { 'D', 'e', 'c', 0 }
2078 month = (int) exsltDateMonthInYear(dateTime);
2079 if(!VALID_MONTH(month))
2081 return monthAbbreviations[month];
2085 * exsltDateWeekInYear:
2086 * @dateTime: a date/time string
2088 * Implements the EXSLT - Dates and Times week-in-year() function
2089 * number date:week-in-year (string?)
2090 * Returns the week of the year as a number. If no argument is given,
2091 * then the current local date/time, as returned by date:date-time is
2092 * used as the default argument. For the purposes of numbering,
2093 * counting follows ISO 8601: week 1 in a year is the week containing
2094 * the first Thursday of the year, with new weeks beginning on a
2096 * The date/time string specified as the argument is a right-truncated
2097 * string in the format defined as the lexical representation of
2098 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2099 * Datatypes]. The permitted formats are as follows:
2100 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2101 * - xs:date (CCYY-MM-DD)
2102 * If the date/time string is not in one of these formats, then NaN is
2106 exsltDateWeekInYear (const xmlChar *dateTime)
2109 long fdiy, fdiw, ret;
2111 if (dateTime == NULL) {
2113 dt = exsltDateCurrent();
2118 dt = exsltDateParse(dateTime);
2121 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2122 exsltDateFreeDate(dt);
2127 fdiy = DAY_IN_YEAR(1, 1, dt->value.date.year);
2130 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2131 * is the first day-in-week
2133 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2135 ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2136 dt->value.date.year) / 7;
2138 /* ISO 8601 adjustment, 3 is Thu */
2142 exsltDateFreeDate(dt);
2144 return (double) ret;
2148 * exsltDateWeekInMonth:
2149 * @dateTime: a date/time string
2151 * Implements the EXSLT - Dates and Times week-in-month() function
2152 * number date:week-in-month (string?)
2153 * The date:week-in-month function returns the week in a month of a
2154 * date as a number. If no argument is given, then the current local
2155 * date/time, as returned by date:date-time is used the default
2156 * argument. For the purposes of numbering, the first day of the month
2157 * is in week 1 and new weeks begin on a Monday (so the first and last
2158 * weeks in a month will often have less than 7 days in them).
2159 * The date/time string specified as the argument is a right-truncated
2160 * string in the format defined as the lexical representation of
2161 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2162 * Datatypes]. The permitted formats are as follows:
2163 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2164 * - xs:date (CCYY-MM-DD)
2165 * If the date/time string is not in one of these formats, then NaN is
2169 exsltDateWeekInMonth (const xmlChar *dateTime)
2172 long fdiy, fdiw, ret;
2174 if (dateTime == NULL) {
2176 dt = exsltDateCurrent();
2181 dt = exsltDateParse(dateTime);
2184 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2185 exsltDateFreeDate(dt);
2190 fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
2192 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2193 * is the first day-in-week
2195 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2197 ret = ((dt->value.date.day + fdiw) / 7) + 1;
2199 exsltDateFreeDate(dt);
2201 return (double) ret;
2205 * exsltDateDayInYear:
2206 * @dateTime: a date/time string
2208 * Implements the EXSLT - Dates and Times day-in-year() function
2209 * number date:day-in-year (string?)
2210 * Returns the day of a date in a year as a number. If no argument is
2211 * given, then the current local date/time, as returned by
2212 * date:date-time is used the default argument.
2213 * The date/time string specified as the argument is a right-truncated
2214 * string in the format defined as the lexical representation of
2215 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2216 * Datatypes]. The permitted formats are as follows:
2217 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2218 * - xs:date (CCYY-MM-DD)
2219 * If the date/time string is not in one of these formats, then NaN is
2223 exsltDateDayInYear (const xmlChar *dateTime)
2228 if (dateTime == NULL) {
2230 dt = exsltDateCurrent();
2235 dt = exsltDateParse(dateTime);
2238 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2239 exsltDateFreeDate(dt);
2244 ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2245 dt->value.date.year);
2247 exsltDateFreeDate(dt);
2249 return (double) ret;
2253 * exsltDateDayInMonth:
2254 * @dateTime: a date/time string
2256 * Implements the EXSLT - Dates and Times day-in-month() function:
2257 * number date:day-in-month (string?)
2258 * Returns the day of a date as a number. If no argument is given,
2259 * then the current local date/time, as returned by date:date-time is
2260 * used the default argument.
2261 * The date/time string specified as the argument is a left or
2262 * right-truncated string in the format defined as the lexical
2263 * representation of xs:dateTime in one of the formats defined in [XML
2264 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2265 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2266 * - xs:date (CCYY-MM-DD)
2267 * - xs:gMonthDay (--MM-DD)
2269 * If the date/time string is not in one of these formats, then NaN is
2273 exsltDateDayInMonth (const xmlChar *dateTime)
2278 if (dateTime == NULL) {
2280 dt = exsltDateCurrent();
2285 dt = exsltDateParse(dateTime);
2288 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
2289 (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
2290 exsltDateFreeDate(dt);
2295 ret = (double) dt->value.date.day;
2296 exsltDateFreeDate(dt);
2302 * exsltDateDayOfWeekInMonth:
2303 * @dateTime: a date/time string
2305 * Implements the EXSLT - Dates and Times day-of-week-in-month() function:
2306 * number date:day-of-week-in-month (string?)
2307 * Returns the day-of-the-week in a month of a date as a number
2308 * (e.g. 3 for the 3rd Tuesday in May). If no argument is
2309 * given, then the current local date/time, as returned by
2310 * date:date-time is used the default argument.
2311 * The date/time string specified as the argument is a right-truncated
2312 * string in the format defined as the lexical representation of
2313 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2314 * Datatypes]. The permitted formats are as follows:
2315 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2316 * - xs:date (CCYY-MM-DD)
2317 * If the date/time string is not in one of these formats, then NaN is
2321 exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
2326 if (dateTime == NULL) {
2328 dt = exsltDateCurrent();
2333 dt = exsltDateParse(dateTime);
2336 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2337 exsltDateFreeDate(dt);
2342 ret = (dt->value.date.day / 7) + 1;
2344 exsltDateFreeDate(dt);
2346 return (double) ret;
2350 * exsltDateDayInWeek:
2351 * @dateTime: a date/time string
2353 * Implements the EXSLT - Dates and Times day-in-week() function:
2354 * number date:day-in-week (string?)
2355 * Returns the day of the week given in a date as a number. If no
2356 * argument is given, then the current local date/time, as returned by
2357 * date:date-time is used the default argument.
2358 * The date/time string specified as the argument is a left or
2359 * right-truncated string in the format defined as the lexical
2360 * representation of xs:dateTime in one of the formats defined in [XML
2361 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2362 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2363 * - xs:date (CCYY-MM-DD)
2364 * If the date/time string is not in one of these formats, then NaN is
2366 * The numbering of days of the week starts at 1 for Sunday, 2 for
2367 * Monday and so on up to 7 for Saturday.
2370 exsltDateDayInWeek (const xmlChar *dateTime)
2375 if (dateTime == NULL) {
2377 dt = exsltDateCurrent();
2382 dt = exsltDateParse(dateTime);
2385 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2386 exsltDateFreeDate(dt);
2391 diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2392 dt->value.date.year);
2394 ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
2396 exsltDateFreeDate(dt);
2398 return (double) ret;
2403 * @dateTime: a date/time string
2405 * Implements the EXSLT - Dates and Time day-name() function
2406 * string date:day-name (string?)
2407 * Returns the full name of the day of the week of a date. If no
2408 * argument is given, then the current local date/time, as returned by
2409 * date:date-time is used the default argument.
2410 * The date/time string specified as the argument is a left or
2411 * right-truncated string in the format defined as the lexical
2412 * representation of xs:dateTime in one of the formats defined in [XML
2413 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2414 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2415 * - xs:date (CCYY-MM-DD)
2416 * If the date/time string is not in one of these formats, then an
2417 * empty string ('') is returned.
2418 * The result is an English day name: one of 'Sunday', 'Monday',
2419 * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
2421 static const xmlChar *
2422 exsltDateDayName (const xmlChar *dateTime)
2424 static const xmlChar dayNames[8][10] = {
2426 { 'S', 'u', 'n', 'd', 'a', 'y', 0 },
2427 { 'M', 'o', 'n', 'd', 'a', 'y', 0 },
2428 { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
2429 { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
2430 { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
2431 { 'F', 'r', 'i', 'd', 'a', 'y', 0 },
2432 { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
2435 day = (int) exsltDateDayInWeek(dateTime);
2436 if((day < 1) || (day > 7))
2438 return dayNames[day];
2442 * exsltDateDayAbbreviation:
2443 * @dateTime: a date/time string
2445 * Implements the EXSLT - Dates and Time day-abbreviation() function
2446 * string date:day-abbreviation (string?)
2447 * Returns the abbreviation 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 a three-letter English day abbreviation: one of
2459 * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
2461 static const xmlChar *
2462 exsltDateDayAbbreviation (const xmlChar *dateTime)
2464 static const xmlChar dayAbbreviations[8][4] = {
2466 { 'S', 'u', 'n', 0 },
2467 { 'M', 'o', 'n', 0 },
2468 { 'T', 'u', 'e', 0 },
2469 { 'W', 'e', 'd', 0 },
2470 { 'T', 'h', 'u', 0 },
2471 { 'F', 'r', 'i', 0 },
2472 { 'S', 'a', 't', 0 }
2475 day = (int) exsltDateDayInWeek(dateTime);
2476 if((day < 1) || (day > 7))
2478 return dayAbbreviations[day];
2482 * exsltDateHourInDay:
2483 * @dateTime: a date/time string
2485 * Implements the EXSLT - Dates and Times day-in-month() function:
2486 * number date:day-in-month (string?)
2487 * Returns the hour of the day as a number. If no argument is given,
2488 * then the current local date/time, as returned by date:date-time is
2489 * 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:time (hh:mm:ss)
2496 * If the date/time string is not in one of these formats, then NaN is
2500 exsltDateHourInDay (const xmlChar *dateTime)
2505 if (dateTime == NULL) {
2507 dt = exsltDateCurrent();
2512 dt = exsltDateParse(dateTime);
2515 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2516 exsltDateFreeDate(dt);
2521 ret = (double) dt->value.date.hour;
2522 exsltDateFreeDate(dt);
2528 * exsltDateMinuteInHour:
2529 * @dateTime: a date/time string
2531 * Implements the EXSLT - Dates and Times day-in-month() function:
2532 * number date:day-in-month (string?)
2533 * Returns the minute of the hour as a number. If no argument is
2534 * given, then the current local date/time, as returned by
2535 * date:date-time is used the default argument.
2536 * The date/time string specified as the argument is a left or
2537 * right-truncated string in the format defined as the lexical
2538 * representation of xs:dateTime in one of the formats defined in [XML
2539 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2540 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2541 * - xs:time (hh:mm:ss)
2542 * If the date/time string is not in one of these formats, then NaN is
2546 exsltDateMinuteInHour (const xmlChar *dateTime)
2551 if (dateTime == NULL) {
2553 dt = exsltDateCurrent();
2558 dt = exsltDateParse(dateTime);
2561 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2562 exsltDateFreeDate(dt);
2567 ret = (double) dt->value.date.min;
2568 exsltDateFreeDate(dt);
2574 * exsltDateSecondInMinute:
2575 * @dateTime: a date/time string
2577 * Implements the EXSLT - Dates and Times second-in-minute() function:
2578 * number date:day-in-month (string?)
2579 * Returns the second of the minute as a number. If no argument is
2580 * given, then the current local date/time, as returned by
2581 * date:date-time is used the default argument.
2582 * The date/time string specified as the argument is a left or
2583 * right-truncated string in the format defined as the lexical
2584 * representation of xs:dateTime in one of the formats defined in [XML
2585 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2586 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2587 * - xs:time (hh:mm:ss)
2588 * If the date/time string is not in one of these formats, then NaN is
2591 * Returns the second or NaN.
2594 exsltDateSecondInMinute (const xmlChar *dateTime)
2599 if (dateTime == NULL) {
2601 dt = exsltDateCurrent();
2606 dt = exsltDateParse(dateTime);
2609 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2610 exsltDateFreeDate(dt);
2615 ret = dt->value.date.sec;
2616 exsltDateFreeDate(dt);
2623 * @xstr: date/time string
2624 * @ystr: date/time string
2626 * Implements the date:add (string,string) function which returns the
2627 * date/time * resulting from adding a duration to a date/time.
2628 * The first argument (@xstr) must be right-truncated date/time
2629 * strings in one of the formats defined in [XML Schema Part 2:
2630 * Datatypes]. The permitted formats are as follows:
2631 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2632 * - xs:date (CCYY-MM-DD)
2633 * - xs:gYearMonth (CCYY-MM)
2635 * The second argument (@ystr) is a string in the format defined for
2636 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2637 * The return value is a right-truncated date/time strings in one of
2638 * the formats defined in [XML Schema Part 2: Datatypes] and listed
2639 * above. This value is calculated using the algorithm described in
2640 * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
2643 * Returns date/time string or NULL.
2646 exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
2648 exsltDateValPtr dt, dur, res;
2651 if ((xstr == NULL) || (ystr == NULL))
2654 dt = exsltDateParse(xstr);
2657 else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
2658 exsltDateFreeDate(dt);
2662 dur = exsltDateParseDuration(ystr);
2664 exsltDateFreeDate(dt);
2668 res = _exsltDateAdd(dt, dur);
2670 exsltDateFreeDate(dt);
2671 exsltDateFreeDate(dur);
2676 ret = exsltDateFormat(res);
2677 exsltDateFreeDate(res);
2683 * exsltDateAddDuration:
2684 * @xstr: first duration string
2685 * @ystr: second duration string
2687 * Implements the date:add-duration (string,string) function which returns
2688 * the duration resulting from adding two durations together.
2689 * Both arguments are strings in the format defined for xs:duration
2690 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
2691 * argument is not in this format, the function returns an empty string
2693 * The return value is a string in the format defined for xs:duration
2694 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2695 * The durations can usually be added by summing the numbers given for
2696 * each of the components in the durations. However, if the durations
2697 * are differently signed, then this sometimes results in durations
2698 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2699 * In these cases, the function returns an empty string ('').
2701 * Returns duration string or NULL.
2704 exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
2706 exsltDateValPtr x, y, res;
2709 if ((xstr == NULL) || (ystr == NULL))
2712 x = exsltDateParseDuration(xstr);
2716 y = exsltDateParseDuration(ystr);
2718 exsltDateFreeDate(x);
2722 res = _exsltDateAddDuration(x, y);
2724 exsltDateFreeDate(x);
2725 exsltDateFreeDate(y);
2730 ret = exsltDateFormatDuration(&(res->value.dur));
2731 exsltDateFreeDate(res);
2737 * exsltDateSumFunction:
2738 * @ns: a node set of duration strings
2740 * The date:sum function adds a set of durations together.
2741 * The string values of the nodes in the node set passed as an argument
2742 * are interpreted as durations and added together as if using the
2743 * date:add-duration function. (from exslt.org)
2745 * The return value is a string in the format defined for xs:duration
2746 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2747 * The durations can usually be added by summing the numbers given for
2748 * each of the components in the durations. However, if the durations
2749 * are differently signed, then this sometimes results in durations
2750 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2751 * In these cases, the function returns an empty string ('').
2753 * Returns duration string or NULL.
2756 exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
2761 exsltDateValPtr x, total;
2766 xmlXPathSetArityError (ctxt);
2770 /* We need to delay the freeing of value->user */
2771 if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
2772 user = ctxt->value->user;
2773 ctxt->value->boolval = 0;
2774 ctxt->value->user = NULL;
2777 ns = xmlXPathPopNodeSet (ctxt);
2778 if (xmlXPathCheckError (ctxt))
2781 if ((ns == NULL) || (ns->nodeNr == 0)) {
2782 xmlXPathReturnEmptyString (ctxt);
2784 xmlXPathFreeNodeSet (ns);
2788 total = exsltDateCreateDate (XS_DURATION);
2789 if (total == NULL) {
2790 xmlXPathFreeNodeSet (ns);
2794 for (i = 0; i < ns->nodeNr; i++) {
2796 tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
2798 xmlXPathFreeNodeSet (ns);
2799 exsltDateFreeDate (total);
2803 x = exsltDateParseDuration (tmp);
2806 exsltDateFreeDate (total);
2807 xmlXPathFreeNodeSet (ns);
2808 xmlXPathReturnEmptyString (ctxt);
2812 result = _exsltDateAddDurCalc(total, total, x);
2814 exsltDateFreeDate (x);
2817 exsltDateFreeDate (total);
2818 xmlXPathFreeNodeSet (ns);
2819 xmlXPathReturnEmptyString (ctxt);
2824 ret = exsltDateFormatDuration (&(total->value.dur));
2825 exsltDateFreeDate (total);
2827 xmlXPathFreeNodeSet (ns);
2829 xmlFreeNodeList ((xmlNodePtr) user);
2832 xmlXPathReturnEmptyString (ctxt);
2834 xmlXPathReturnString (ctxt, ret);
2839 * @dateTime: a date/time string
2841 * Implements the EXSLT - Dates and Times seconds() function:
2842 * number date:seconds(string?)
2843 * The date:seconds function returns the number of seconds specified
2844 * by the argument string. If no argument is given, then the current
2845 * local date/time, as returned by exsltDateCurrent() is used as the
2846 * default argument. If the date/time string is a xs:duration, then the
2847 * years and months must be zero (or not present). Parsing a duration
2848 * converts the fields to seconds. If the date/time string is not a
2849 * duration (and not null), then the legal formats are:
2850 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2851 * - xs:date (CCYY-MM-DD)
2852 * - xs:gYearMonth (CCYY-MM)
2854 * In these cases the difference between the @dateTime and
2855 * 1970-01-01T00:00:00Z is calculated and converted to seconds.
2857 * Note that there was some confusion over whether "difference" meant
2858 * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
2859 * a negative one. After correspondence with exslt.org, it was determined
2860 * that the intent of the specification was to have it positive. The
2861 * coding was modified in July 2003 to reflect this.
2863 * Returns seconds or Nan.
2866 exsltDateSeconds (const xmlChar *dateTime)
2869 double ret = xmlXPathNAN;
2871 if (dateTime == NULL) {
2873 dt = exsltDateCurrent();
2878 dt = exsltDateParseDuration(dateTime);
2880 dt = exsltDateParse(dateTime);
2886 if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
2887 exsltDateValPtr y, dur;
2890 * compute the difference between the given (or current) date
2893 y = exsltDateCreateDate(XS_DATETIME);
2895 y->value.date.year = 1970;
2896 y->value.date.mon = 1;
2897 y->value.date.day = 1;
2898 y->value.date.tz_flag = 1;
2900 dur = _exsltDateDifference(y, dt, 1);
2902 ret = exsltDateCastDateToNumber(dur);
2903 exsltDateFreeDate(dur);
2905 exsltDateFreeDate(y);
2908 } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
2909 ret = exsltDateCastDateToNumber(dt);
2911 exsltDateFreeDate(dt);
2917 * exsltDateDifference:
2918 * @xstr: date/time string
2919 * @ystr: date/time string
2921 * Implements the date:difference (string,string) function which returns
2922 * the duration between the first date and the second date. If the first
2923 * date occurs before the second date, then the result is a positive
2924 * duration; if it occurs after the second date, the result is a
2925 * negative duration. The two dates must both be right-truncated
2926 * date/time strings in one of the formats defined in [XML Schema Part
2927 * 2: Datatypes]. The date/time with the most specific format (i.e. the
2928 * least truncation) is converted into the same format as the date with
2929 * the least specific format (i.e. the most truncation). The permitted
2930 * formats are as follows, from most specific to least specific:
2931 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2932 * - xs:date (CCYY-MM-DD)
2933 * - xs:gYearMonth (CCYY-MM)
2935 * If either of the arguments is not in one of these formats,
2936 * date:difference returns the empty string ('').
2937 * The difference between the date/times is returned as a string in the
2938 * format defined for xs:duration in [3.2.6 duration] of [XML Schema
2939 * Part 2: Datatypes].
2940 * If the date/time string with the least specific format is in either
2941 * xs:gYearMonth or xs:gYear format, then the number of days, hours,
2942 * minutes and seconds in the duration string must be equal to zero.
2943 * (The format of the string will be PnYnM.) The number of months
2944 * specified in the duration must be less than 12.
2945 * Otherwise, the number of years and months in the duration string
2946 * must be equal to zero. (The format of the string will be
2947 * PnDTnHnMnS.) The number of seconds specified in the duration string
2948 * must be less than 60; the number of minutes must be less than 60;
2949 * the number of hours must be less than 24.
2951 * Returns duration string or NULL.
2954 exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
2956 exsltDateValPtr x, y, dur;
2957 xmlChar *ret = NULL;
2959 if ((xstr == NULL) || (ystr == NULL))
2962 x = exsltDateParse(xstr);
2966 y = exsltDateParse(ystr);
2968 exsltDateFreeDate(x);
2972 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
2973 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) {
2974 exsltDateFreeDate(x);
2975 exsltDateFreeDate(y);
2979 dur = _exsltDateDifference(x, y, 0);
2981 exsltDateFreeDate(x);
2982 exsltDateFreeDate(y);
2987 ret = exsltDateFormatDuration(&(dur->value.dur));
2988 exsltDateFreeDate(dur);
2994 * exsltDateDuration:
2995 * @number: a xmlChar string
2997 * Implements the The date:duration function returns a duration string
2998 * representing the number of seconds specified by the argument string.
2999 * If no argument is given, then the result of calling date:seconds
3000 * without any arguments is used as a default argument.
3001 * The duration is returned as a string in the format defined for
3002 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
3003 * The number of years and months in the duration string must be equal
3004 * to zero. (The format of the string will be PnDTnHnMnS.) The number
3005 * of seconds specified in the duration string must be less than 60;
3006 * the number of minutes must be less than 60; the number of hours must
3008 * If the argument is Infinity, -Infinity or NaN, then date:duration
3009 * returns an empty string ('').
3011 * Returns duration string or NULL.
3014 exsltDateDuration (const xmlChar *number)
3016 exsltDateValPtr dur;
3021 secs = exsltDateSeconds(number);
3023 secs = xmlXPathCastStringToNumber(number);
3025 if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
3028 dur = exsltDateCreateDate(XS_DURATION);
3032 dur->value.dur.sec = secs;
3034 ret = exsltDateFormatDuration(&(dur->value.dur));
3035 exsltDateFreeDate(dur);
3040 /****************************************************************
3042 * Wrappers for use by the XPath engine *
3044 ****************************************************************/
3048 * exsltDateDateTimeFunction:
3049 * @ctxt: an XPath parser context
3050 * @nargs : the number of arguments
3052 * Wraps exsltDateDateTime() for use by the XPath engine.
3055 exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3060 xmlXPathSetArityError(ctxt);
3064 ret = exsltDateDateTime();
3066 xmlXPathReturnEmptyString(ctxt);
3068 xmlXPathReturnString(ctxt, ret);
3073 * exsltDateDateFunction:
3074 * @ctxt: an XPath parser context
3075 * @nargs : the number of arguments
3077 * Wraps exsltDateDate() for use by the XPath engine.
3080 exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
3082 xmlChar *ret, *dt = NULL;
3084 if ((nargs < 0) || (nargs > 1)) {
3085 xmlXPathSetArityError(ctxt);
3089 dt = xmlXPathPopString(ctxt);
3090 if (xmlXPathCheckError(ctxt)) {
3091 xmlXPathSetTypeError(ctxt);
3096 ret = exsltDateDate(dt);
3099 xsltGenericDebug(xsltGenericDebugContext,
3100 "{http://exslt.org/dates-and-times}date: "
3101 "invalid date or format %s\n", dt);
3102 xmlXPathReturnEmptyString(ctxt);
3104 xmlXPathReturnString(ctxt, ret);
3112 * exsltDateTimeFunction:
3113 * @ctxt: an XPath parser context
3114 * @nargs : the number of arguments
3116 * Wraps exsltDateTime() for use by the XPath engine.
3119 exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3121 xmlChar *ret, *dt = NULL;
3123 if ((nargs < 0) || (nargs > 1)) {
3124 xmlXPathSetArityError(ctxt);
3128 dt = xmlXPathPopString(ctxt);
3129 if (xmlXPathCheckError(ctxt)) {
3130 xmlXPathSetTypeError(ctxt);
3135 ret = exsltDateTime(dt);
3138 xsltGenericDebug(xsltGenericDebugContext,
3139 "{http://exslt.org/dates-and-times}time: "
3140 "invalid date or format %s\n", dt);
3141 xmlXPathReturnEmptyString(ctxt);
3143 xmlXPathReturnString(ctxt, ret);
3151 * exsltDateYearFunction:
3152 * @ctxt: an XPath parser context
3153 * @nargs : the number of arguments
3155 * Wraps exsltDateYear() for use by the XPath engine.
3158 exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3163 if ((nargs < 0) || (nargs > 1)) {
3164 xmlXPathSetArityError(ctxt);
3169 dt = xmlXPathPopString(ctxt);
3170 if (xmlXPathCheckError(ctxt)) {
3171 xmlXPathSetTypeError(ctxt);
3176 ret = exsltDateYear(dt);
3181 xmlXPathReturnNumber(ctxt, ret);
3185 * exsltDateLeapYearFunction:
3186 * @ctxt: an XPath parser context
3187 * @nargs : the number of arguments
3189 * Wraps exsltDateLeapYear() for use by the XPath engine.
3192 exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3195 xmlXPathObjectPtr ret;
3197 if ((nargs < 0) || (nargs > 1)) {
3198 xmlXPathSetArityError(ctxt);
3203 dt = xmlXPathPopString(ctxt);
3204 if (xmlXPathCheckError(ctxt)) {
3205 xmlXPathSetTypeError(ctxt);
3210 ret = exsltDateLeapYear(dt);
3215 valuePush(ctxt, ret);
3218 #define X_IN_Y(x, y) \
3220 exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \
3222 xmlChar *dt = NULL; \
3225 if ((nargs < 0) || (nargs > 1)) { \
3226 xmlXPathSetArityError(ctxt); \
3231 dt = xmlXPathPopString(ctxt); \
3232 if (xmlXPathCheckError(ctxt)) { \
3233 xmlXPathSetTypeError(ctxt); \
3238 ret = exsltDate##x##In##y(dt); \
3243 xmlXPathReturnNumber(ctxt, ret); \
3247 * exsltDateMonthInYearFunction:
3248 * @ctxt: an XPath parser context
3249 * @nargs : the number of arguments
3251 * Wraps exsltDateMonthInYear() for use by the XPath engine.
3256 * exsltDateMonthNameFunction:
3257 * @ctxt: an XPath parser context
3258 * @nargs : the number of arguments
3260 * Wraps exsltDateMonthName() for use by the XPath engine.
3263 exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3268 if ((nargs < 0) || (nargs > 1)) {
3269 xmlXPathSetArityError(ctxt);
3274 dt = xmlXPathPopString(ctxt);
3275 if (xmlXPathCheckError(ctxt)) {
3276 xmlXPathSetTypeError(ctxt);
3281 ret = exsltDateMonthName(dt);
3287 xmlXPathReturnEmptyString(ctxt);
3289 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3293 * exsltDateMonthAbbreviationFunction:
3294 * @ctxt: an XPath parser context
3295 * @nargs : the number of arguments
3297 * Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
3300 exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3305 if ((nargs < 0) || (nargs > 1)) {
3306 xmlXPathSetArityError(ctxt);
3311 dt = xmlXPathPopString(ctxt);
3312 if (xmlXPathCheckError(ctxt)) {
3313 xmlXPathSetTypeError(ctxt);
3318 ret = exsltDateMonthAbbreviation(dt);
3324 xmlXPathReturnEmptyString(ctxt);
3326 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3330 * exsltDateWeekInYearFunction:
3331 * @ctxt: an XPath parser context
3332 * @nargs : the number of arguments
3334 * Wraps exsltDateWeekInYear() for use by the XPath engine.
3339 * exsltDateWeekInMonthFunction:
3340 * @ctxt: an XPath parser context
3341 * @nargs : the number of arguments
3343 * Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
3348 * exsltDateDayInYearFunction:
3349 * @ctxt: an XPath parser context
3350 * @nargs : the number of arguments
3352 * Wraps exsltDateDayInYear() for use by the XPath engine.
3357 * exsltDateDayInMonthFunction:
3358 * @ctxt: an XPath parser context
3359 * @nargs : the number of arguments
3361 * Wraps exsltDateDayInMonth() for use by the XPath engine.
3366 * exsltDateDayOfWeekInMonthFunction:
3367 * @ctxt: an XPath parser context
3368 * @nargs : the number of arguments
3370 * Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
3372 X_IN_Y(DayOfWeek,Month)
3375 * exsltDateDayInWeekFunction:
3376 * @ctxt: an XPath parser context
3377 * @nargs : the number of arguments
3379 * Wraps exsltDateDayInWeek() for use by the XPath engine.
3384 * exsltDateDayNameFunction:
3385 * @ctxt: an XPath parser context
3386 * @nargs : the number of arguments
3388 * Wraps exsltDateDayName() for use by the XPath engine.
3391 exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3396 if ((nargs < 0) || (nargs > 1)) {
3397 xmlXPathSetArityError(ctxt);
3402 dt = xmlXPathPopString(ctxt);
3403 if (xmlXPathCheckError(ctxt)) {
3404 xmlXPathSetTypeError(ctxt);
3409 ret = exsltDateDayName(dt);
3415 xmlXPathReturnEmptyString(ctxt);
3417 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3421 * exsltDateMonthDayFunction:
3422 * @ctxt: an XPath parser context
3423 * @nargs : the number of arguments
3425 * Wraps exsltDateDayAbbreviation() for use by the XPath engine.
3428 exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3433 if ((nargs < 0) || (nargs > 1)) {
3434 xmlXPathSetArityError(ctxt);
3439 dt = xmlXPathPopString(ctxt);
3440 if (xmlXPathCheckError(ctxt)) {
3441 xmlXPathSetTypeError(ctxt);
3446 ret = exsltDateDayAbbreviation(dt);
3452 xmlXPathReturnEmptyString(ctxt);
3454 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3459 * exsltDateHourInDayFunction:
3460 * @ctxt: an XPath parser context
3461 * @nargs : the number of arguments
3463 * Wraps exsltDateHourInDay() for use by the XPath engine.
3468 * exsltDateMinuteInHourFunction:
3469 * @ctxt: an XPath parser context
3470 * @nargs : the number of arguments
3472 * Wraps exsltDateMinuteInHour() for use by the XPath engine.
3477 * exsltDateSecondInMinuteFunction:
3478 * @ctxt: an XPath parser context
3479 * @nargs : the number of arguments
3481 * Wraps exsltDateSecondInMinute() for use by the XPath engine.
3483 X_IN_Y(Second,Minute)
3486 * exsltDateSecondsFunction:
3487 * @ctxt: an XPath parser context
3488 * @nargs : the number of arguments
3490 * Wraps exsltDateSeconds() for use by the XPath engine.
3493 exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
3495 xmlChar *str = NULL;
3499 xmlXPathSetArityError(ctxt);
3504 str = xmlXPathPopString(ctxt);
3505 if (xmlXPathCheckError(ctxt)) {
3506 xmlXPathSetTypeError(ctxt);
3511 ret = exsltDateSeconds(str);
3515 xmlXPathReturnNumber(ctxt, ret);
3519 * exsltDateAddFunction:
3520 * @ctxt: an XPath parser context
3521 * @nargs: the number of arguments
3523 * Wraps exsltDateAdd() for use by the XPath processor.
3526 exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
3528 xmlChar *ret, *xstr, *ystr;
3531 xmlXPathSetArityError(ctxt);
3534 ystr = xmlXPathPopString(ctxt);
3535 if (xmlXPathCheckError(ctxt))
3538 xstr = xmlXPathPopString(ctxt);
3539 if (xmlXPathCheckError(ctxt)) {
3544 ret = exsltDateAdd(xstr, ystr);
3550 xmlXPathReturnEmptyString(ctxt);
3552 xmlXPathReturnString(ctxt, ret);
3556 * exsltDateAddDurationFunction:
3557 * @ctxt: an XPath parser context
3558 * @nargs: the number of arguments
3560 * Wraps exsltDateAddDuration() for use by the XPath processor.
3563 exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3565 xmlChar *ret, *xstr, *ystr;
3568 xmlXPathSetArityError(ctxt);
3571 ystr = xmlXPathPopString(ctxt);
3572 if (xmlXPathCheckError(ctxt))
3575 xstr = xmlXPathPopString(ctxt);
3576 if (xmlXPathCheckError(ctxt)) {
3581 ret = exsltDateAddDuration(xstr, ystr);
3587 xmlXPathReturnEmptyString(ctxt);
3589 xmlXPathReturnString(ctxt, ret);
3593 * exsltDateDifferenceFunction:
3594 * @ctxt: an XPath parser context
3595 * @nargs: the number of arguments
3597 * Wraps exsltDateDifference() for use by the XPath processor.
3600 exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
3602 xmlChar *ret, *xstr, *ystr;
3605 xmlXPathSetArityError(ctxt);
3608 ystr = xmlXPathPopString(ctxt);
3609 if (xmlXPathCheckError(ctxt))
3612 xstr = xmlXPathPopString(ctxt);
3613 if (xmlXPathCheckError(ctxt)) {
3618 ret = exsltDateDifference(xstr, ystr);
3624 xmlXPathReturnEmptyString(ctxt);
3626 xmlXPathReturnString(ctxt, ret);
3630 * exsltDateDurationFunction:
3631 * @ctxt: an XPath parser context
3632 * @nargs : the number of arguments
3634 * Wraps exsltDateDuration() for use by the XPath engine
3637 exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3640 xmlChar *number = NULL;
3642 if ((nargs < 0) || (nargs > 1)) {
3643 xmlXPathSetArityError(ctxt);
3648 number = xmlXPathPopString(ctxt);
3649 if (xmlXPathCheckError(ctxt)) {
3650 xmlXPathSetTypeError(ctxt);
3655 ret = exsltDateDuration(number);
3661 xmlXPathReturnEmptyString(ctxt);
3663 xmlXPathReturnString(ctxt, ret);
3667 * exsltDateRegister:
3669 * Registers the EXSLT - Dates and Times module
3672 exsltDateRegister (void)
3674 xsltRegisterExtModuleFunction ((const xmlChar *) "add",
3675 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3676 exsltDateAddFunction);
3677 xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
3678 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3679 exsltDateAddDurationFunction);
3680 xsltRegisterExtModuleFunction ((const xmlChar *) "date",
3681 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3682 exsltDateDateFunction);
3684 xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
3685 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3686 exsltDateDateTimeFunction);
3688 xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
3689 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3690 exsltDateDayAbbreviationFunction);
3691 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
3692 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3693 exsltDateDayInMonthFunction);
3694 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
3695 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3696 exsltDateDayInWeekFunction);
3697 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
3698 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3699 exsltDateDayInYearFunction);
3700 xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
3701 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3702 exsltDateDayNameFunction);
3703 xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
3704 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3705 exsltDateDayOfWeekInMonthFunction);
3706 xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
3707 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3708 exsltDateDifferenceFunction);
3709 xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
3710 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3711 exsltDateDurationFunction);
3712 xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
3713 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3714 exsltDateHourInDayFunction);
3715 xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
3716 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3717 exsltDateLeapYearFunction);
3718 xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
3719 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3720 exsltDateMinuteInHourFunction);
3721 xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
3722 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3723 exsltDateMonthAbbreviationFunction);
3724 xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
3725 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3726 exsltDateMonthInYearFunction);
3727 xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
3728 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3729 exsltDateMonthNameFunction);
3730 xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
3731 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3732 exsltDateSecondInMinuteFunction);
3733 xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
3734 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3735 exsltDateSecondsFunction);
3736 xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
3737 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3738 exsltDateSumFunction);
3739 xsltRegisterExtModuleFunction ((const xmlChar *) "time",
3740 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3741 exsltDateTimeFunction);
3742 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
3743 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3744 exsltDateWeekInMonthFunction);
3745 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
3746 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3747 exsltDateWeekInYearFunction);
3748 xsltRegisterExtModuleFunction ((const xmlChar *) "year",
3749 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3750 exsltDateYearFunction);