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 signed int tzo :12; /* -1440 <= tzo <= 1440
93 currently only -840 to +840 are needed */
97 typedef struct _exsltDateValDuration exsltDateValDuration;
98 typedef exsltDateValDuration *exsltDateValDurationPtr;
99 struct _exsltDateValDuration {
100 long mon; /* mon stores years also */
102 double sec; /* sec stores min and hour also */
105 typedef struct _exsltDateVal exsltDateVal;
106 typedef exsltDateVal *exsltDateValPtr;
107 struct _exsltDateVal {
110 exsltDateValDate date;
111 exsltDateValDuration dur;
115 /****************************************************************
117 * Compat./Port. macros *
119 ****************************************************************/
121 #if defined(HAVE_TIME_H) \
122 && (defined(HAVE_LOCALTIME) || defined(HAVE_LOCALTIME_R)) \
123 && (defined(HAVE_GMTIME) || defined(HAVE_GMTIME_R)) \
124 && defined(HAVE_TIME)
128 /****************************************************************
130 * Convenience macros and functions *
132 ****************************************************************/
134 #define IS_TZO_CHAR(c) \
135 ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
137 #define VALID_ALWAYS(num) (num >= 0)
138 #define VALID_YEAR(yr) (yr != 0)
139 #define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
140 /* VALID_DAY should only be used when month is unknown */
141 #define VALID_DAY(day) ((day >= 1) && (day <= 31))
142 #define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
143 #define VALID_MIN(min) ((min >= 0) && (min <= 59))
144 #define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
145 #define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
147 (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
149 static const unsigned long daysInMonth[12] =
150 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
151 static const unsigned long daysInMonthLeap[12] =
152 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
154 #define MAX_DAYINMONTH(yr,mon) \
155 (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
157 #define VALID_MDAY(dt) \
158 (IS_LEAP(dt->year) ? \
159 (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
160 (dt->day <= daysInMonth[dt->mon - 1]))
162 #define VALID_DATE(dt) \
163 (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
166 hour and min structure vals are unsigned, so normal macros give
167 warnings on some compilers.
169 #define VALID_TIME(dt) \
170 ((dt->hour <=23 ) && (dt->min <= 59) && \
171 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
173 #define VALID_DATETIME(dt) \
174 (VALID_DATE(dt) && VALID_TIME(dt))
176 #define SECS_PER_MIN (60)
177 #define SECS_PER_HOUR (60 * SECS_PER_MIN)
178 #define SECS_PER_DAY (24 * SECS_PER_HOUR)
180 static const unsigned long dayInYearByMonth[12] =
181 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
182 static const unsigned long dayInLeapYearByMonth[12] =
183 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
185 #define DAY_IN_YEAR(day, month, year) \
187 dayInLeapYearByMonth[month - 1] : \
188 dayInYearByMonth[month - 1]) + day)
191 * _exsltDateParseGYear:
192 * @dt: pointer to a date structure
193 * @str: pointer to the string to analyze
195 * Parses a xs:gYear without time zone and fills in the appropriate
196 * field of the @dt structure. @str is updated to point just after the
197 * xs:gYear. It is supposed that @dt->year is big enough to contain
200 * Returns 0 or the error code
203 _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
205 const xmlChar *cur = *str, *firstChar;
206 int isneg = 0, digcnt = 0;
208 if (((*cur < '0') || (*cur > '9')) &&
209 (*cur != '-') && (*cur != '+'))
219 while ((*cur >= '0') && (*cur <= '9')) {
220 dt->year = dt->year * 10 + (*cur - '0');
225 /* year must be at least 4 digits (CCYY); over 4
226 * digits cannot have a leading zero. */
227 if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
231 dt->year = - dt->year;
233 if (!VALID_YEAR(dt->year))
238 #ifdef DEBUG_EXSLT_DATE
239 xsltGenericDebug(xsltGenericDebugContext,
240 "Parsed year %04i\n", dt->year);
248 * @yr: the year to format
249 * @cur: a pointer to an allocated buffer
251 * Formats @yr in xsl:gYear format. Result is appended to @cur and
252 * @cur is updated to point after the xsl:gYear.
254 #define FORMAT_GYEAR(yr, cur) \
260 long year = (yr < 0) ? - yr : yr; \
261 xmlChar tmp_buf[100], *tmp = tmp_buf; \
262 /* result is in reverse-order */ \
264 *tmp = '0' + (xmlChar)(year % 10); \
268 /* virtually adds leading zeros */ \
269 while ((tmp - tmp_buf) < 4) \
271 /* restore the correct order */ \
272 while (tmp > tmp_buf) { \
281 * @num: the integer to fill in
282 * @cur: an #xmlChar *
283 * @func: validation function for the number
284 * @invalid: an integer
286 * Parses a 2-digits integer and updates @num with the value. @cur is
287 * updated to point just after the integer.
288 * In case of error, @invalid is set to %TRUE, values of @num and
289 * @cur are undefined.
291 #define PARSE_2_DIGITS(num, cur, func, invalid) \
292 if ((cur[0] < '0') || (cur[0] > '9') || \
293 (cur[1] < '0') || (cur[1] > '9')) \
297 val = (cur[0] - '0') * 10 + (cur[1] - '0'); \
307 * @num: the integer to format
308 * @cur: a pointer to an allocated buffer
310 * Formats a 2-digits integer. Result is appended to @cur and
311 * @cur is updated to point after the integer.
313 #define FORMAT_2_DIGITS(num, cur) \
314 *cur = '0' + ((num / 10) % 10); \
316 *cur = '0' + (num % 10); \
321 * @num: the double to fill in
322 * @cur: an #xmlChar *
323 * @invalid: an integer
325 * Parses a float and updates @num with the value. @cur is
326 * updated to point just after the float. The float must have a
327 * 2-digits integer part and may or may not have a decimal part.
328 * In case of error, @invalid is set to %TRUE, values of @num and
329 * @cur are undefined.
331 #define PARSE_FLOAT(num, cur, invalid) \
332 PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid); \
333 if (!invalid && (*cur == '.')) { \
336 if ((*cur < '0') || (*cur > '9')) \
338 while ((*cur >= '0') && (*cur <= '9')) { \
340 num += (*cur - '0') * mult; \
347 * @num: the double to format
348 * @cur: a pointer to an allocated buffer
349 * @pad: a flag for padding to 2 integer digits
351 * Formats a float. Result is appended to @cur and @cur is updated to
352 * point after the integer. If the @pad flag is non-zero, then the
353 * float representation has a minimum 2-digits integer part. The
354 * fractional part is formatted if @num has a fractional value.
356 #define FORMAT_FLOAT(num, cur, pad) \
358 xmlChar *sav, *str; \
359 if ((pad) && (num < 10.0)) \
361 str = xmlXPathCastNumberToString(num); \
369 * _exsltDateParseGMonth:
370 * @dt: pointer to a date structure
371 * @str: pointer to the string to analyze
373 * Parses a xs:gMonth without time zone and fills in the appropriate
374 * field of the @dt structure. @str is updated to point just after the
377 * Returns 0 or the error code
380 _exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
382 const xmlChar *cur = *str;
385 PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret);
391 #ifdef DEBUG_EXSLT_DATE
392 xsltGenericDebug(xsltGenericDebugContext,
393 "Parsed month %02i\n", dt->mon);
401 * @mon: the month to format
402 * @cur: a pointer to an allocated buffer
404 * Formats @mon in xsl:gMonth format. Result is appended to @cur and
405 * @cur is updated to point after the xsl:gMonth.
407 #define FORMAT_GMONTH(mon, cur) \
408 FORMAT_2_DIGITS(mon, cur)
411 * _exsltDateParseGDay:
412 * @dt: pointer to a date structure
413 * @str: pointer to the string to analyze
415 * Parses a xs:gDay without time zone and fills in the appropriate
416 * field of the @dt structure. @str is updated to point just after the
419 * Returns 0 or the error code
422 _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
424 const xmlChar *cur = *str;
427 PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret);
433 #ifdef DEBUG_EXSLT_DATE
434 xsltGenericDebug(xsltGenericDebugContext,
435 "Parsed day %02i\n", dt->day);
443 * @dt: the #exsltDateValDate to format
444 * @cur: a pointer to an allocated buffer
446 * Formats @dt in xsl:gDay format. Result is appended to @cur and
447 * @cur is updated to point after the xsl:gDay.
449 #define FORMAT_GDAY(dt, cur) \
450 FORMAT_2_DIGITS(dt->day, cur)
454 * @dt: the #exsltDateValDate to format
455 * @cur: a pointer to an allocated buffer
457 * Formats @dt in xsl:date format. Result is appended to @cur and
458 * @cur is updated to point after the xsl:date.
460 #define FORMAT_DATE(dt, cur) \
461 FORMAT_GYEAR(dt->year, cur); \
464 FORMAT_GMONTH(dt->mon, cur); \
467 FORMAT_GDAY(dt, cur);
470 * _exsltDateParseTime:
471 * @dt: pointer to a date structure
472 * @str: pointer to the string to analyze
474 * Parses a xs:time without time zone and fills in the appropriate
475 * fields of the @dt structure. @str is updated to point just after the
477 * In case of error, values of @dt fields are undefined.
479 * Returns 0 or the error code
482 _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
484 const xmlChar *cur = *str;
485 unsigned int hour = 0; /* use temp var in case str is not xs:time */
488 PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret);
496 /* the ':' insures this string is xs:time */
499 PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret);
507 PARSE_FLOAT(dt->sec, cur, ret);
516 #ifdef DEBUG_EXSLT_DATE
517 xsltGenericDebug(xsltGenericDebugContext,
518 "Parsed time %02i:%02i:%02.f\n",
519 dt->hour, dt->min, dt->sec);
527 * @dt: the #exsltDateValDate to format
528 * @cur: a pointer to an allocated buffer
530 * Formats @dt in xsl:time format. Result is appended to @cur and
531 * @cur is updated to point after the xsl:time.
533 #define FORMAT_TIME(dt, cur) \
534 FORMAT_2_DIGITS(dt->hour, cur); \
537 FORMAT_2_DIGITS(dt->min, cur); \
540 FORMAT_FLOAT(dt->sec, cur, 1);
543 * _exsltDateParseTimeZone:
544 * @dt: pointer to a date structure
545 * @str: pointer to the string to analyze
547 * Parses a time zone without time zone and fills in the appropriate
548 * field of the @dt structure. @str is updated to point just after the
551 * Returns 0 or the error code
554 _exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str)
576 int isneg = 0, tmp = 0;
577 isneg = (*cur == '-');
581 PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret);
591 PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret);
599 if (!VALID_TZO(dt->tzo))
610 #ifdef DEBUG_EXSLT_DATE
611 xsltGenericDebug(xsltGenericDebugContext,
612 "Parsed time zone offset (%s) %i\n",
613 dt->tz_flag ? "explicit" : "implicit", dt->tzo);
621 * @tzo: the timezone offset to format
622 * @cur: a pointer to an allocated buffer
624 * Formats @tzo timezone. Result is appended to @cur and
625 * @cur is updated to point after the timezone.
627 #define FORMAT_TZ(tzo, cur) \
632 int aTzo = (tzo < 0) ? - tzo : tzo; \
633 int tzHh = aTzo / 60, tzMm = aTzo % 60; \
634 *cur = (tzo < 0) ? '-' : '+' ; \
636 FORMAT_2_DIGITS(tzHh, cur); \
639 FORMAT_2_DIGITS(tzMm, cur); \
642 /****************************************************************
644 * XML Schema Dates/Times Datatypes Handling *
646 ****************************************************************/
649 * exsltDateCreateDate:
650 * @type: type to create
652 * Creates a new #exsltDateVal, uninitialized.
654 * Returns the #exsltDateValPtr
656 static exsltDateValPtr
657 exsltDateCreateDate (exsltDateType type)
661 ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal));
663 xsltGenericError(xsltGenericErrorContext,
664 "exsltDateCreateDate: out of memory\n");
667 memset (ret, 0, sizeof(exsltDateVal));
669 if (type != EXSLT_UNKNOWN)
677 * @date: an #exsltDateValPtr
682 exsltDateFreeDate (exsltDateValPtr date) {
691 * @num: the integer to fill in
692 * @cur: an #xmlChar *
693 * @num_type: an integer flag
695 * Parses a digits integer and updates @num with the value. @cur is
696 * updated to point just after the integer.
697 * In case of error, @num_type is set to -1, values of @num and
698 * @cur are undefined.
700 #define PARSE_DIGITS(num, cur, num_type) \
701 if ((*cur < '0') || (*cur > '9')) \
704 while ((*cur >= '0') && (*cur <= '9')) { \
705 num = num * 10 + (*cur - '0'); \
711 * @num: the double to fill in
712 * @cur: an #xmlChar *
713 * @num_type: an integer flag
715 * Parses a float or integer and updates @num with the value. @cur is
716 * updated to point just after the number. If the number is a float,
717 * then it must have an integer part and a decimal part; @num_type will
718 * be set to 1. If there is no decimal part, @num_type is set to zero.
719 * In case of error, @num_type is set to -1, values of @num and
720 * @cur are undefined.
722 #define PARSE_NUM(num, cur, num_type) \
724 PARSE_DIGITS(num, cur, num_type); \
725 if (!num_type && (*cur == '.')) { \
728 if ((*cur < '0') || (*cur > '9')) \
732 while ((*cur >= '0') && (*cur <= '9')) { \
734 num += (*cur - '0') * mult; \
743 * Returns the current date and time.
745 static exsltDateValPtr
746 exsltDateCurrent (void)
748 struct tm *localTm, *gmTm;
758 ret = exsltDateCreateDate(XS_DATETIME);
762 /* get current time */
765 localtime_r(&secs, &localTmS);
768 localTm = localtime(&secs);
771 /* get real year, not years since 1900 */
772 ret->value.date.year = localTm->tm_year + 1900;
774 ret->value.date.mon = localTm->tm_mon + 1;
775 ret->value.date.day = localTm->tm_mday;
776 ret->value.date.hour = localTm->tm_hour;
777 ret->value.date.min = localTm->tm_min;
779 /* floating point seconds */
780 ret->value.date.sec = (double) localTm->tm_sec;
782 /* determine the time zone offset from local to gm time */
784 gmtime_r(&secs, &gmTmS);
787 gmTm = gmtime(&secs);
789 ret->value.date.tz_flag = 0;
791 ret->value.date.tzo = (((ret->value.date.day * 1440) +
792 (ret->value.date.hour * 60) +
793 ret->value.date.min) -
794 ((gmTm->tm_mday * 1440) + (gmTm->tm_hour * 60) +
797 gsecs = mktime(gmTm);
798 ret->value.date.tzo = (secs - gsecs) / 60;
806 * @dateTime: string to analyze
808 * Parses a date/time string
810 * Returns a newly built #exsltDateValPtr of NULL in case of error
812 static exsltDateValPtr
813 exsltDateParse (const xmlChar *dateTime)
817 const xmlChar *cur = dateTime;
819 #define RETURN_TYPE_IF_VALID(t) \
820 if (IS_TZO_CHAR(*cur)) { \
821 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \
830 if (dateTime == NULL)
833 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
836 dt = exsltDateCreateDate(EXSLT_UNKNOWN);
840 if ((cur[0] == '-') && (cur[1] == '-')) {
842 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
847 /* is it an xs:gDay? */
850 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
854 RETURN_TYPE_IF_VALID(XS_GDAY);
860 * it should be an xs:gMonthDay or xs:gMonth
862 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
870 /* is it an xs:gMonth? */
873 RETURN_TYPE_IF_VALID(XS_GMONTH);
877 /* it should be an xs:gMonthDay */
878 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
882 RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
888 * It's a right-truncated date or an xs:time.
889 * Try to parse an xs:time then fallback on right-truncated dates.
891 if ((*cur >= '0') && (*cur <= '9')) {
892 ret = _exsltDateParseTime(&(dt->value.date), &cur);
894 /* it's an xs:time */
895 RETURN_TYPE_IF_VALID(XS_TIME);
899 /* fallback on date parsing */
902 ret = _exsltDateParseGYear(&(dt->value.date), &cur);
906 /* is it an xs:gYear? */
907 RETURN_TYPE_IF_VALID(XS_GYEAR);
913 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
917 /* is it an xs:gYearMonth? */
918 RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
924 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
925 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
928 /* is it an xs:date? */
929 RETURN_TYPE_IF_VALID(XS_DATE);
935 /* it should be an xs:dateTime */
936 ret = _exsltDateParseTime(&(dt->value.date), &cur);
940 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
941 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
944 dt->type = XS_DATETIME;
950 exsltDateFreeDate(dt);
955 * exsltDateParseDuration:
956 * @duration: string to analyze
958 * Parses a duration string
960 * Returns a newly built #exsltDateValPtr of NULL in case of error
962 static exsltDateValPtr
963 exsltDateParseDuration (const xmlChar *duration)
965 const xmlChar *cur = duration;
968 unsigned int seq = 0;
970 if (duration == NULL)
978 /* duration must start with 'P' (after sign) */
982 dur = exsltDateCreateDate(XS_DURATION);
988 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
989 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
990 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
992 /* input string should be empty or invalid date/time item */
993 if (seq >= sizeof(desig))
996 /* T designator must be present for time items */
1003 } else if (seq == 3)
1006 /* parse the number portion of the item */
1007 PARSE_NUM(num, cur, num_type);
1009 if ((num_type == -1) || (*cur == 0))
1012 /* update duration based on item type */
1013 while (seq < sizeof(desig)) {
1014 if (*cur == desig[seq]) {
1016 /* verify numeric type; only seconds can be float */
1017 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
1022 dur->value.dur.mon = (long)num * 12;
1025 dur->value.dur.mon += (long)num;
1028 /* convert to seconds using multiplier */
1029 dur->value.dur.sec += num * multi[seq];
1034 break; /* exit loop */
1036 /* no date designators found? */
1044 dur->value.dur.mon = -dur->value.dur.mon;
1045 dur->value.dur.day = -dur->value.dur.day;
1046 dur->value.dur.sec = -dur->value.dur.sec;
1049 #ifdef DEBUG_EXSLT_DATE
1050 xsltGenericDebug(xsltGenericDebugContext,
1051 "Parsed duration %f\n", dur->value.dur.sec);
1058 exsltDateFreeDate(dur);
1064 * @num: number to format
1065 * @cur: current location to convert number
1067 * @item: char designator
1070 #define FORMAT_ITEM(num, cur, limit, item) \
1072 long comp = (long)num / limit; \
1074 FORMAT_FLOAT((double)comp, cur, 0); \
1076 num -= (double)(comp * limit); \
1081 * exsltDateFormatDuration:
1082 * @dt: an #exsltDateValDurationPtr
1084 * Formats @dt in xs:duration format.
1086 * Returns a newly allocated string, or NULL in case of error
1089 exsltDateFormatDuration (const exsltDateValDurationPtr dt)
1091 xmlChar buf[100], *cur = buf;
1093 double years, months;
1098 /* quick and dirty check */
1099 if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
1100 return xmlStrdup((xmlChar*)"P0D");
1103 days = (double)dt->day;
1104 years = (double)(dt->mon / 12);
1105 months = (double)(dt->mon % 12);
1130 FORMAT_ITEM(years, cur, 1, 'Y');
1133 if (months != 0.0) {
1134 FORMAT_ITEM(months, cur, 1, 'M');
1137 if (secs >= SECS_PER_DAY) {
1138 double tmp = floor(secs / SECS_PER_DAY);
1140 secs -= (tmp * SECS_PER_DAY);
1143 FORMAT_ITEM(days, cur, 1, 'D');
1147 FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
1148 FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
1150 FORMAT_FLOAT(secs, cur, 0);
1156 return xmlStrdup(buf);
1160 * exsltDateFormatDateTime:
1161 * @dt: an #exsltDateValDatePtr
1163 * Formats @dt in xs:dateTime format.
1165 * Returns a newly allocated string, or NULL in case of error
1168 exsltDateFormatDateTime (const exsltDateValDatePtr dt)
1170 xmlChar buf[100], *cur = buf;
1172 if ((dt == NULL) || !VALID_DATETIME(dt))
1175 FORMAT_DATE(dt, cur);
1178 FORMAT_TIME(dt, cur);
1179 FORMAT_TZ(dt->tzo, cur);
1182 return xmlStrdup(buf);
1186 * exsltDateFormatDate:
1187 * @dt: an #exsltDateValDatePtr
1189 * Formats @dt in xs:date format.
1191 * Returns a newly allocated string, or NULL in case of error
1194 exsltDateFormatDate (const exsltDateValDatePtr dt)
1196 xmlChar buf[100], *cur = buf;
1198 if ((dt == NULL) || !VALID_DATETIME(dt))
1201 FORMAT_DATE(dt, cur);
1202 if (dt->tz_flag || (dt->tzo != 0)) {
1203 FORMAT_TZ(dt->tzo, cur);
1207 return xmlStrdup(buf);
1211 * exsltDateFormatTime:
1212 * @dt: an #exsltDateValDatePtr
1214 * Formats @dt in xs:time format.
1216 * Returns a newly allocated string, or NULL in case of error
1219 exsltDateFormatTime (const exsltDateValDatePtr dt)
1221 xmlChar buf[100], *cur = buf;
1223 if ((dt == NULL) || !VALID_TIME(dt))
1226 FORMAT_TIME(dt, cur);
1227 if (dt->tz_flag || (dt->tzo != 0)) {
1228 FORMAT_TZ(dt->tzo, cur);
1232 return xmlStrdup(buf);
1237 * @dt: an #exsltDateValPtr
1239 * Formats @dt in the proper format.
1240 * Note: xs:gmonth and xs:gday are not formatted as there are no
1241 * routines that output them.
1243 * Returns a newly allocated string, or NULL in case of error
1246 exsltDateFormat (const exsltDateValPtr dt)
1254 return exsltDateFormatDuration(&(dt->value.dur));
1256 return exsltDateFormatDateTime(&(dt->value.date));
1258 return exsltDateFormatDate(&(dt->value.date));
1260 return exsltDateFormatTime(&(dt->value.date));
1265 if (dt->type & XS_GYEAR) {
1266 xmlChar buf[20], *cur = buf;
1268 FORMAT_GYEAR(dt->value.date.year, cur);
1269 if (dt->type == XS_GYEARMONTH) {
1272 FORMAT_GMONTH(dt->value.date.mon, cur);
1275 if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
1276 FORMAT_TZ(dt->value.date.tzo, cur);
1279 return xmlStrdup(buf);
1286 * _exsltDateCastYMToDays:
1287 * @dt: an #exsltDateValPtr
1289 * Convert mon and year of @dt to total number of days. Take the
1290 * number of years since (or before) 1 AD and add the number of leap
1291 * years. This is a function because negative
1292 * years must be handled a little differently and there is no zero year.
1294 * Returns number of days.
1297 _exsltDateCastYMToDays (const exsltDateValPtr dt)
1301 if (dt->value.date.year < 0)
1302 ret = (dt->value.date.year * 365) +
1303 (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
1304 ((dt->value.date.year+1)/400)) +
1305 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1307 ret = ((dt->value.date.year-1) * 365) +
1308 (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
1309 ((dt->value.date.year-1)/400)) +
1310 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1317 * @dt: an #exsltDateValPtr
1319 * Calculates the number of seconds in the time portion of @dt.
1323 #define TIME_TO_NUMBER(dt) \
1324 ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
1325 (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
1328 * exsltDateCastDateToNumber:
1329 * @dt: an #exsltDateValPtr
1331 * Calculates the number of seconds from year zero.
1333 * Returns seconds from zero year.
1336 exsltDateCastDateToNumber (const exsltDateValPtr dt)
1343 if ((dt->type & XS_GYEAR) == XS_GYEAR) {
1344 ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
1348 if (dt->type == XS_DURATION) {
1349 ret += (double)dt->value.dur.day * SECS_PER_DAY;
1350 ret += dt->value.dur.sec;
1352 ret += (double)dt->value.date.day * SECS_PER_DAY;
1354 ret += TIME_TO_NUMBER(dt);
1362 * _exsltDateTruncateDate:
1363 * @dt: an #exsltDateValPtr
1364 * @type: dateTime type to set to
1366 * Set @dt to truncated @type.
1368 * Returns 0 success, non-zero otherwise.
1371 _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
1376 if ((type & XS_TIME) != XS_TIME) {
1377 dt->value.date.hour = 0;
1378 dt->value.date.min = 0;
1379 dt->value.date.sec = 0.0;
1382 if ((type & XS_GDAY) != XS_GDAY)
1383 dt->value.date.day = 0;
1385 if ((type & XS_GMONTH) != XS_GMONTH)
1386 dt->value.date.mon = 0;
1388 if ((type & XS_GYEAR) != XS_GYEAR)
1389 dt->value.date.year = 0;
1398 * @yday: year day (1-366)
1401 * Determine the day-in-week from @yday and @yr. 0001-01-01 was
1402 * a Monday so all other days are calculated from there. Take the
1403 * number of years since (or before) add the number of leap years and
1404 * the day-in-year and mod by 7. This is a function because negative
1405 * years must be handled a little differently and there is no zero year.
1407 * Returns day in week (Sunday = 0).
1410 _exsltDateDayInWeek(long yday, long yr)
1415 ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
1419 ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
1425 * macros for adding date/times and durations
1427 #define FQUOTIENT(a,b) ((floor(((double)a/(double)b))))
1428 #define MODULO(a,b) ((a - FQUOTIENT(a,b) * b))
1429 #define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
1430 #define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
1434 * @dt: an #exsltDateValPtr
1435 * @dur: an #exsltDateValPtr of type #XS_DURATION
1437 * Compute a new date/time from @dt and @dur. This function assumes @dt
1438 * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
1440 * Returns date/time pointer or NULL.
1442 static exsltDateValPtr
1443 _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
1445 exsltDateValPtr ret;
1446 long carry, tempdays, temp;
1447 exsltDateValDatePtr r, d;
1448 exsltDateValDurationPtr u;
1450 if ((dt == NULL) || (dur == NULL))
1453 ret = exsltDateCreateDate(dt->type);
1457 r = &(ret->value.date);
1458 d = &(dt->value.date);
1459 u = &(dur->value.dur);
1465 /* normalize for time zone offset */
1466 u->sec -= (d->tzo * 60); /* changed from + to - (bug 153000) */
1474 carry = d->mon + u->mon;
1475 r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
1476 carry = (long)FQUOTIENT_RANGE(carry, 1, 13);
1478 /* year (may be modified later) */
1479 r->year = d->year + carry;
1489 r->tz_flag = d->tz_flag;
1492 r->sec = d->sec + u->sec;
1493 carry = (long)FQUOTIENT((long)r->sec, 60);
1494 if (r->sec != 0.0) {
1495 r->sec = MODULO(r->sec, 60.0);
1500 r->min = (unsigned int)MODULO(carry, 60);
1501 carry = (long)FQUOTIENT(carry, 60);
1505 r->hour = (unsigned int)MODULO(carry, 24);
1506 carry = (long)FQUOTIENT(carry, 24);
1510 * Note we use tempdays because the temporary values may need more
1513 if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
1514 (d->day > MAX_DAYINMONTH(r->year, r->mon)))
1515 tempdays = MAX_DAYINMONTH(r->year, r->mon);
1516 else if (d->day < 1)
1521 tempdays += u->day + carry;
1525 long tmon = (long)MODULO_RANGE((int)r->mon-1, 1, 13);
1526 long tyr = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13);
1530 * Coverity detected an overrun in daysInMonth
1531 * of size 12 at position 12 with index variable "((r)->mon - 1)"
1537 tempdays += MAX_DAYINMONTH(tyr, tmon);
1539 } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
1540 tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
1545 temp = r->mon + carry;
1546 r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
1547 r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
1559 * adjust the date/time type to the date values
1561 if (ret->type != XS_DATETIME) {
1562 if ((r->hour) || (r->min) || (r->sec))
1563 ret->type = XS_DATETIME;
1564 else if (ret->type != XS_DATE) {
1565 if ((r->mon != 1) && (r->day != 1))
1566 ret->type = XS_DATE;
1567 else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
1568 ret->type = XS_GYEARMONTH;
1576 * exsltDateNormalize:
1577 * @dt: an #exsltDateValPtr
1579 * Normalize @dt to GMT time.
1583 exsltDateNormalize (exsltDateValPtr dt)
1585 exsltDateValPtr dur, tmp;
1590 if (((dt->type & XS_TIME) != XS_TIME) || (dt->value.date.tzo == 0))
1593 dur = exsltDateCreateDate(XS_DURATION);
1597 tmp = _exsltDateAdd(dt, dur);
1601 memcpy(dt, tmp, sizeof(exsltDateVal));
1603 exsltDateFreeDate(tmp);
1604 exsltDateFreeDate(dur);
1606 dt->value.date.tzo = 0;
1610 * _exsltDateDifference:
1611 * @x: an #exsltDateValPtr
1612 * @y: an #exsltDateValPtr
1613 * @flag: force difference in days
1615 * Calculate the difference between @x and @y as a duration
1616 * (i.e. y - x). If the @flag is set then even if the least specific
1617 * format of @x or @y is xs:gYear or xs:gYearMonth.
1619 * Returns date/time pointer or NULL.
1621 static exsltDateValPtr
1622 _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
1624 exsltDateValPtr ret;
1626 if ((x == NULL) || (y == NULL))
1629 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
1630 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
1633 exsltDateNormalize(x);
1634 exsltDateNormalize(y);
1637 * the operand with the most specific format must be converted to
1638 * the same type as the operand with the least specific format.
1640 if (x->type != y->type) {
1641 if (x->type < y->type) {
1642 _exsltDateTruncateDate(y, x->type);
1644 _exsltDateTruncateDate(x, y->type);
1648 ret = exsltDateCreateDate(XS_DURATION);
1652 if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
1653 /* compute the difference in months */
1654 ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
1655 ((x->value.date.year * 12) + x->value.date.mon);
1656 /* The above will give a wrong result if x and y are on different sides
1657 of the September 1752. Resolution is welcome :-) */
1659 ret->value.dur.day = _exsltDateCastYMToDays(y) -
1660 _exsltDateCastYMToDays(x);
1661 ret->value.dur.day += y->value.date.day - x->value.date.day;
1662 ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
1663 if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
1664 ret->value.dur.day -= 1;
1665 ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
1666 } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
1667 ret->value.dur.day += 1;
1668 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1676 * _exsltDateAddDurCalc
1677 * @ret: an exsltDateValPtr for the return value:
1678 * @x: an exsltDateValPtr for the first operand
1679 * @y: an exsltDateValPtr for the second operand
1681 * Add two durations, catering for possible negative values.
1682 * The sum is placed in @ret.
1684 * Returns 1 for success, 0 if error detected.
1687 _exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
1693 ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
1696 ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
1697 carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
1698 if (ret->value.dur.sec != 0.0) {
1699 ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
1701 * Our function MODULO always gives us a positive value, so
1702 * if we end up with a "-ve" carry we need to adjust it
1703 * appropriately (bug 154021)
1705 if ((carry < 0) && (ret->value.dur.sec != 0)) {
1706 /* change seconds to equiv negative modulus */
1707 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1713 ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
1716 * are the results indeterminate? i.e. how do you subtract days from
1719 if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
1720 (ret->value.dur.mon < 0)) ||
1721 (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
1722 (ret->value.dur.mon > 0))) {
1729 * _exsltDateAddDuration:
1730 * @x: an #exsltDateValPtr of type #XS_DURATION
1731 * @y: an #exsltDateValPtr of type #XS_DURATION
1733 * Compute a new duration from @x and @y.
1735 * Returns date/time pointer or NULL.
1737 static exsltDateValPtr
1738 _exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
1740 exsltDateValPtr ret;
1742 if ((x == NULL) || (y == NULL))
1745 ret = exsltDateCreateDate(XS_DURATION);
1749 if (_exsltDateAddDurCalc(ret, x, y))
1752 exsltDateFreeDate(ret);
1756 /****************************************************************
1758 * EXSLT - Dates and Times functions *
1760 ****************************************************************/
1763 * exsltDateDateTime:
1765 * Implements the EXSLT - Dates and Times date-time() function:
1766 * string date:date-time()
1768 * Returns the current date and time as a date/time string.
1771 exsltDateDateTime (void)
1773 xmlChar *ret = NULL;
1775 exsltDateValPtr cur;
1777 cur = exsltDateCurrent();
1779 ret = exsltDateFormatDateTime(&(cur->value.date));
1780 exsltDateFreeDate(cur);
1789 * @dateTime: a date/time string
1791 * Implements the EXSLT - Dates and Times date() function:
1792 * string date:date (string?)
1794 * Returns the date specified in the date/time string given as the
1795 * argument. If no argument is given, then the current local
1796 * date/time, as returned by date:date-time is used as a default
1798 * The date/time string specified as an argument must be a string in
1799 * the format defined as the lexical representation of either
1800 * xs:dateTime or xs:date. If the argument is not in either of these
1801 * formats, returns NULL.
1804 exsltDateDate (const xmlChar *dateTime)
1806 exsltDateValPtr dt = NULL;
1807 xmlChar *ret = NULL;
1809 if (dateTime == NULL) {
1811 dt = exsltDateCurrent();
1816 dt = exsltDateParse(dateTime);
1819 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1820 exsltDateFreeDate(dt);
1825 ret = exsltDateFormatDate(&(dt->value.date));
1826 exsltDateFreeDate(dt);
1833 * @dateTime: a date/time string
1835 * Implements the EXSLT - Dates and Times time() function:
1836 * string date:time (string?)
1838 * Returns the time specified in the date/time string given as the
1839 * argument. If no argument is given, then the current local
1840 * date/time, as returned by date:date-time is used as a default
1842 * The date/time string specified as an argument must be a string in
1843 * the format defined as the lexical representation of either
1844 * xs:dateTime or xs:time. If the argument is not in either of these
1845 * formats, returns NULL.
1848 exsltDateTime (const xmlChar *dateTime)
1850 exsltDateValPtr dt = NULL;
1851 xmlChar *ret = NULL;
1853 if (dateTime == NULL) {
1855 dt = exsltDateCurrent();
1860 dt = exsltDateParse(dateTime);
1863 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1864 exsltDateFreeDate(dt);
1869 ret = exsltDateFormatTime(&(dt->value.date));
1870 exsltDateFreeDate(dt);
1877 * @dateTime: a date/time string
1879 * Implements the EXSLT - Dates and Times year() function
1880 * number date:year (string?)
1881 * Returns the year of a date as a number. If no argument is given,
1882 * then the current local date/time, as returned by date:date-time is
1883 * used as a default argument.
1884 * The date/time string specified as the first argument must be a
1885 * right-truncated string in the format defined as the lexical
1886 * representation of xs:dateTime in one of the formats defined in [XML
1887 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1888 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1889 * - xs:date (CCYY-MM-DD)
1890 * - xs:gYearMonth (CCYY-MM)
1892 * If the date/time string is not in one of these formats, then NaN is
1896 exsltDateYear (const xmlChar *dateTime)
1901 if (dateTime == NULL) {
1903 dt = exsltDateCurrent();
1908 dt = exsltDateParse(dateTime);
1911 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1912 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
1913 exsltDateFreeDate(dt);
1918 ret = (double) dt->value.date.year;
1919 exsltDateFreeDate(dt);
1925 * exsltDateLeapYear:
1926 * @dateTime: a date/time string
1928 * Implements the EXSLT - Dates and Times leap-year() function:
1929 * boolean date:leap-yea (string?)
1930 * Returns true if the year given in a date is a leap year. If no
1931 * argument is given, then the current local date/time, as returned by
1932 * date:date-time is used as a default argument.
1933 * The date/time string specified as the first argument must be a
1934 * right-truncated string in the format defined as the lexical
1935 * representation of xs:dateTime in one of the formats defined in [XML
1936 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1937 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1938 * - xs:date (CCYY-MM-DD)
1939 * - xs:gYearMonth (CCYY-MM)
1941 * If the date/time string is not in one of these formats, then NaN is
1944 static xmlXPathObjectPtr
1945 exsltDateLeapYear (const xmlChar *dateTime)
1949 year = exsltDateYear(dateTime);
1950 if (xmlXPathIsNaN(year))
1951 return xmlXPathNewFloat(xmlXPathNAN);
1953 if (IS_LEAP((long)year))
1954 return xmlXPathNewBoolean(1);
1956 return xmlXPathNewBoolean(0);
1960 * exsltDateMonthInYear:
1961 * @dateTime: a date/time string
1963 * Implements the EXSLT - Dates and Times month-in-year() function:
1964 * number date:month-in-year (string?)
1965 * Returns the month of a date as a number. If no argument is given,
1966 * then the current local date/time, as returned by date:date-time is
1967 * used the default argument.
1968 * The date/time string specified as the argument is a left or
1969 * right-truncated string in the format defined as the lexical
1970 * representation of xs:dateTime in one of the formats defined in [XML
1971 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1972 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1973 * - xs:date (CCYY-MM-DD)
1974 * - xs:gYearMonth (CCYY-MM)
1975 * - xs:gMonth (--MM--)
1976 * - xs:gMonthDay (--MM-DD)
1977 * If the date/time string is not in one of these formats, then NaN is
1981 exsltDateMonthInYear (const xmlChar *dateTime)
1986 if (dateTime == NULL) {
1988 dt = exsltDateCurrent();
1993 dt = exsltDateParse(dateTime);
1996 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1997 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
1998 (dt->type != XS_GMONTHDAY)) {
1999 exsltDateFreeDate(dt);
2004 ret = (double) dt->value.date.mon;
2005 exsltDateFreeDate(dt);
2011 * exsltDateMonthName:
2012 * @dateTime: a date/time string
2014 * Implements the EXSLT - Dates and Time month-name() function
2015 * string date:month-name (string?)
2016 * Returns the full name of the month of a date. If no argument is
2017 * given, then the current local date/time, as returned by
2018 * date:date-time is used the default argument.
2019 * The date/time string specified as the argument is a left or
2020 * right-truncated string in the format defined as the lexical
2021 * representation of xs:dateTime in one of the formats defined in [XML
2022 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2023 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2024 * - xs:date (CCYY-MM-DD)
2025 * - xs:gYearMonth (CCYY-MM)
2026 * - xs:gMonth (--MM--)
2027 * If the date/time string is not in one of these formats, then an
2028 * empty string ('') is returned.
2029 * The result is an English month name: one of 'January', 'February',
2030 * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
2031 * 'October', 'November' or 'December'.
2033 static const xmlChar *
2034 exsltDateMonthName (const xmlChar *dateTime)
2036 static const xmlChar monthNames[13][10] = {
2038 { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
2039 { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
2040 { 'M', 'a', 'r', 'c', 'h', 0 },
2041 { 'A', 'p', 'r', 'i', 'l', 0 },
2042 { 'M', 'a', 'y', 0 },
2043 { 'J', 'u', 'n', 'e', 0 },
2044 { 'J', 'u', 'l', 'y', 0 },
2045 { 'A', 'u', 'g', 'u', 's', 't', 0 },
2046 { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
2047 { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
2048 { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
2049 { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
2052 month = (int) exsltDateMonthInYear(dateTime);
2053 if (!VALID_MONTH(month))
2055 return monthNames[month];
2059 * exsltDateMonthAbbreviation:
2060 * @dateTime: a date/time string
2062 * Implements the EXSLT - Dates and Time month-abbreviation() function
2063 * string date:month-abbreviation (string?)
2064 * Returns the abbreviation of the month of a date. If no argument is
2065 * given, then the current local date/time, as returned by
2066 * date:date-time is used the default argument.
2067 * The date/time string specified as the argument is a left or
2068 * right-truncated string in the format defined as the lexical
2069 * representation of xs:dateTime in one of the formats defined in [XML
2070 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2071 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2072 * - xs:date (CCYY-MM-DD)
2073 * - xs:gYearMonth (CCYY-MM)
2074 * - xs:gMonth (--MM--)
2075 * If the date/time string is not in one of these formats, then an
2076 * empty string ('') is returned.
2077 * The result is an English month abbreviation: one of 'Jan', 'Feb',
2078 * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
2081 static const xmlChar *
2082 exsltDateMonthAbbreviation (const xmlChar *dateTime)
2084 static const xmlChar monthAbbreviations[13][4] = {
2086 { 'J', 'a', 'n', 0 },
2087 { 'F', 'e', 'b', 0 },
2088 { 'M', 'a', 'r', 0 },
2089 { 'A', 'p', 'r', 0 },
2090 { 'M', 'a', 'y', 0 },
2091 { 'J', 'u', 'n', 0 },
2092 { 'J', 'u', 'l', 0 },
2093 { 'A', 'u', 'g', 0 },
2094 { 'S', 'e', 'p', 0 },
2095 { 'O', 'c', 't', 0 },
2096 { 'N', 'o', 'v', 0 },
2097 { 'D', 'e', 'c', 0 }
2100 month = (int) exsltDateMonthInYear(dateTime);
2101 if(!VALID_MONTH(month))
2103 return monthAbbreviations[month];
2107 * exsltDateWeekInYear:
2108 * @dateTime: a date/time string
2110 * Implements the EXSLT - Dates and Times week-in-year() function
2111 * number date:week-in-year (string?)
2112 * Returns the week of the year as a number. If no argument is given,
2113 * then the current local date/time, as returned by date:date-time is
2114 * used as the default argument. For the purposes of numbering,
2115 * counting follows ISO 8601: week 1 in a year is the week containing
2116 * the first Thursday of the year, with new weeks beginning on a
2118 * The date/time string specified as the argument is a right-truncated
2119 * string in the format defined as the lexical representation of
2120 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2121 * Datatypes]. The permitted formats are as follows:
2122 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2123 * - xs:date (CCYY-MM-DD)
2124 * If the date/time string is not in one of these formats, then NaN is
2128 exsltDateWeekInYear (const xmlChar *dateTime)
2131 long fdiy, fdiw, ret;
2133 if (dateTime == NULL) {
2135 dt = exsltDateCurrent();
2140 dt = exsltDateParse(dateTime);
2143 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2144 exsltDateFreeDate(dt);
2149 fdiy = DAY_IN_YEAR(1, 1, dt->value.date.year);
2152 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2153 * is the first day-in-week
2155 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2157 ret = (DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2158 dt->value.date.year) + fdiw) / 7;
2160 /* ISO 8601 adjustment, 3 is Thu */
2164 exsltDateFreeDate(dt);
2166 return (double) ret;
2170 * exsltDateWeekInMonth:
2171 * @dateTime: a date/time string
2173 * Implements the EXSLT - Dates and Times week-in-month() function
2174 * number date:week-in-month (string?)
2175 * The date:week-in-month function returns the week in a month of a
2176 * date as a number. If no argument is given, then the current local
2177 * date/time, as returned by date:date-time is used the default
2178 * argument. For the purposes of numbering, the first day of the month
2179 * is in week 1 and new weeks begin on a Monday (so the first and last
2180 * weeks in a month will often have less than 7 days in them).
2181 * The date/time string specified as the argument is a right-truncated
2182 * string in the format defined as the lexical representation of
2183 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2184 * Datatypes]. The permitted formats are as follows:
2185 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2186 * - xs:date (CCYY-MM-DD)
2187 * If the date/time string is not in one of these formats, then NaN is
2191 exsltDateWeekInMonth (const xmlChar *dateTime)
2194 long fdiy, fdiw, ret;
2196 if (dateTime == NULL) {
2198 dt = exsltDateCurrent();
2203 dt = exsltDateParse(dateTime);
2206 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2207 exsltDateFreeDate(dt);
2212 fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
2214 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2215 * is the first day-in-week
2217 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2219 ret = ((dt->value.date.day + fdiw - 1) / 7) + 1;
2221 exsltDateFreeDate(dt);
2223 return (double) ret;
2227 * exsltDateDayInYear:
2228 * @dateTime: a date/time string
2230 * Implements the EXSLT - Dates and Times day-in-year() function
2231 * number date:day-in-year (string?)
2232 * Returns the day of a date in a year as a number. If no argument is
2233 * given, then the current local date/time, as returned by
2234 * date:date-time is used the default argument.
2235 * The date/time string specified as the argument is a right-truncated
2236 * string in the format defined as the lexical representation of
2237 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2238 * Datatypes]. The permitted formats are as follows:
2239 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2240 * - xs:date (CCYY-MM-DD)
2241 * If the date/time string is not in one of these formats, then NaN is
2245 exsltDateDayInYear (const xmlChar *dateTime)
2250 if (dateTime == NULL) {
2252 dt = exsltDateCurrent();
2257 dt = exsltDateParse(dateTime);
2260 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2261 exsltDateFreeDate(dt);
2266 ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2267 dt->value.date.year);
2269 exsltDateFreeDate(dt);
2271 return (double) ret;
2275 * exsltDateDayInMonth:
2276 * @dateTime: a date/time string
2278 * Implements the EXSLT - Dates and Times day-in-month() function:
2279 * number date:day-in-month (string?)
2280 * Returns the day of a date as a number. If no argument is given,
2281 * then the current local date/time, as returned by date:date-time is
2282 * used the default argument.
2283 * The date/time string specified as the argument is a left or
2284 * right-truncated string in the format defined as the lexical
2285 * representation of xs:dateTime in one of the formats defined in [XML
2286 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2287 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2288 * - xs:date (CCYY-MM-DD)
2289 * - xs:gMonthDay (--MM-DD)
2291 * If the date/time string is not in one of these formats, then NaN is
2295 exsltDateDayInMonth (const xmlChar *dateTime)
2300 if (dateTime == NULL) {
2302 dt = exsltDateCurrent();
2307 dt = exsltDateParse(dateTime);
2310 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
2311 (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
2312 exsltDateFreeDate(dt);
2317 ret = (double) dt->value.date.day;
2318 exsltDateFreeDate(dt);
2324 * exsltDateDayOfWeekInMonth:
2325 * @dateTime: a date/time string
2327 * Implements the EXSLT - Dates and Times day-of-week-in-month() function:
2328 * number date:day-of-week-in-month (string?)
2329 * Returns the day-of-the-week in a month of a date as a number
2330 * (e.g. 3 for the 3rd Tuesday in May). If no argument is
2331 * given, then the current local date/time, as returned by
2332 * date:date-time is used the default argument.
2333 * The date/time string specified as the argument is a right-truncated
2334 * string in the format defined as the lexical representation of
2335 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2336 * Datatypes]. The permitted formats are as follows:
2337 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2338 * - xs:date (CCYY-MM-DD)
2339 * If the date/time string is not in one of these formats, then NaN is
2343 exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
2348 if (dateTime == NULL) {
2350 dt = exsltDateCurrent();
2355 dt = exsltDateParse(dateTime);
2358 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2359 exsltDateFreeDate(dt);
2364 ret = ((dt->value.date.day -1) / 7) + 1;
2366 exsltDateFreeDate(dt);
2368 return (double) ret;
2372 * exsltDateDayInWeek:
2373 * @dateTime: a date/time string
2375 * Implements the EXSLT - Dates and Times day-in-week() function:
2376 * number date:day-in-week (string?)
2377 * Returns the day of the week given in a date as a number. If no
2378 * argument is given, then the current local date/time, as returned by
2379 * date:date-time is used the default argument.
2380 * The date/time string specified as the argument is a left or
2381 * right-truncated string in the format defined as the lexical
2382 * representation of xs:dateTime in one of the formats defined in [XML
2383 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2384 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2385 * - xs:date (CCYY-MM-DD)
2386 * If the date/time string is not in one of these formats, then NaN is
2388 * The numbering of days of the week starts at 1 for Sunday, 2 for
2389 * Monday and so on up to 7 for Saturday.
2392 exsltDateDayInWeek (const xmlChar *dateTime)
2397 if (dateTime == NULL) {
2399 dt = exsltDateCurrent();
2404 dt = exsltDateParse(dateTime);
2407 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2408 exsltDateFreeDate(dt);
2413 diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2414 dt->value.date.year);
2416 ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
2418 exsltDateFreeDate(dt);
2420 return (double) ret;
2425 * @dateTime: a date/time string
2427 * Implements the EXSLT - Dates and Time day-name() function
2428 * string date:day-name (string?)
2429 * Returns the full name of the day of the week of a date. If no
2430 * argument is given, then the current local date/time, as returned by
2431 * date:date-time is used the default argument.
2432 * The date/time string specified as the argument is a left or
2433 * right-truncated string in the format defined as the lexical
2434 * representation of xs:dateTime in one of the formats defined in [XML
2435 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2436 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2437 * - xs:date (CCYY-MM-DD)
2438 * If the date/time string is not in one of these formats, then an
2439 * empty string ('') is returned.
2440 * The result is an English day name: one of 'Sunday', 'Monday',
2441 * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
2443 static const xmlChar *
2444 exsltDateDayName (const xmlChar *dateTime)
2446 static const xmlChar dayNames[8][10] = {
2448 { 'S', 'u', 'n', 'd', 'a', 'y', 0 },
2449 { 'M', 'o', 'n', 'd', 'a', 'y', 0 },
2450 { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
2451 { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
2452 { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
2453 { 'F', 'r', 'i', 'd', 'a', 'y', 0 },
2454 { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
2457 day = (int) exsltDateDayInWeek(dateTime);
2458 if((day < 1) || (day > 7))
2460 return dayNames[day];
2464 * exsltDateDayAbbreviation:
2465 * @dateTime: a date/time string
2467 * Implements the EXSLT - Dates and Time day-abbreviation() function
2468 * string date:day-abbreviation (string?)
2469 * Returns the abbreviation of the day of the week of a date. If no
2470 * argument is given, then the current local date/time, as returned by
2471 * date:date-time is used the default argument.
2472 * The date/time string specified as the argument is a left or
2473 * right-truncated string in the format defined as the lexical
2474 * representation of xs:dateTime in one of the formats defined in [XML
2475 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2476 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2477 * - xs:date (CCYY-MM-DD)
2478 * If the date/time string is not in one of these formats, then an
2479 * empty string ('') is returned.
2480 * The result is a three-letter English day abbreviation: one of
2481 * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
2483 static const xmlChar *
2484 exsltDateDayAbbreviation (const xmlChar *dateTime)
2486 static const xmlChar dayAbbreviations[8][4] = {
2488 { 'S', 'u', 'n', 0 },
2489 { 'M', 'o', 'n', 0 },
2490 { 'T', 'u', 'e', 0 },
2491 { 'W', 'e', 'd', 0 },
2492 { 'T', 'h', 'u', 0 },
2493 { 'F', 'r', 'i', 0 },
2494 { 'S', 'a', 't', 0 }
2497 day = (int) exsltDateDayInWeek(dateTime);
2498 if((day < 1) || (day > 7))
2500 return dayAbbreviations[day];
2504 * exsltDateHourInDay:
2505 * @dateTime: a date/time string
2507 * Implements the EXSLT - Dates and Times day-in-month() function:
2508 * number date:day-in-month (string?)
2509 * Returns the hour of the day as a number. If no argument is given,
2510 * then the current local date/time, as returned by date:date-time is
2511 * used the default argument.
2512 * The date/time string specified as the argument is a left or
2513 * right-truncated string in the format defined as the lexical
2514 * representation of xs:dateTime in one of the formats defined in [XML
2515 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2516 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2517 * - xs:time (hh:mm:ss)
2518 * If the date/time string is not in one of these formats, then NaN is
2522 exsltDateHourInDay (const xmlChar *dateTime)
2527 if (dateTime == NULL) {
2529 dt = exsltDateCurrent();
2534 dt = exsltDateParse(dateTime);
2537 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2538 exsltDateFreeDate(dt);
2543 ret = (double) dt->value.date.hour;
2544 exsltDateFreeDate(dt);
2550 * exsltDateMinuteInHour:
2551 * @dateTime: a date/time string
2553 * Implements the EXSLT - Dates and Times day-in-month() function:
2554 * number date:day-in-month (string?)
2555 * Returns the minute of the hour as a number. If no argument is
2556 * given, then the current local date/time, as returned by
2557 * date:date-time is used the default argument.
2558 * The date/time string specified as the argument is a left or
2559 * right-truncated string in the format defined as the lexical
2560 * representation of xs:dateTime in one of the formats defined in [XML
2561 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2562 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2563 * - xs:time (hh:mm:ss)
2564 * If the date/time string is not in one of these formats, then NaN is
2568 exsltDateMinuteInHour (const xmlChar *dateTime)
2573 if (dateTime == NULL) {
2575 dt = exsltDateCurrent();
2580 dt = exsltDateParse(dateTime);
2583 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2584 exsltDateFreeDate(dt);
2589 ret = (double) dt->value.date.min;
2590 exsltDateFreeDate(dt);
2596 * exsltDateSecondInMinute:
2597 * @dateTime: a date/time string
2599 * Implements the EXSLT - Dates and Times second-in-minute() function:
2600 * number date:day-in-month (string?)
2601 * Returns the second of the minute as a number. If no argument is
2602 * given, then the current local date/time, as returned by
2603 * date:date-time is used the default argument.
2604 * The date/time string specified as the argument is a left or
2605 * right-truncated string in the format defined as the lexical
2606 * representation of xs:dateTime in one of the formats defined in [XML
2607 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2608 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2609 * - xs:time (hh:mm:ss)
2610 * If the date/time string is not in one of these formats, then NaN is
2613 * Returns the second or NaN.
2616 exsltDateSecondInMinute (const xmlChar *dateTime)
2621 if (dateTime == NULL) {
2623 dt = exsltDateCurrent();
2628 dt = exsltDateParse(dateTime);
2631 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2632 exsltDateFreeDate(dt);
2637 ret = dt->value.date.sec;
2638 exsltDateFreeDate(dt);
2645 * @xstr: date/time string
2646 * @ystr: date/time string
2648 * Implements the date:add (string,string) function which returns the
2649 * date/time * resulting from adding a duration to a date/time.
2650 * The first argument (@xstr) must be right-truncated date/time
2651 * strings in one of the formats defined in [XML Schema Part 2:
2652 * Datatypes]. The permitted formats are as follows:
2653 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2654 * - xs:date (CCYY-MM-DD)
2655 * - xs:gYearMonth (CCYY-MM)
2657 * The second argument (@ystr) is a string in the format defined for
2658 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2659 * The return value is a right-truncated date/time strings in one of
2660 * the formats defined in [XML Schema Part 2: Datatypes] and listed
2661 * above. This value is calculated using the algorithm described in
2662 * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
2665 * Returns date/time string or NULL.
2668 exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
2670 exsltDateValPtr dt, dur, res;
2673 if ((xstr == NULL) || (ystr == NULL))
2676 dt = exsltDateParse(xstr);
2679 else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
2680 exsltDateFreeDate(dt);
2684 dur = exsltDateParseDuration(ystr);
2686 exsltDateFreeDate(dt);
2690 res = _exsltDateAdd(dt, dur);
2692 exsltDateFreeDate(dt);
2693 exsltDateFreeDate(dur);
2698 ret = exsltDateFormat(res);
2699 exsltDateFreeDate(res);
2705 * exsltDateAddDuration:
2706 * @xstr: first duration string
2707 * @ystr: second duration string
2709 * Implements the date:add-duration (string,string) function which returns
2710 * the duration resulting from adding two durations together.
2711 * Both arguments are strings in the format defined for xs:duration
2712 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
2713 * argument is not in this format, the function returns an empty string
2715 * The return value is a string in the format defined for xs:duration
2716 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2717 * The durations can usually be added by summing the numbers given for
2718 * each of the components in the durations. However, if the durations
2719 * are differently signed, then this sometimes results in durations
2720 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2721 * In these cases, the function returns an empty string ('').
2723 * Returns duration string or NULL.
2726 exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
2728 exsltDateValPtr x, y, res;
2731 if ((xstr == NULL) || (ystr == NULL))
2734 x = exsltDateParseDuration(xstr);
2738 y = exsltDateParseDuration(ystr);
2740 exsltDateFreeDate(x);
2744 res = _exsltDateAddDuration(x, y);
2746 exsltDateFreeDate(x);
2747 exsltDateFreeDate(y);
2752 ret = exsltDateFormatDuration(&(res->value.dur));
2753 exsltDateFreeDate(res);
2759 * exsltDateSumFunction:
2760 * @ns: a node set of duration strings
2762 * The date:sum function adds a set of durations together.
2763 * The string values of the nodes in the node set passed as an argument
2764 * are interpreted as durations and added together as if using the
2765 * date:add-duration function. (from exslt.org)
2767 * The return value is a string in the format defined for xs:duration
2768 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2769 * The durations can usually be added by summing the numbers given for
2770 * each of the components in the durations. However, if the durations
2771 * are differently signed, then this sometimes results in durations
2772 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2773 * In these cases, the function returns an empty string ('').
2775 * Returns duration string or NULL.
2778 exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
2783 exsltDateValPtr x, total;
2788 xmlXPathSetArityError (ctxt);
2792 /* We need to delay the freeing of value->user */
2793 if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
2794 user = ctxt->value->user;
2795 ctxt->value->boolval = 0;
2796 ctxt->value->user = NULL;
2799 ns = xmlXPathPopNodeSet (ctxt);
2800 if (xmlXPathCheckError (ctxt))
2803 if ((ns == NULL) || (ns->nodeNr == 0)) {
2804 xmlXPathReturnEmptyString (ctxt);
2806 xmlXPathFreeNodeSet (ns);
2810 total = exsltDateCreateDate (XS_DURATION);
2811 if (total == NULL) {
2812 xmlXPathFreeNodeSet (ns);
2816 for (i = 0; i < ns->nodeNr; i++) {
2818 tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
2820 xmlXPathFreeNodeSet (ns);
2821 exsltDateFreeDate (total);
2825 x = exsltDateParseDuration (tmp);
2828 exsltDateFreeDate (total);
2829 xmlXPathFreeNodeSet (ns);
2830 xmlXPathReturnEmptyString (ctxt);
2834 result = _exsltDateAddDurCalc(total, total, x);
2836 exsltDateFreeDate (x);
2839 exsltDateFreeDate (total);
2840 xmlXPathFreeNodeSet (ns);
2841 xmlXPathReturnEmptyString (ctxt);
2846 ret = exsltDateFormatDuration (&(total->value.dur));
2847 exsltDateFreeDate (total);
2849 xmlXPathFreeNodeSet (ns);
2851 xmlFreeNodeList ((xmlNodePtr) user);
2854 xmlXPathReturnEmptyString (ctxt);
2856 xmlXPathReturnString (ctxt, ret);
2861 * @dateTime: a date/time string
2863 * Implements the EXSLT - Dates and Times seconds() function:
2864 * number date:seconds(string?)
2865 * The date:seconds function returns the number of seconds specified
2866 * by the argument string. If no argument is given, then the current
2867 * local date/time, as returned by exsltDateCurrent() is used as the
2868 * default argument. If the date/time string is a xs:duration, then the
2869 * years and months must be zero (or not present). Parsing a duration
2870 * converts the fields to seconds. If the date/time string is not a
2871 * duration (and not null), then the legal formats are:
2872 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2873 * - xs:date (CCYY-MM-DD)
2874 * - xs:gYearMonth (CCYY-MM)
2876 * In these cases the difference between the @dateTime and
2877 * 1970-01-01T00:00:00Z is calculated and converted to seconds.
2879 * Note that there was some confusion over whether "difference" meant
2880 * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
2881 * a negative one. After correspondence with exslt.org, it was determined
2882 * that the intent of the specification was to have it positive. The
2883 * coding was modified in July 2003 to reflect this.
2885 * Returns seconds or Nan.
2888 exsltDateSeconds (const xmlChar *dateTime)
2891 double ret = xmlXPathNAN;
2893 if (dateTime == NULL) {
2895 dt = exsltDateCurrent();
2900 dt = exsltDateParseDuration(dateTime);
2902 dt = exsltDateParse(dateTime);
2908 if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
2909 exsltDateValPtr y, dur;
2912 * compute the difference between the given (or current) date
2915 y = exsltDateCreateDate(XS_DATETIME);
2917 y->value.date.year = 1970;
2918 y->value.date.mon = 1;
2919 y->value.date.day = 1;
2920 y->value.date.tz_flag = 1;
2922 dur = _exsltDateDifference(y, dt, 1);
2924 ret = exsltDateCastDateToNumber(dur);
2925 exsltDateFreeDate(dur);
2927 exsltDateFreeDate(y);
2930 } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
2931 ret = exsltDateCastDateToNumber(dt);
2933 exsltDateFreeDate(dt);
2939 * exsltDateDifference:
2940 * @xstr: date/time string
2941 * @ystr: date/time string
2943 * Implements the date:difference (string,string) function which returns
2944 * the duration between the first date and the second date. If the first
2945 * date occurs before the second date, then the result is a positive
2946 * duration; if it occurs after the second date, the result is a
2947 * negative duration. The two dates must both be right-truncated
2948 * date/time strings in one of the formats defined in [XML Schema Part
2949 * 2: Datatypes]. The date/time with the most specific format (i.e. the
2950 * least truncation) is converted into the same format as the date with
2951 * the least specific format (i.e. the most truncation). The permitted
2952 * formats are as follows, from most specific to least specific:
2953 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2954 * - xs:date (CCYY-MM-DD)
2955 * - xs:gYearMonth (CCYY-MM)
2957 * If either of the arguments is not in one of these formats,
2958 * date:difference returns the empty string ('').
2959 * The difference between the date/times is returned as a string in the
2960 * format defined for xs:duration in [3.2.6 duration] of [XML Schema
2961 * Part 2: Datatypes].
2962 * If the date/time string with the least specific format is in either
2963 * xs:gYearMonth or xs:gYear format, then the number of days, hours,
2964 * minutes and seconds in the duration string must be equal to zero.
2965 * (The format of the string will be PnYnM.) The number of months
2966 * specified in the duration must be less than 12.
2967 * Otherwise, the number of years and months in the duration string
2968 * must be equal to zero. (The format of the string will be
2969 * PnDTnHnMnS.) The number of seconds specified in the duration string
2970 * must be less than 60; the number of minutes must be less than 60;
2971 * the number of hours must be less than 24.
2973 * Returns duration string or NULL.
2976 exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
2978 exsltDateValPtr x, y, dur;
2979 xmlChar *ret = NULL;
2981 if ((xstr == NULL) || (ystr == NULL))
2984 x = exsltDateParse(xstr);
2988 y = exsltDateParse(ystr);
2990 exsltDateFreeDate(x);
2994 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
2995 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) {
2996 exsltDateFreeDate(x);
2997 exsltDateFreeDate(y);
3001 dur = _exsltDateDifference(x, y, 0);
3003 exsltDateFreeDate(x);
3004 exsltDateFreeDate(y);
3009 ret = exsltDateFormatDuration(&(dur->value.dur));
3010 exsltDateFreeDate(dur);
3016 * exsltDateDuration:
3017 * @number: a xmlChar string
3019 * Implements the The date:duration function returns a duration string
3020 * representing the number of seconds specified by the argument string.
3021 * If no argument is given, then the result of calling date:seconds
3022 * without any arguments is used as a default argument.
3023 * The duration is returned as a string in the format defined for
3024 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
3025 * The number of years and months in the duration string must be equal
3026 * to zero. (The format of the string will be PnDTnHnMnS.) The number
3027 * of seconds specified in the duration string must be less than 60;
3028 * the number of minutes must be less than 60; the number of hours must
3030 * If the argument is Infinity, -Infinity or NaN, then date:duration
3031 * returns an empty string ('').
3033 * Returns duration string or NULL.
3036 exsltDateDuration (const xmlChar *number)
3038 exsltDateValPtr dur;
3043 secs = exsltDateSeconds(number);
3045 secs = xmlXPathCastStringToNumber(number);
3047 if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
3050 dur = exsltDateCreateDate(XS_DURATION);
3054 dur->value.dur.sec = secs;
3056 ret = exsltDateFormatDuration(&(dur->value.dur));
3057 exsltDateFreeDate(dur);
3062 /****************************************************************
3064 * Wrappers for use by the XPath engine *
3066 ****************************************************************/
3070 * exsltDateDateTimeFunction:
3071 * @ctxt: an XPath parser context
3072 * @nargs : the number of arguments
3074 * Wraps exsltDateDateTime() for use by the XPath engine.
3077 exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3082 xmlXPathSetArityError(ctxt);
3086 ret = exsltDateDateTime();
3088 xmlXPathReturnEmptyString(ctxt);
3090 xmlXPathReturnString(ctxt, ret);
3095 * exsltDateDateFunction:
3096 * @ctxt: an XPath parser context
3097 * @nargs : the number of arguments
3099 * Wraps exsltDateDate() for use by the XPath engine.
3102 exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
3104 xmlChar *ret, *dt = NULL;
3106 if ((nargs < 0) || (nargs > 1)) {
3107 xmlXPathSetArityError(ctxt);
3111 dt = xmlXPathPopString(ctxt);
3112 if (xmlXPathCheckError(ctxt)) {
3113 xmlXPathSetTypeError(ctxt);
3118 ret = exsltDateDate(dt);
3121 xsltGenericDebug(xsltGenericDebugContext,
3122 "{http://exslt.org/dates-and-times}date: "
3123 "invalid date or format %s\n", dt);
3124 xmlXPathReturnEmptyString(ctxt);
3126 xmlXPathReturnString(ctxt, ret);
3134 * exsltDateTimeFunction:
3135 * @ctxt: an XPath parser context
3136 * @nargs : the number of arguments
3138 * Wraps exsltDateTime() for use by the XPath engine.
3141 exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3143 xmlChar *ret, *dt = NULL;
3145 if ((nargs < 0) || (nargs > 1)) {
3146 xmlXPathSetArityError(ctxt);
3150 dt = xmlXPathPopString(ctxt);
3151 if (xmlXPathCheckError(ctxt)) {
3152 xmlXPathSetTypeError(ctxt);
3157 ret = exsltDateTime(dt);
3160 xsltGenericDebug(xsltGenericDebugContext,
3161 "{http://exslt.org/dates-and-times}time: "
3162 "invalid date or format %s\n", dt);
3163 xmlXPathReturnEmptyString(ctxt);
3165 xmlXPathReturnString(ctxt, ret);
3173 * exsltDateYearFunction:
3174 * @ctxt: an XPath parser context
3175 * @nargs : the number of arguments
3177 * Wraps exsltDateYear() for use by the XPath engine.
3180 exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3185 if ((nargs < 0) || (nargs > 1)) {
3186 xmlXPathSetArityError(ctxt);
3191 dt = xmlXPathPopString(ctxt);
3192 if (xmlXPathCheckError(ctxt)) {
3193 xmlXPathSetTypeError(ctxt);
3198 ret = exsltDateYear(dt);
3203 xmlXPathReturnNumber(ctxt, ret);
3207 * exsltDateLeapYearFunction:
3208 * @ctxt: an XPath parser context
3209 * @nargs : the number of arguments
3211 * Wraps exsltDateLeapYear() for use by the XPath engine.
3214 exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3217 xmlXPathObjectPtr ret;
3219 if ((nargs < 0) || (nargs > 1)) {
3220 xmlXPathSetArityError(ctxt);
3225 dt = xmlXPathPopString(ctxt);
3226 if (xmlXPathCheckError(ctxt)) {
3227 xmlXPathSetTypeError(ctxt);
3232 ret = exsltDateLeapYear(dt);
3237 valuePush(ctxt, ret);
3240 #define X_IN_Y(x, y) \
3242 exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \
3244 xmlChar *dt = NULL; \
3247 if ((nargs < 0) || (nargs > 1)) { \
3248 xmlXPathSetArityError(ctxt); \
3253 dt = xmlXPathPopString(ctxt); \
3254 if (xmlXPathCheckError(ctxt)) { \
3255 xmlXPathSetTypeError(ctxt); \
3260 ret = exsltDate##x##In##y(dt); \
3265 xmlXPathReturnNumber(ctxt, ret); \
3269 * exsltDateMonthInYearFunction:
3270 * @ctxt: an XPath parser context
3271 * @nargs : the number of arguments
3273 * Wraps exsltDateMonthInYear() for use by the XPath engine.
3278 * exsltDateMonthNameFunction:
3279 * @ctxt: an XPath parser context
3280 * @nargs : the number of arguments
3282 * Wraps exsltDateMonthName() for use by the XPath engine.
3285 exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3290 if ((nargs < 0) || (nargs > 1)) {
3291 xmlXPathSetArityError(ctxt);
3296 dt = xmlXPathPopString(ctxt);
3297 if (xmlXPathCheckError(ctxt)) {
3298 xmlXPathSetTypeError(ctxt);
3303 ret = exsltDateMonthName(dt);
3309 xmlXPathReturnEmptyString(ctxt);
3311 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3315 * exsltDateMonthAbbreviationFunction:
3316 * @ctxt: an XPath parser context
3317 * @nargs : the number of arguments
3319 * Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
3322 exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3327 if ((nargs < 0) || (nargs > 1)) {
3328 xmlXPathSetArityError(ctxt);
3333 dt = xmlXPathPopString(ctxt);
3334 if (xmlXPathCheckError(ctxt)) {
3335 xmlXPathSetTypeError(ctxt);
3340 ret = exsltDateMonthAbbreviation(dt);
3346 xmlXPathReturnEmptyString(ctxt);
3348 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3352 * exsltDateWeekInYearFunction:
3353 * @ctxt: an XPath parser context
3354 * @nargs : the number of arguments
3356 * Wraps exsltDateWeekInYear() for use by the XPath engine.
3361 * exsltDateWeekInMonthFunction:
3362 * @ctxt: an XPath parser context
3363 * @nargs : the number of arguments
3365 * Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
3370 * exsltDateDayInYearFunction:
3371 * @ctxt: an XPath parser context
3372 * @nargs : the number of arguments
3374 * Wraps exsltDateDayInYear() for use by the XPath engine.
3379 * exsltDateDayInMonthFunction:
3380 * @ctxt: an XPath parser context
3381 * @nargs : the number of arguments
3383 * Wraps exsltDateDayInMonth() for use by the XPath engine.
3388 * exsltDateDayOfWeekInMonthFunction:
3389 * @ctxt: an XPath parser context
3390 * @nargs : the number of arguments
3392 * Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
3394 X_IN_Y(DayOfWeek,Month)
3397 * exsltDateDayInWeekFunction:
3398 * @ctxt: an XPath parser context
3399 * @nargs : the number of arguments
3401 * Wraps exsltDateDayInWeek() for use by the XPath engine.
3406 * exsltDateDayNameFunction:
3407 * @ctxt: an XPath parser context
3408 * @nargs : the number of arguments
3410 * Wraps exsltDateDayName() for use by the XPath engine.
3413 exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3418 if ((nargs < 0) || (nargs > 1)) {
3419 xmlXPathSetArityError(ctxt);
3424 dt = xmlXPathPopString(ctxt);
3425 if (xmlXPathCheckError(ctxt)) {
3426 xmlXPathSetTypeError(ctxt);
3431 ret = exsltDateDayName(dt);
3437 xmlXPathReturnEmptyString(ctxt);
3439 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3443 * exsltDateMonthDayFunction:
3444 * @ctxt: an XPath parser context
3445 * @nargs : the number of arguments
3447 * Wraps exsltDateDayAbbreviation() for use by the XPath engine.
3450 exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3455 if ((nargs < 0) || (nargs > 1)) {
3456 xmlXPathSetArityError(ctxt);
3461 dt = xmlXPathPopString(ctxt);
3462 if (xmlXPathCheckError(ctxt)) {
3463 xmlXPathSetTypeError(ctxt);
3468 ret = exsltDateDayAbbreviation(dt);
3474 xmlXPathReturnEmptyString(ctxt);
3476 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3481 * exsltDateHourInDayFunction:
3482 * @ctxt: an XPath parser context
3483 * @nargs : the number of arguments
3485 * Wraps exsltDateHourInDay() for use by the XPath engine.
3490 * exsltDateMinuteInHourFunction:
3491 * @ctxt: an XPath parser context
3492 * @nargs : the number of arguments
3494 * Wraps exsltDateMinuteInHour() for use by the XPath engine.
3499 * exsltDateSecondInMinuteFunction:
3500 * @ctxt: an XPath parser context
3501 * @nargs : the number of arguments
3503 * Wraps exsltDateSecondInMinute() for use by the XPath engine.
3505 X_IN_Y(Second,Minute)
3508 * exsltDateSecondsFunction:
3509 * @ctxt: an XPath parser context
3510 * @nargs : the number of arguments
3512 * Wraps exsltDateSeconds() for use by the XPath engine.
3515 exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
3517 xmlChar *str = NULL;
3521 xmlXPathSetArityError(ctxt);
3526 str = xmlXPathPopString(ctxt);
3527 if (xmlXPathCheckError(ctxt)) {
3528 xmlXPathSetTypeError(ctxt);
3533 ret = exsltDateSeconds(str);
3537 xmlXPathReturnNumber(ctxt, ret);
3541 * exsltDateAddFunction:
3542 * @ctxt: an XPath parser context
3543 * @nargs: the number of arguments
3545 * Wraps exsltDateAdd() for use by the XPath processor.
3548 exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
3550 xmlChar *ret, *xstr, *ystr;
3553 xmlXPathSetArityError(ctxt);
3556 ystr = xmlXPathPopString(ctxt);
3557 if (xmlXPathCheckError(ctxt))
3560 xstr = xmlXPathPopString(ctxt);
3561 if (xmlXPathCheckError(ctxt)) {
3566 ret = exsltDateAdd(xstr, ystr);
3572 xmlXPathReturnEmptyString(ctxt);
3574 xmlXPathReturnString(ctxt, ret);
3578 * exsltDateAddDurationFunction:
3579 * @ctxt: an XPath parser context
3580 * @nargs: the number of arguments
3582 * Wraps exsltDateAddDuration() for use by the XPath processor.
3585 exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3587 xmlChar *ret, *xstr, *ystr;
3590 xmlXPathSetArityError(ctxt);
3593 ystr = xmlXPathPopString(ctxt);
3594 if (xmlXPathCheckError(ctxt))
3597 xstr = xmlXPathPopString(ctxt);
3598 if (xmlXPathCheckError(ctxt)) {
3603 ret = exsltDateAddDuration(xstr, ystr);
3609 xmlXPathReturnEmptyString(ctxt);
3611 xmlXPathReturnString(ctxt, ret);
3615 * exsltDateDifferenceFunction:
3616 * @ctxt: an XPath parser context
3617 * @nargs: the number of arguments
3619 * Wraps exsltDateDifference() for use by the XPath processor.
3622 exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
3624 xmlChar *ret, *xstr, *ystr;
3627 xmlXPathSetArityError(ctxt);
3630 ystr = xmlXPathPopString(ctxt);
3631 if (xmlXPathCheckError(ctxt))
3634 xstr = xmlXPathPopString(ctxt);
3635 if (xmlXPathCheckError(ctxt)) {
3640 ret = exsltDateDifference(xstr, ystr);
3646 xmlXPathReturnEmptyString(ctxt);
3648 xmlXPathReturnString(ctxt, ret);
3652 * exsltDateDurationFunction:
3653 * @ctxt: an XPath parser context
3654 * @nargs : the number of arguments
3656 * Wraps exsltDateDuration() for use by the XPath engine
3659 exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3662 xmlChar *number = NULL;
3664 if ((nargs < 0) || (nargs > 1)) {
3665 xmlXPathSetArityError(ctxt);
3670 number = xmlXPathPopString(ctxt);
3671 if (xmlXPathCheckError(ctxt)) {
3672 xmlXPathSetTypeError(ctxt);
3677 ret = exsltDateDuration(number);
3683 xmlXPathReturnEmptyString(ctxt);
3685 xmlXPathReturnString(ctxt, ret);
3689 * exsltDateRegister:
3691 * Registers the EXSLT - Dates and Times module
3694 exsltDateRegister (void)
3696 xsltRegisterExtModuleFunction ((const xmlChar *) "add",
3697 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3698 exsltDateAddFunction);
3699 xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
3700 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3701 exsltDateAddDurationFunction);
3702 xsltRegisterExtModuleFunction ((const xmlChar *) "date",
3703 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3704 exsltDateDateFunction);
3706 xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
3707 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3708 exsltDateDateTimeFunction);
3710 xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
3711 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3712 exsltDateDayAbbreviationFunction);
3713 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
3714 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3715 exsltDateDayInMonthFunction);
3716 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
3717 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3718 exsltDateDayInWeekFunction);
3719 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
3720 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3721 exsltDateDayInYearFunction);
3722 xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
3723 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3724 exsltDateDayNameFunction);
3725 xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
3726 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3727 exsltDateDayOfWeekInMonthFunction);
3728 xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
3729 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3730 exsltDateDifferenceFunction);
3731 xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
3732 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3733 exsltDateDurationFunction);
3734 xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
3735 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3736 exsltDateHourInDayFunction);
3737 xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
3738 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3739 exsltDateLeapYearFunction);
3740 xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
3741 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3742 exsltDateMinuteInHourFunction);
3743 xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
3744 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3745 exsltDateMonthAbbreviationFunction);
3746 xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
3747 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3748 exsltDateMonthInYearFunction);
3749 xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
3750 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3751 exsltDateMonthNameFunction);
3752 xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
3753 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3754 exsltDateSecondInMinuteFunction);
3755 xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
3756 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3757 exsltDateSecondsFunction);
3758 xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
3759 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3760 exsltDateSumFunction);
3761 xsltRegisterExtModuleFunction ((const xmlChar *) "time",
3762 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3763 exsltDateTimeFunction);
3764 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
3765 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3766 exsltDateWeekInMonthFunction);
3767 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
3768 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3769 exsltDateWeekInYearFunction);
3770 xsltRegisterExtModuleFunction ((const xmlChar *) "year",
3771 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3772 exsltDateYearFunction);