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;
790 ret->value.date.tzo = (((ret->value.date.day * 1440) +
791 (ret->value.date.hour * 60) +
792 ret->value.date.min) -
793 ((gmTm->tm_mday * 1440) + (gmTm->tm_hour * 60) +
802 * @dateTime: string to analyze
804 * Parses a date/time string
806 * Returns a newly built #exsltDateValPtr of NULL in case of error
808 static exsltDateValPtr
809 exsltDateParse (const xmlChar *dateTime)
813 const xmlChar *cur = dateTime;
815 #define RETURN_TYPE_IF_VALID(t) \
816 if (IS_TZO_CHAR(*cur)) { \
817 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \
826 if (dateTime == NULL)
829 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
832 dt = exsltDateCreateDate(EXSLT_UNKNOWN);
836 if ((cur[0] == '-') && (cur[1] == '-')) {
838 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
843 /* is it an xs:gDay? */
846 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
850 RETURN_TYPE_IF_VALID(XS_GDAY);
856 * it should be an xs:gMonthDay or xs:gMonth
858 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
866 /* is it an xs:gMonth? */
869 RETURN_TYPE_IF_VALID(XS_GMONTH);
873 /* it should be an xs:gMonthDay */
874 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
878 RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
884 * It's a right-truncated date or an xs:time.
885 * Try to parse an xs:time then fallback on right-truncated dates.
887 if ((*cur >= '0') && (*cur <= '9')) {
888 ret = _exsltDateParseTime(&(dt->value.date), &cur);
890 /* it's an xs:time */
891 RETURN_TYPE_IF_VALID(XS_TIME);
895 /* fallback on date parsing */
898 ret = _exsltDateParseGYear(&(dt->value.date), &cur);
902 /* is it an xs:gYear? */
903 RETURN_TYPE_IF_VALID(XS_GYEAR);
909 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
913 /* is it an xs:gYearMonth? */
914 RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
920 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
921 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
924 /* is it an xs:date? */
925 RETURN_TYPE_IF_VALID(XS_DATE);
931 /* it should be an xs:dateTime */
932 ret = _exsltDateParseTime(&(dt->value.date), &cur);
936 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
937 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
940 dt->type = XS_DATETIME;
946 exsltDateFreeDate(dt);
951 * exsltDateParseDuration:
952 * @duration: string to analyze
954 * Parses a duration string
956 * Returns a newly built #exsltDateValPtr of NULL in case of error
958 static exsltDateValPtr
959 exsltDateParseDuration (const xmlChar *duration)
961 const xmlChar *cur = duration;
964 unsigned int seq = 0;
966 if (duration == NULL)
974 /* duration must start with 'P' (after sign) */
978 dur = exsltDateCreateDate(XS_DURATION);
984 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
985 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
986 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
988 /* input string should be empty or invalid date/time item */
989 if (seq >= sizeof(desig))
992 /* T designator must be present for time items */
1002 /* parse the number portion of the item */
1003 PARSE_NUM(num, cur, num_type);
1005 if ((num_type == -1) || (*cur == 0))
1008 /* update duration based on item type */
1009 while (seq < sizeof(desig)) {
1010 if (*cur == desig[seq]) {
1012 /* verify numeric type; only seconds can be float */
1013 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
1018 dur->value.dur.mon = (long)num * 12;
1021 dur->value.dur.mon += (long)num;
1024 /* convert to seconds using multiplier */
1025 dur->value.dur.sec += num * multi[seq];
1030 break; /* exit loop */
1032 /* no date designators found? */
1040 dur->value.dur.mon = -dur->value.dur.mon;
1041 dur->value.dur.day = -dur->value.dur.day;
1042 dur->value.dur.sec = -dur->value.dur.sec;
1045 #ifdef DEBUG_EXSLT_DATE
1046 xsltGenericDebug(xsltGenericDebugContext,
1047 "Parsed duration %f\n", dur->value.dur.sec);
1054 exsltDateFreeDate(dur);
1060 * @num: number to format
1061 * @cur: current location to convert number
1063 * @item: char designator
1066 #define FORMAT_ITEM(num, cur, limit, item) \
1068 long comp = (long)num / limit; \
1070 FORMAT_FLOAT((double)comp, cur, 0); \
1072 num -= (double)(comp * limit); \
1077 * exsltDateFormatDuration:
1078 * @dt: an #exsltDateValDurationPtr
1080 * Formats @dt in xs:duration format.
1082 * Returns a newly allocated string, or NULL in case of error
1085 exsltDateFormatDuration (const exsltDateValDurationPtr dt)
1087 xmlChar buf[100], *cur = buf;
1089 double years, months;
1094 /* quick and dirty check */
1095 if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
1096 return xmlStrdup((xmlChar*)"P0D");
1099 days = (double)dt->day;
1100 years = (double)(dt->mon / 12);
1101 months = (double)(dt->mon % 12);
1126 FORMAT_ITEM(years, cur, 1, 'Y');
1129 if (months != 0.0) {
1130 FORMAT_ITEM(months, cur, 1, 'M');
1133 if (secs >= SECS_PER_DAY) {
1134 double tmp = floor(secs / SECS_PER_DAY);
1136 secs -= (tmp * SECS_PER_DAY);
1139 FORMAT_ITEM(days, cur, 1, 'D');
1143 FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
1144 FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
1146 FORMAT_FLOAT(secs, cur, 0);
1152 return xmlStrdup(buf);
1156 * exsltDateFormatDateTime:
1157 * @dt: an #exsltDateValDatePtr
1159 * Formats @dt in xs:dateTime format.
1161 * Returns a newly allocated string, or NULL in case of error
1164 exsltDateFormatDateTime (const exsltDateValDatePtr dt)
1166 xmlChar buf[100], *cur = buf;
1168 if ((dt == NULL) || !VALID_DATETIME(dt))
1171 FORMAT_DATE(dt, cur);
1174 FORMAT_TIME(dt, cur);
1175 FORMAT_TZ(dt->tzo, cur);
1178 return xmlStrdup(buf);
1182 * exsltDateFormatDate:
1183 * @dt: an #exsltDateValDatePtr
1185 * Formats @dt in xs:date format.
1187 * Returns a newly allocated string, or NULL in case of error
1190 exsltDateFormatDate (const exsltDateValDatePtr dt)
1192 xmlChar buf[100], *cur = buf;
1194 if ((dt == NULL) || !VALID_DATETIME(dt))
1197 FORMAT_DATE(dt, cur);
1198 if (dt->tz_flag || (dt->tzo != 0)) {
1199 FORMAT_TZ(dt->tzo, cur);
1203 return xmlStrdup(buf);
1207 * exsltDateFormatTime:
1208 * @dt: an #exsltDateValDatePtr
1210 * Formats @dt in xs:time format.
1212 * Returns a newly allocated string, or NULL in case of error
1215 exsltDateFormatTime (const exsltDateValDatePtr dt)
1217 xmlChar buf[100], *cur = buf;
1219 if ((dt == NULL) || !VALID_TIME(dt))
1222 FORMAT_TIME(dt, cur);
1223 if (dt->tz_flag || (dt->tzo != 0)) {
1224 FORMAT_TZ(dt->tzo, cur);
1228 return xmlStrdup(buf);
1233 * @dt: an #exsltDateValPtr
1235 * Formats @dt in the proper format.
1236 * Note: xs:gmonth and xs:gday are not formatted as there are no
1237 * routines that output them.
1239 * Returns a newly allocated string, or NULL in case of error
1242 exsltDateFormat (const exsltDateValPtr dt)
1250 return exsltDateFormatDuration(&(dt->value.dur));
1252 return exsltDateFormatDateTime(&(dt->value.date));
1254 return exsltDateFormatDate(&(dt->value.date));
1256 return exsltDateFormatTime(&(dt->value.date));
1261 if (dt->type & XS_GYEAR) {
1262 xmlChar buf[20], *cur = buf;
1264 FORMAT_GYEAR(dt->value.date.year, cur);
1265 if (dt->type == XS_GYEARMONTH) {
1268 FORMAT_GMONTH(dt->value.date.mon, cur);
1271 if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
1272 FORMAT_TZ(dt->value.date.tzo, cur);
1275 return xmlStrdup(buf);
1282 * _exsltDateCastYMToDays:
1283 * @dt: an #exsltDateValPtr
1285 * Convert mon and year of @dt to total number of days. Take the
1286 * number of years since (or before) 1 AD and add the number of leap
1287 * years. This is a function because negative
1288 * years must be handled a little differently and there is no zero year.
1290 * Returns number of days.
1293 _exsltDateCastYMToDays (const exsltDateValPtr dt)
1297 if (dt->value.date.year < 0)
1298 ret = (dt->value.date.year * 365) +
1299 (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
1300 ((dt->value.date.year+1)/400)) +
1301 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1303 ret = ((dt->value.date.year-1) * 365) +
1304 (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
1305 ((dt->value.date.year-1)/400)) +
1306 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1313 * @dt: an #exsltDateValPtr
1315 * Calculates the number of seconds in the time portion of @dt.
1319 #define TIME_TO_NUMBER(dt) \
1320 ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
1321 (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
1324 * exsltDateCastDateToNumber:
1325 * @dt: an #exsltDateValPtr
1327 * Calculates the number of seconds from year zero.
1329 * Returns seconds from zero year.
1332 exsltDateCastDateToNumber (const exsltDateValPtr dt)
1339 if ((dt->type & XS_GYEAR) == XS_GYEAR) {
1340 ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
1344 if (dt->type == XS_DURATION) {
1345 ret += (double)dt->value.dur.day * SECS_PER_DAY;
1346 ret += dt->value.dur.sec;
1348 ret += (double)dt->value.date.day * SECS_PER_DAY;
1350 ret += TIME_TO_NUMBER(dt);
1358 * _exsltDateTruncateDate:
1359 * @dt: an #exsltDateValPtr
1360 * @type: dateTime type to set to
1362 * Set @dt to truncated @type.
1364 * Returns 0 success, non-zero otherwise.
1367 _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
1372 if ((type & XS_TIME) != XS_TIME) {
1373 dt->value.date.hour = 0;
1374 dt->value.date.min = 0;
1375 dt->value.date.sec = 0.0;
1378 if ((type & XS_GDAY) != XS_GDAY)
1379 dt->value.date.day = 0;
1381 if ((type & XS_GMONTH) != XS_GMONTH)
1382 dt->value.date.mon = 0;
1384 if ((type & XS_GYEAR) != XS_GYEAR)
1385 dt->value.date.year = 0;
1394 * @yday: year day (1-366)
1397 * Determine the day-in-week from @yday and @yr. 0001-01-01 was
1398 * a Monday so all other days are calculated from there. Take the
1399 * number of years since (or before) add the number of leap years and
1400 * the day-in-year and mod by 7. This is a function because negative
1401 * years must be handled a little differently and there is no zero year.
1403 * Returns day in week (Sunday = 0).
1406 _exsltDateDayInWeek(long yday, long yr)
1411 ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
1415 ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
1421 * macros for adding date/times and durations
1423 #define FQUOTIENT(a,b) ((floor(((double)a/(double)b))))
1424 #define MODULO(a,b) ((a - FQUOTIENT(a,b) * b))
1425 #define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
1426 #define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
1430 * @dt: an #exsltDateValPtr
1431 * @dur: an #exsltDateValPtr of type #XS_DURATION
1433 * Compute a new date/time from @dt and @dur. This function assumes @dt
1434 * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
1436 * Returns date/time pointer or NULL.
1438 static exsltDateValPtr
1439 _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
1441 exsltDateValPtr ret;
1442 long carry, tempdays, temp;
1443 exsltDateValDatePtr r, d;
1444 exsltDateValDurationPtr u;
1446 if ((dt == NULL) || (dur == NULL))
1449 ret = exsltDateCreateDate(dt->type);
1453 r = &(ret->value.date);
1454 d = &(dt->value.date);
1455 u = &(dur->value.dur);
1461 /* normalize for time zone offset */
1462 u->sec -= (d->tzo * 60); /* changed from + to - (bug 153000) */
1470 carry = d->mon + u->mon;
1471 r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
1472 carry = (long)FQUOTIENT_RANGE(carry, 1, 13);
1474 /* year (may be modified later) */
1475 r->year = d->year + carry;
1485 r->tz_flag = d->tz_flag;
1488 r->sec = d->sec + u->sec;
1489 carry = (long)FQUOTIENT((long)r->sec, 60);
1490 if (r->sec != 0.0) {
1491 r->sec = MODULO(r->sec, 60.0);
1496 r->min = (unsigned int)MODULO(carry, 60);
1497 carry = (long)FQUOTIENT(carry, 60);
1501 r->hour = (unsigned int)MODULO(carry, 24);
1502 carry = (long)FQUOTIENT(carry, 24);
1506 * Note we use tempdays because the temporary values may need more
1509 if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
1510 (d->day > MAX_DAYINMONTH(r->year, r->mon)))
1511 tempdays = MAX_DAYINMONTH(r->year, r->mon);
1512 else if (d->day < 1)
1517 tempdays += u->day + carry;
1521 long tmon = (long)MODULO_RANGE((int)r->mon-1, 1, 13);
1522 long tyr = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13);
1526 * Coverity detected an overrun in daysInMonth
1527 * of size 12 at position 12 with index variable "((r)->mon - 1)"
1533 tempdays += MAX_DAYINMONTH(tyr, tmon);
1535 } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
1536 tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
1541 temp = r->mon + carry;
1542 r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
1543 r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
1555 * adjust the date/time type to the date values
1557 if (ret->type != XS_DATETIME) {
1558 if ((r->hour) || (r->min) || (r->sec))
1559 ret->type = XS_DATETIME;
1560 else if (ret->type != XS_DATE) {
1561 if ((r->mon != 1) && (r->day != 1))
1562 ret->type = XS_DATE;
1563 else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
1564 ret->type = XS_GYEARMONTH;
1572 * exsltDateNormalize:
1573 * @dt: an #exsltDateValPtr
1575 * Normalize @dt to GMT time.
1579 exsltDateNormalize (exsltDateValPtr dt)
1581 exsltDateValPtr dur, tmp;
1586 if (((dt->type & XS_TIME) != XS_TIME) || (dt->value.date.tzo == 0))
1589 dur = exsltDateCreateDate(XS_DURATION);
1593 tmp = _exsltDateAdd(dt, dur);
1597 memcpy(dt, tmp, sizeof(exsltDateVal));
1599 exsltDateFreeDate(tmp);
1600 exsltDateFreeDate(dur);
1602 dt->value.date.tzo = 0;
1606 * _exsltDateDifference:
1607 * @x: an #exsltDateValPtr
1608 * @y: an #exsltDateValPtr
1609 * @flag: force difference in days
1611 * Calculate the difference between @x and @y as a duration
1612 * (i.e. y - x). If the @flag is set then even if the least specific
1613 * format of @x or @y is xs:gYear or xs:gYearMonth.
1615 * Returns date/time pointer or NULL.
1617 static exsltDateValPtr
1618 _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
1620 exsltDateValPtr ret;
1622 if ((x == NULL) || (y == NULL))
1625 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
1626 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
1629 exsltDateNormalize(x);
1630 exsltDateNormalize(y);
1633 * the operand with the most specific format must be converted to
1634 * the same type as the operand with the least specific format.
1636 if (x->type != y->type) {
1637 if (x->type < y->type) {
1638 _exsltDateTruncateDate(y, x->type);
1640 _exsltDateTruncateDate(x, y->type);
1644 ret = exsltDateCreateDate(XS_DURATION);
1648 if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
1649 /* compute the difference in months */
1650 ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
1651 ((x->value.date.year * 12) + x->value.date.mon);
1652 /* The above will give a wrong result if x and y are on different sides
1653 of the September 1752. Resolution is welcome :-) */
1655 ret->value.dur.day = _exsltDateCastYMToDays(y) -
1656 _exsltDateCastYMToDays(x);
1657 ret->value.dur.day += y->value.date.day - x->value.date.day;
1658 ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
1659 if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
1660 ret->value.dur.day -= 1;
1661 ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
1662 } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
1663 ret->value.dur.day += 1;
1664 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1672 * _exsltDateAddDurCalc
1673 * @ret: an exsltDateValPtr for the return value:
1674 * @x: an exsltDateValPtr for the first operand
1675 * @y: an exsltDateValPtr for the second operand
1677 * Add two durations, catering for possible negative values.
1678 * The sum is placed in @ret.
1680 * Returns 1 for success, 0 if error detected.
1683 _exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
1689 ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
1692 ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
1693 carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
1694 if (ret->value.dur.sec != 0.0) {
1695 ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
1697 * Our function MODULO always gives us a positive value, so
1698 * if we end up with a "-ve" carry we need to adjust it
1699 * appropriately (bug 154021)
1701 if ((carry < 0) && (ret->value.dur.sec != 0)) {
1702 /* change seconds to equiv negative modulus */
1703 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1709 ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
1712 * are the results indeterminate? i.e. how do you subtract days from
1715 if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
1716 (ret->value.dur.mon < 0)) ||
1717 (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
1718 (ret->value.dur.mon > 0))) {
1725 * _exsltDateAddDuration:
1726 * @x: an #exsltDateValPtr of type #XS_DURATION
1727 * @y: an #exsltDateValPtr of type #XS_DURATION
1729 * Compute a new duration from @x and @y.
1731 * Returns date/time pointer or NULL.
1733 static exsltDateValPtr
1734 _exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
1736 exsltDateValPtr ret;
1738 if ((x == NULL) || (y == NULL))
1741 ret = exsltDateCreateDate(XS_DURATION);
1745 if (_exsltDateAddDurCalc(ret, x, y))
1748 exsltDateFreeDate(ret);
1752 /****************************************************************
1754 * EXSLT - Dates and Times functions *
1756 ****************************************************************/
1759 * exsltDateDateTime:
1761 * Implements the EXSLT - Dates and Times date-time() function:
1762 * string date:date-time()
1764 * Returns the current date and time as a date/time string.
1767 exsltDateDateTime (void)
1769 xmlChar *ret = NULL;
1771 exsltDateValPtr cur;
1773 cur = exsltDateCurrent();
1775 ret = exsltDateFormatDateTime(&(cur->value.date));
1776 exsltDateFreeDate(cur);
1785 * @dateTime: a date/time string
1787 * Implements the EXSLT - Dates and Times date() function:
1788 * string date:date (string?)
1790 * Returns the date specified in the date/time string given as the
1791 * argument. If no argument is given, then the current local
1792 * date/time, as returned by date:date-time is used as a default
1794 * The date/time string specified as an argument must be a string in
1795 * the format defined as the lexical representation of either
1796 * xs:dateTime or xs:date. If the argument is not in either of these
1797 * formats, returns NULL.
1800 exsltDateDate (const xmlChar *dateTime)
1802 exsltDateValPtr dt = NULL;
1803 xmlChar *ret = NULL;
1805 if (dateTime == NULL) {
1807 dt = exsltDateCurrent();
1812 dt = exsltDateParse(dateTime);
1815 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1816 exsltDateFreeDate(dt);
1821 ret = exsltDateFormatDate(&(dt->value.date));
1822 exsltDateFreeDate(dt);
1829 * @dateTime: a date/time string
1831 * Implements the EXSLT - Dates and Times time() function:
1832 * string date:time (string?)
1834 * Returns the time specified in the date/time string given as the
1835 * argument. If no argument is given, then the current local
1836 * date/time, as returned by date:date-time is used as a default
1838 * The date/time string specified as an argument must be a string in
1839 * the format defined as the lexical representation of either
1840 * xs:dateTime or xs:time. If the argument is not in either of these
1841 * formats, returns NULL.
1844 exsltDateTime (const xmlChar *dateTime)
1846 exsltDateValPtr dt = NULL;
1847 xmlChar *ret = NULL;
1849 if (dateTime == NULL) {
1851 dt = exsltDateCurrent();
1856 dt = exsltDateParse(dateTime);
1859 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1860 exsltDateFreeDate(dt);
1865 ret = exsltDateFormatTime(&(dt->value.date));
1866 exsltDateFreeDate(dt);
1873 * @dateTime: a date/time string
1875 * Implements the EXSLT - Dates and Times year() function
1876 * number date:year (string?)
1877 * Returns the year of a date as a number. If no argument is given,
1878 * then the current local date/time, as returned by date:date-time is
1879 * used as a default argument.
1880 * The date/time string specified as the first argument must be a
1881 * right-truncated string in the format defined as the lexical
1882 * representation of xs:dateTime in one of the formats defined in [XML
1883 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1884 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1885 * - xs:date (CCYY-MM-DD)
1886 * - xs:gYearMonth (CCYY-MM)
1888 * If the date/time string is not in one of these formats, then NaN is
1892 exsltDateYear (const xmlChar *dateTime)
1897 if (dateTime == NULL) {
1899 dt = exsltDateCurrent();
1904 dt = exsltDateParse(dateTime);
1907 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1908 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
1909 exsltDateFreeDate(dt);
1914 ret = (double) dt->value.date.year;
1915 exsltDateFreeDate(dt);
1921 * exsltDateLeapYear:
1922 * @dateTime: a date/time string
1924 * Implements the EXSLT - Dates and Times leap-year() function:
1925 * boolean date:leap-yea (string?)
1926 * Returns true if the year given in a date is a leap year. If no
1927 * argument is given, then the current local date/time, as returned by
1928 * date:date-time is used as a default argument.
1929 * The date/time string specified as the first argument must be a
1930 * right-truncated string in the format defined as the lexical
1931 * representation of xs:dateTime in one of the formats defined in [XML
1932 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1933 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1934 * - xs:date (CCYY-MM-DD)
1935 * - xs:gYearMonth (CCYY-MM)
1937 * If the date/time string is not in one of these formats, then NaN is
1940 static xmlXPathObjectPtr
1941 exsltDateLeapYear (const xmlChar *dateTime)
1945 year = exsltDateYear(dateTime);
1946 if (xmlXPathIsNaN(year))
1947 return xmlXPathNewFloat(xmlXPathNAN);
1949 if (IS_LEAP((long)year))
1950 return xmlXPathNewBoolean(1);
1952 return xmlXPathNewBoolean(0);
1956 * exsltDateMonthInYear:
1957 * @dateTime: a date/time string
1959 * Implements the EXSLT - Dates and Times month-in-year() function:
1960 * number date:month-in-year (string?)
1961 * Returns the month of a date as a number. If no argument is given,
1962 * then the current local date/time, as returned by date:date-time is
1963 * used the default argument.
1964 * The date/time string specified as the argument is a left or
1965 * right-truncated string in the format defined as the lexical
1966 * representation of xs:dateTime in one of the formats defined in [XML
1967 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1968 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1969 * - xs:date (CCYY-MM-DD)
1970 * - xs:gYearMonth (CCYY-MM)
1971 * - xs:gMonth (--MM--)
1972 * - xs:gMonthDay (--MM-DD)
1973 * If the date/time string is not in one of these formats, then NaN is
1977 exsltDateMonthInYear (const xmlChar *dateTime)
1982 if (dateTime == NULL) {
1984 dt = exsltDateCurrent();
1989 dt = exsltDateParse(dateTime);
1992 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1993 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
1994 (dt->type != XS_GMONTHDAY)) {
1995 exsltDateFreeDate(dt);
2000 ret = (double) dt->value.date.mon;
2001 exsltDateFreeDate(dt);
2007 * exsltDateMonthName:
2008 * @dateTime: a date/time string
2010 * Implements the EXSLT - Dates and Time month-name() function
2011 * string date:month-name (string?)
2012 * Returns the full name of the month of a date. If no argument is
2013 * given, then the current local date/time, as returned by
2014 * date:date-time is used the default argument.
2015 * The date/time string specified as the argument is a left or
2016 * right-truncated string in the format defined as the lexical
2017 * representation of xs:dateTime in one of the formats defined in [XML
2018 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2019 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2020 * - xs:date (CCYY-MM-DD)
2021 * - xs:gYearMonth (CCYY-MM)
2022 * - xs:gMonth (--MM--)
2023 * If the date/time string is not in one of these formats, then an
2024 * empty string ('') is returned.
2025 * The result is an English month name: one of 'January', 'February',
2026 * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
2027 * 'October', 'November' or 'December'.
2029 static const xmlChar *
2030 exsltDateMonthName (const xmlChar *dateTime)
2032 static const xmlChar monthNames[13][10] = {
2034 { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
2035 { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
2036 { 'M', 'a', 'r', 'c', 'h', 0 },
2037 { 'A', 'p', 'r', 'i', 'l', 0 },
2038 { 'M', 'a', 'y', 0 },
2039 { 'J', 'u', 'n', 'e', 0 },
2040 { 'J', 'u', 'l', 'y', 0 },
2041 { 'A', 'u', 'g', 'u', 's', 't', 0 },
2042 { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
2043 { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
2044 { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
2045 { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
2048 month = (int) exsltDateMonthInYear(dateTime);
2049 if (!VALID_MONTH(month))
2051 return monthNames[month];
2055 * exsltDateMonthAbbreviation:
2056 * @dateTime: a date/time string
2058 * Implements the EXSLT - Dates and Time month-abbreviation() function
2059 * string date:month-abbreviation (string?)
2060 * Returns the abbreviation of the month of a date. If no argument is
2061 * given, then the current local date/time, as returned by
2062 * date:date-time is used the default argument.
2063 * The date/time string specified as the argument is a left or
2064 * right-truncated string in the format defined as the lexical
2065 * representation of xs:dateTime in one of the formats defined in [XML
2066 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2067 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2068 * - xs:date (CCYY-MM-DD)
2069 * - xs:gYearMonth (CCYY-MM)
2070 * - xs:gMonth (--MM--)
2071 * If the date/time string is not in one of these formats, then an
2072 * empty string ('') is returned.
2073 * The result is an English month abbreviation: one of 'Jan', 'Feb',
2074 * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
2077 static const xmlChar *
2078 exsltDateMonthAbbreviation (const xmlChar *dateTime)
2080 static const xmlChar monthAbbreviations[13][4] = {
2082 { 'J', 'a', 'n', 0 },
2083 { 'F', 'e', 'b', 0 },
2084 { 'M', 'a', 'r', 0 },
2085 { 'A', 'p', 'r', 0 },
2086 { 'M', 'a', 'y', 0 },
2087 { 'J', 'u', 'n', 0 },
2088 { 'J', 'u', 'l', 0 },
2089 { 'A', 'u', 'g', 0 },
2090 { 'S', 'e', 'p', 0 },
2091 { 'O', 'c', 't', 0 },
2092 { 'N', 'o', 'v', 0 },
2093 { 'D', 'e', 'c', 0 }
2096 month = (int) exsltDateMonthInYear(dateTime);
2097 if(!VALID_MONTH(month))
2099 return monthAbbreviations[month];
2103 * exsltDateWeekInYear:
2104 * @dateTime: a date/time string
2106 * Implements the EXSLT - Dates and Times week-in-year() function
2107 * number date:week-in-year (string?)
2108 * Returns the week of the year as a number. If no argument is given,
2109 * then the current local date/time, as returned by date:date-time is
2110 * used as the default argument. For the purposes of numbering,
2111 * counting follows ISO 8601: week 1 in a year is the week containing
2112 * the first Thursday of the year, with new weeks beginning on a
2114 * The date/time string specified as the argument is a right-truncated
2115 * string in the format defined as the lexical representation of
2116 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2117 * Datatypes]. The permitted formats are as follows:
2118 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2119 * - xs:date (CCYY-MM-DD)
2120 * If the date/time string is not in one of these formats, then NaN is
2124 exsltDateWeekInYear (const xmlChar *dateTime)
2127 long fdiy, fdiw, ret;
2129 if (dateTime == NULL) {
2131 dt = exsltDateCurrent();
2136 dt = exsltDateParse(dateTime);
2139 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2140 exsltDateFreeDate(dt);
2145 fdiy = DAY_IN_YEAR(1, 1, dt->value.date.year);
2148 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2149 * is the first day-in-week
2151 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2153 ret = (DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2154 dt->value.date.year) + fdiw) / 7;
2156 /* ISO 8601 adjustment, 3 is Thu */
2160 exsltDateFreeDate(dt);
2162 return (double) ret;
2166 * exsltDateWeekInMonth:
2167 * @dateTime: a date/time string
2169 * Implements the EXSLT - Dates and Times week-in-month() function
2170 * number date:week-in-month (string?)
2171 * The date:week-in-month function returns the week in a month of a
2172 * date as a number. If no argument is given, then the current local
2173 * date/time, as returned by date:date-time is used the default
2174 * argument. For the purposes of numbering, the first day of the month
2175 * is in week 1 and new weeks begin on a Monday (so the first and last
2176 * weeks in a month will often have less than 7 days in them).
2177 * The date/time string specified as the argument is a right-truncated
2178 * string in the format defined as the lexical representation of
2179 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2180 * Datatypes]. The permitted formats are as follows:
2181 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2182 * - xs:date (CCYY-MM-DD)
2183 * If the date/time string is not in one of these formats, then NaN is
2187 exsltDateWeekInMonth (const xmlChar *dateTime)
2190 long fdiy, fdiw, ret;
2192 if (dateTime == NULL) {
2194 dt = exsltDateCurrent();
2199 dt = exsltDateParse(dateTime);
2202 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2203 exsltDateFreeDate(dt);
2208 fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
2210 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2211 * is the first day-in-week
2213 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2215 ret = ((dt->value.date.day + fdiw - 1) / 7) + 1;
2217 exsltDateFreeDate(dt);
2219 return (double) ret;
2223 * exsltDateDayInYear:
2224 * @dateTime: a date/time string
2226 * Implements the EXSLT - Dates and Times day-in-year() function
2227 * number date:day-in-year (string?)
2228 * Returns the day of a date in a year as a number. If no argument is
2229 * given, then the current local date/time, as returned by
2230 * date:date-time is used the default argument.
2231 * The date/time string specified as the argument is a right-truncated
2232 * string in the format defined as the lexical representation of
2233 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2234 * Datatypes]. The permitted formats are as follows:
2235 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2236 * - xs:date (CCYY-MM-DD)
2237 * If the date/time string is not in one of these formats, then NaN is
2241 exsltDateDayInYear (const xmlChar *dateTime)
2246 if (dateTime == NULL) {
2248 dt = exsltDateCurrent();
2253 dt = exsltDateParse(dateTime);
2256 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2257 exsltDateFreeDate(dt);
2262 ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2263 dt->value.date.year);
2265 exsltDateFreeDate(dt);
2267 return (double) ret;
2271 * exsltDateDayInMonth:
2272 * @dateTime: a date/time string
2274 * Implements the EXSLT - Dates and Times day-in-month() function:
2275 * number date:day-in-month (string?)
2276 * Returns the day of a date as a number. If no argument is given,
2277 * then the current local date/time, as returned by date:date-time is
2278 * used the default argument.
2279 * The date/time string specified as the argument is a left or
2280 * right-truncated string in the format defined as the lexical
2281 * representation of xs:dateTime in one of the formats defined in [XML
2282 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2283 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2284 * - xs:date (CCYY-MM-DD)
2285 * - xs:gMonthDay (--MM-DD)
2287 * If the date/time string is not in one of these formats, then NaN is
2291 exsltDateDayInMonth (const xmlChar *dateTime)
2296 if (dateTime == NULL) {
2298 dt = exsltDateCurrent();
2303 dt = exsltDateParse(dateTime);
2306 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
2307 (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
2308 exsltDateFreeDate(dt);
2313 ret = (double) dt->value.date.day;
2314 exsltDateFreeDate(dt);
2320 * exsltDateDayOfWeekInMonth:
2321 * @dateTime: a date/time string
2323 * Implements the EXSLT - Dates and Times day-of-week-in-month() function:
2324 * number date:day-of-week-in-month (string?)
2325 * Returns the day-of-the-week in a month of a date as a number
2326 * (e.g. 3 for the 3rd Tuesday in May). If no argument is
2327 * given, then the current local date/time, as returned by
2328 * date:date-time is used the default argument.
2329 * The date/time string specified as the argument is a right-truncated
2330 * string in the format defined as the lexical representation of
2331 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2332 * Datatypes]. The permitted formats are as follows:
2333 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2334 * - xs:date (CCYY-MM-DD)
2335 * If the date/time string is not in one of these formats, then NaN is
2339 exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
2344 if (dateTime == NULL) {
2346 dt = exsltDateCurrent();
2351 dt = exsltDateParse(dateTime);
2354 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2355 exsltDateFreeDate(dt);
2360 ret = ((dt->value.date.day -1) / 7) + 1;
2362 exsltDateFreeDate(dt);
2364 return (double) ret;
2368 * exsltDateDayInWeek:
2369 * @dateTime: a date/time string
2371 * Implements the EXSLT - Dates and Times day-in-week() function:
2372 * number date:day-in-week (string?)
2373 * Returns the day of the week given in a date as a number. If no
2374 * argument is given, then the current local date/time, as returned by
2375 * date:date-time is used the default argument.
2376 * The date/time string specified as the argument is a left or
2377 * right-truncated string in the format defined as the lexical
2378 * representation of xs:dateTime in one of the formats defined in [XML
2379 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2380 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2381 * - xs:date (CCYY-MM-DD)
2382 * If the date/time string is not in one of these formats, then NaN is
2384 * The numbering of days of the week starts at 1 for Sunday, 2 for
2385 * Monday and so on up to 7 for Saturday.
2388 exsltDateDayInWeek (const xmlChar *dateTime)
2393 if (dateTime == NULL) {
2395 dt = exsltDateCurrent();
2400 dt = exsltDateParse(dateTime);
2403 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2404 exsltDateFreeDate(dt);
2409 diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2410 dt->value.date.year);
2412 ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
2414 exsltDateFreeDate(dt);
2416 return (double) ret;
2421 * @dateTime: a date/time string
2423 * Implements the EXSLT - Dates and Time day-name() function
2424 * string date:day-name (string?)
2425 * Returns the full name of the day of the week of a date. If no
2426 * argument is given, then the current local date/time, as returned by
2427 * date:date-time is used the default argument.
2428 * The date/time string specified as the argument is a left or
2429 * right-truncated string in the format defined as the lexical
2430 * representation of xs:dateTime in one of the formats defined in [XML
2431 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2432 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2433 * - xs:date (CCYY-MM-DD)
2434 * If the date/time string is not in one of these formats, then an
2435 * empty string ('') is returned.
2436 * The result is an English day name: one of 'Sunday', 'Monday',
2437 * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
2439 static const xmlChar *
2440 exsltDateDayName (const xmlChar *dateTime)
2442 static const xmlChar dayNames[8][10] = {
2444 { 'S', 'u', 'n', 'd', 'a', 'y', 0 },
2445 { 'M', 'o', 'n', 'd', 'a', 'y', 0 },
2446 { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
2447 { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
2448 { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
2449 { 'F', 'r', 'i', 'd', 'a', 'y', 0 },
2450 { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
2453 day = (int) exsltDateDayInWeek(dateTime);
2454 if((day < 1) || (day > 7))
2456 return dayNames[day];
2460 * exsltDateDayAbbreviation:
2461 * @dateTime: a date/time string
2463 * Implements the EXSLT - Dates and Time day-abbreviation() function
2464 * string date:day-abbreviation (string?)
2465 * Returns the abbreviation of the day of the week of a date. If no
2466 * argument is given, then the current local date/time, as returned by
2467 * date:date-time is used the default argument.
2468 * The date/time string specified as the argument is a left or
2469 * right-truncated string in the format defined as the lexical
2470 * representation of xs:dateTime in one of the formats defined in [XML
2471 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2472 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2473 * - xs:date (CCYY-MM-DD)
2474 * If the date/time string is not in one of these formats, then an
2475 * empty string ('') is returned.
2476 * The result is a three-letter English day abbreviation: one of
2477 * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
2479 static const xmlChar *
2480 exsltDateDayAbbreviation (const xmlChar *dateTime)
2482 static const xmlChar dayAbbreviations[8][4] = {
2484 { 'S', 'u', 'n', 0 },
2485 { 'M', 'o', 'n', 0 },
2486 { 'T', 'u', 'e', 0 },
2487 { 'W', 'e', 'd', 0 },
2488 { 'T', 'h', 'u', 0 },
2489 { 'F', 'r', 'i', 0 },
2490 { 'S', 'a', 't', 0 }
2493 day = (int) exsltDateDayInWeek(dateTime);
2494 if((day < 1) || (day > 7))
2496 return dayAbbreviations[day];
2500 * exsltDateHourInDay:
2501 * @dateTime: a date/time string
2503 * Implements the EXSLT - Dates and Times day-in-month() function:
2504 * number date:day-in-month (string?)
2505 * Returns the hour of the day as a number. If no argument is given,
2506 * then the current local date/time, as returned by date:date-time is
2507 * used the default argument.
2508 * The date/time string specified as the argument is a left or
2509 * right-truncated string in the format defined as the lexical
2510 * representation of xs:dateTime in one of the formats defined in [XML
2511 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2512 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2513 * - xs:time (hh:mm:ss)
2514 * If the date/time string is not in one of these formats, then NaN is
2518 exsltDateHourInDay (const xmlChar *dateTime)
2523 if (dateTime == NULL) {
2525 dt = exsltDateCurrent();
2530 dt = exsltDateParse(dateTime);
2533 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2534 exsltDateFreeDate(dt);
2539 ret = (double) dt->value.date.hour;
2540 exsltDateFreeDate(dt);
2546 * exsltDateMinuteInHour:
2547 * @dateTime: a date/time string
2549 * Implements the EXSLT - Dates and Times day-in-month() function:
2550 * number date:day-in-month (string?)
2551 * Returns the minute of the hour as a number. If no argument is
2552 * given, then the current local date/time, as returned by
2553 * date:date-time is used the default argument.
2554 * The date/time string specified as the argument is a left or
2555 * right-truncated string in the format defined as the lexical
2556 * representation of xs:dateTime in one of the formats defined in [XML
2557 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2558 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2559 * - xs:time (hh:mm:ss)
2560 * If the date/time string is not in one of these formats, then NaN is
2564 exsltDateMinuteInHour (const xmlChar *dateTime)
2569 if (dateTime == NULL) {
2571 dt = exsltDateCurrent();
2576 dt = exsltDateParse(dateTime);
2579 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2580 exsltDateFreeDate(dt);
2585 ret = (double) dt->value.date.min;
2586 exsltDateFreeDate(dt);
2592 * exsltDateSecondInMinute:
2593 * @dateTime: a date/time string
2595 * Implements the EXSLT - Dates and Times second-in-minute() function:
2596 * number date:day-in-month (string?)
2597 * Returns the second of the minute as a number. If no argument is
2598 * given, then the current local date/time, as returned by
2599 * date:date-time is used the default argument.
2600 * The date/time string specified as the argument is a left or
2601 * right-truncated string in the format defined as the lexical
2602 * representation of xs:dateTime in one of the formats defined in [XML
2603 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2604 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2605 * - xs:time (hh:mm:ss)
2606 * If the date/time string is not in one of these formats, then NaN is
2609 * Returns the second or NaN.
2612 exsltDateSecondInMinute (const xmlChar *dateTime)
2617 if (dateTime == NULL) {
2619 dt = exsltDateCurrent();
2624 dt = exsltDateParse(dateTime);
2627 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2628 exsltDateFreeDate(dt);
2633 ret = dt->value.date.sec;
2634 exsltDateFreeDate(dt);
2641 * @xstr: date/time string
2642 * @ystr: date/time string
2644 * Implements the date:add (string,string) function which returns the
2645 * date/time * resulting from adding a duration to a date/time.
2646 * The first argument (@xstr) must be right-truncated date/time
2647 * strings in one of the formats defined in [XML Schema Part 2:
2648 * Datatypes]. The permitted formats are as follows:
2649 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2650 * - xs:date (CCYY-MM-DD)
2651 * - xs:gYearMonth (CCYY-MM)
2653 * The second argument (@ystr) is a string in the format defined for
2654 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2655 * The return value is a right-truncated date/time strings in one of
2656 * the formats defined in [XML Schema Part 2: Datatypes] and listed
2657 * above. This value is calculated using the algorithm described in
2658 * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
2661 * Returns date/time string or NULL.
2664 exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
2666 exsltDateValPtr dt, dur, res;
2669 if ((xstr == NULL) || (ystr == NULL))
2672 dt = exsltDateParse(xstr);
2675 else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
2676 exsltDateFreeDate(dt);
2680 dur = exsltDateParseDuration(ystr);
2682 exsltDateFreeDate(dt);
2686 res = _exsltDateAdd(dt, dur);
2688 exsltDateFreeDate(dt);
2689 exsltDateFreeDate(dur);
2694 ret = exsltDateFormat(res);
2695 exsltDateFreeDate(res);
2701 * exsltDateAddDuration:
2702 * @xstr: first duration string
2703 * @ystr: second duration string
2705 * Implements the date:add-duration (string,string) function which returns
2706 * the duration resulting from adding two durations together.
2707 * Both arguments are strings in the format defined for xs:duration
2708 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
2709 * argument is not in this format, the function returns an empty string
2711 * The return value is a string in the format defined for xs:duration
2712 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2713 * The durations can usually be added by summing the numbers given for
2714 * each of the components in the durations. However, if the durations
2715 * are differently signed, then this sometimes results in durations
2716 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2717 * In these cases, the function returns an empty string ('').
2719 * Returns duration string or NULL.
2722 exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
2724 exsltDateValPtr x, y, res;
2727 if ((xstr == NULL) || (ystr == NULL))
2730 x = exsltDateParseDuration(xstr);
2734 y = exsltDateParseDuration(ystr);
2736 exsltDateFreeDate(x);
2740 res = _exsltDateAddDuration(x, y);
2742 exsltDateFreeDate(x);
2743 exsltDateFreeDate(y);
2748 ret = exsltDateFormatDuration(&(res->value.dur));
2749 exsltDateFreeDate(res);
2755 * exsltDateSumFunction:
2756 * @ns: a node set of duration strings
2758 * The date:sum function adds a set of durations together.
2759 * The string values of the nodes in the node set passed as an argument
2760 * are interpreted as durations and added together as if using the
2761 * date:add-duration function. (from exslt.org)
2763 * The return value is a string in the format defined for xs:duration
2764 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2765 * The durations can usually be added by summing the numbers given for
2766 * each of the components in the durations. However, if the durations
2767 * are differently signed, then this sometimes results in durations
2768 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2769 * In these cases, the function returns an empty string ('').
2771 * Returns duration string or NULL.
2774 exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
2779 exsltDateValPtr x, total;
2784 xmlXPathSetArityError (ctxt);
2788 /* We need to delay the freeing of value->user */
2789 if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
2790 user = ctxt->value->user;
2791 ctxt->value->boolval = 0;
2792 ctxt->value->user = NULL;
2795 ns = xmlXPathPopNodeSet (ctxt);
2796 if (xmlXPathCheckError (ctxt))
2799 if ((ns == NULL) || (ns->nodeNr == 0)) {
2800 xmlXPathReturnEmptyString (ctxt);
2802 xmlXPathFreeNodeSet (ns);
2806 total = exsltDateCreateDate (XS_DURATION);
2807 if (total == NULL) {
2808 xmlXPathFreeNodeSet (ns);
2812 for (i = 0; i < ns->nodeNr; i++) {
2814 tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
2816 xmlXPathFreeNodeSet (ns);
2817 exsltDateFreeDate (total);
2821 x = exsltDateParseDuration (tmp);
2824 exsltDateFreeDate (total);
2825 xmlXPathFreeNodeSet (ns);
2826 xmlXPathReturnEmptyString (ctxt);
2830 result = _exsltDateAddDurCalc(total, total, x);
2832 exsltDateFreeDate (x);
2835 exsltDateFreeDate (total);
2836 xmlXPathFreeNodeSet (ns);
2837 xmlXPathReturnEmptyString (ctxt);
2842 ret = exsltDateFormatDuration (&(total->value.dur));
2843 exsltDateFreeDate (total);
2845 xmlXPathFreeNodeSet (ns);
2847 xmlFreeNodeList ((xmlNodePtr) user);
2850 xmlXPathReturnEmptyString (ctxt);
2852 xmlXPathReturnString (ctxt, ret);
2857 * @dateTime: a date/time string
2859 * Implements the EXSLT - Dates and Times seconds() function:
2860 * number date:seconds(string?)
2861 * The date:seconds function returns the number of seconds specified
2862 * by the argument string. If no argument is given, then the current
2863 * local date/time, as returned by exsltDateCurrent() is used as the
2864 * default argument. If the date/time string is a xs:duration, then the
2865 * years and months must be zero (or not present). Parsing a duration
2866 * converts the fields to seconds. If the date/time string is not a
2867 * duration (and not null), then the legal formats are:
2868 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2869 * - xs:date (CCYY-MM-DD)
2870 * - xs:gYearMonth (CCYY-MM)
2872 * In these cases the difference between the @dateTime and
2873 * 1970-01-01T00:00:00Z is calculated and converted to seconds.
2875 * Note that there was some confusion over whether "difference" meant
2876 * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
2877 * a negative one. After correspondence with exslt.org, it was determined
2878 * that the intent of the specification was to have it positive. The
2879 * coding was modified in July 2003 to reflect this.
2881 * Returns seconds or Nan.
2884 exsltDateSeconds (const xmlChar *dateTime)
2887 double ret = xmlXPathNAN;
2889 if (dateTime == NULL) {
2891 dt = exsltDateCurrent();
2896 dt = exsltDateParseDuration(dateTime);
2898 dt = exsltDateParse(dateTime);
2904 if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
2905 exsltDateValPtr y, dur;
2908 * compute the difference between the given (or current) date
2911 y = exsltDateCreateDate(XS_DATETIME);
2913 y->value.date.year = 1970;
2914 y->value.date.mon = 1;
2915 y->value.date.day = 1;
2916 y->value.date.tz_flag = 1;
2918 dur = _exsltDateDifference(y, dt, 1);
2920 ret = exsltDateCastDateToNumber(dur);
2921 exsltDateFreeDate(dur);
2923 exsltDateFreeDate(y);
2926 } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
2927 ret = exsltDateCastDateToNumber(dt);
2929 exsltDateFreeDate(dt);
2935 * exsltDateDifference:
2936 * @xstr: date/time string
2937 * @ystr: date/time string
2939 * Implements the date:difference (string,string) function which returns
2940 * the duration between the first date and the second date. If the first
2941 * date occurs before the second date, then the result is a positive
2942 * duration; if it occurs after the second date, the result is a
2943 * negative duration. The two dates must both be right-truncated
2944 * date/time strings in one of the formats defined in [XML Schema Part
2945 * 2: Datatypes]. The date/time with the most specific format (i.e. the
2946 * least truncation) is converted into the same format as the date with
2947 * the least specific format (i.e. the most truncation). The permitted
2948 * formats are as follows, from most specific to least specific:
2949 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2950 * - xs:date (CCYY-MM-DD)
2951 * - xs:gYearMonth (CCYY-MM)
2953 * If either of the arguments is not in one of these formats,
2954 * date:difference returns the empty string ('').
2955 * The difference between the date/times is returned as a string in the
2956 * format defined for xs:duration in [3.2.6 duration] of [XML Schema
2957 * Part 2: Datatypes].
2958 * If the date/time string with the least specific format is in either
2959 * xs:gYearMonth or xs:gYear format, then the number of days, hours,
2960 * minutes and seconds in the duration string must be equal to zero.
2961 * (The format of the string will be PnYnM.) The number of months
2962 * specified in the duration must be less than 12.
2963 * Otherwise, the number of years and months in the duration string
2964 * must be equal to zero. (The format of the string will be
2965 * PnDTnHnMnS.) The number of seconds specified in the duration string
2966 * must be less than 60; the number of minutes must be less than 60;
2967 * the number of hours must be less than 24.
2969 * Returns duration string or NULL.
2972 exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
2974 exsltDateValPtr x, y, dur;
2975 xmlChar *ret = NULL;
2977 if ((xstr == NULL) || (ystr == NULL))
2980 x = exsltDateParse(xstr);
2984 y = exsltDateParse(ystr);
2986 exsltDateFreeDate(x);
2990 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
2991 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) {
2992 exsltDateFreeDate(x);
2993 exsltDateFreeDate(y);
2997 dur = _exsltDateDifference(x, y, 0);
2999 exsltDateFreeDate(x);
3000 exsltDateFreeDate(y);
3005 ret = exsltDateFormatDuration(&(dur->value.dur));
3006 exsltDateFreeDate(dur);
3012 * exsltDateDuration:
3013 * @number: a xmlChar string
3015 * Implements the The date:duration function returns a duration string
3016 * representing the number of seconds specified by the argument string.
3017 * If no argument is given, then the result of calling date:seconds
3018 * without any arguments is used as a default argument.
3019 * The duration is returned as a string in the format defined for
3020 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
3021 * The number of years and months in the duration string must be equal
3022 * to zero. (The format of the string will be PnDTnHnMnS.) The number
3023 * of seconds specified in the duration string must be less than 60;
3024 * the number of minutes must be less than 60; the number of hours must
3026 * If the argument is Infinity, -Infinity or NaN, then date:duration
3027 * returns an empty string ('').
3029 * Returns duration string or NULL.
3032 exsltDateDuration (const xmlChar *number)
3034 exsltDateValPtr dur;
3039 secs = exsltDateSeconds(number);
3041 secs = xmlXPathCastStringToNumber(number);
3043 if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
3046 dur = exsltDateCreateDate(XS_DURATION);
3050 dur->value.dur.sec = secs;
3052 ret = exsltDateFormatDuration(&(dur->value.dur));
3053 exsltDateFreeDate(dur);
3058 /****************************************************************
3060 * Wrappers for use by the XPath engine *
3062 ****************************************************************/
3066 * exsltDateDateTimeFunction:
3067 * @ctxt: an XPath parser context
3068 * @nargs : the number of arguments
3070 * Wraps exsltDateDateTime() for use by the XPath engine.
3073 exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3078 xmlXPathSetArityError(ctxt);
3082 ret = exsltDateDateTime();
3084 xmlXPathReturnEmptyString(ctxt);
3086 xmlXPathReturnString(ctxt, ret);
3091 * exsltDateDateFunction:
3092 * @ctxt: an XPath parser context
3093 * @nargs : the number of arguments
3095 * Wraps exsltDateDate() for use by the XPath engine.
3098 exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
3100 xmlChar *ret, *dt = NULL;
3102 if ((nargs < 0) || (nargs > 1)) {
3103 xmlXPathSetArityError(ctxt);
3107 dt = xmlXPathPopString(ctxt);
3108 if (xmlXPathCheckError(ctxt)) {
3109 xmlXPathSetTypeError(ctxt);
3114 ret = exsltDateDate(dt);
3117 xsltGenericDebug(xsltGenericDebugContext,
3118 "{http://exslt.org/dates-and-times}date: "
3119 "invalid date or format %s\n", dt);
3120 xmlXPathReturnEmptyString(ctxt);
3122 xmlXPathReturnString(ctxt, ret);
3130 * exsltDateTimeFunction:
3131 * @ctxt: an XPath parser context
3132 * @nargs : the number of arguments
3134 * Wraps exsltDateTime() for use by the XPath engine.
3137 exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3139 xmlChar *ret, *dt = NULL;
3141 if ((nargs < 0) || (nargs > 1)) {
3142 xmlXPathSetArityError(ctxt);
3146 dt = xmlXPathPopString(ctxt);
3147 if (xmlXPathCheckError(ctxt)) {
3148 xmlXPathSetTypeError(ctxt);
3153 ret = exsltDateTime(dt);
3156 xsltGenericDebug(xsltGenericDebugContext,
3157 "{http://exslt.org/dates-and-times}time: "
3158 "invalid date or format %s\n", dt);
3159 xmlXPathReturnEmptyString(ctxt);
3161 xmlXPathReturnString(ctxt, ret);
3169 * exsltDateYearFunction:
3170 * @ctxt: an XPath parser context
3171 * @nargs : the number of arguments
3173 * Wraps exsltDateYear() for use by the XPath engine.
3176 exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3181 if ((nargs < 0) || (nargs > 1)) {
3182 xmlXPathSetArityError(ctxt);
3187 dt = xmlXPathPopString(ctxt);
3188 if (xmlXPathCheckError(ctxt)) {
3189 xmlXPathSetTypeError(ctxt);
3194 ret = exsltDateYear(dt);
3199 xmlXPathReturnNumber(ctxt, ret);
3203 * exsltDateLeapYearFunction:
3204 * @ctxt: an XPath parser context
3205 * @nargs : the number of arguments
3207 * Wraps exsltDateLeapYear() for use by the XPath engine.
3210 exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3213 xmlXPathObjectPtr ret;
3215 if ((nargs < 0) || (nargs > 1)) {
3216 xmlXPathSetArityError(ctxt);
3221 dt = xmlXPathPopString(ctxt);
3222 if (xmlXPathCheckError(ctxt)) {
3223 xmlXPathSetTypeError(ctxt);
3228 ret = exsltDateLeapYear(dt);
3233 valuePush(ctxt, ret);
3236 #define X_IN_Y(x, y) \
3238 exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \
3240 xmlChar *dt = NULL; \
3243 if ((nargs < 0) || (nargs > 1)) { \
3244 xmlXPathSetArityError(ctxt); \
3249 dt = xmlXPathPopString(ctxt); \
3250 if (xmlXPathCheckError(ctxt)) { \
3251 xmlXPathSetTypeError(ctxt); \
3256 ret = exsltDate##x##In##y(dt); \
3261 xmlXPathReturnNumber(ctxt, ret); \
3265 * exsltDateMonthInYearFunction:
3266 * @ctxt: an XPath parser context
3267 * @nargs : the number of arguments
3269 * Wraps exsltDateMonthInYear() for use by the XPath engine.
3274 * exsltDateMonthNameFunction:
3275 * @ctxt: an XPath parser context
3276 * @nargs : the number of arguments
3278 * Wraps exsltDateMonthName() for use by the XPath engine.
3281 exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3286 if ((nargs < 0) || (nargs > 1)) {
3287 xmlXPathSetArityError(ctxt);
3292 dt = xmlXPathPopString(ctxt);
3293 if (xmlXPathCheckError(ctxt)) {
3294 xmlXPathSetTypeError(ctxt);
3299 ret = exsltDateMonthName(dt);
3305 xmlXPathReturnEmptyString(ctxt);
3307 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3311 * exsltDateMonthAbbreviationFunction:
3312 * @ctxt: an XPath parser context
3313 * @nargs : the number of arguments
3315 * Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
3318 exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3323 if ((nargs < 0) || (nargs > 1)) {
3324 xmlXPathSetArityError(ctxt);
3329 dt = xmlXPathPopString(ctxt);
3330 if (xmlXPathCheckError(ctxt)) {
3331 xmlXPathSetTypeError(ctxt);
3336 ret = exsltDateMonthAbbreviation(dt);
3342 xmlXPathReturnEmptyString(ctxt);
3344 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3348 * exsltDateWeekInYearFunction:
3349 * @ctxt: an XPath parser context
3350 * @nargs : the number of arguments
3352 * Wraps exsltDateWeekInYear() for use by the XPath engine.
3357 * exsltDateWeekInMonthFunction:
3358 * @ctxt: an XPath parser context
3359 * @nargs : the number of arguments
3361 * Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
3366 * exsltDateDayInYearFunction:
3367 * @ctxt: an XPath parser context
3368 * @nargs : the number of arguments
3370 * Wraps exsltDateDayInYear() for use by the XPath engine.
3375 * exsltDateDayInMonthFunction:
3376 * @ctxt: an XPath parser context
3377 * @nargs : the number of arguments
3379 * Wraps exsltDateDayInMonth() for use by the XPath engine.
3384 * exsltDateDayOfWeekInMonthFunction:
3385 * @ctxt: an XPath parser context
3386 * @nargs : the number of arguments
3388 * Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
3390 X_IN_Y(DayOfWeek,Month)
3393 * exsltDateDayInWeekFunction:
3394 * @ctxt: an XPath parser context
3395 * @nargs : the number of arguments
3397 * Wraps exsltDateDayInWeek() for use by the XPath engine.
3402 * exsltDateDayNameFunction:
3403 * @ctxt: an XPath parser context
3404 * @nargs : the number of arguments
3406 * Wraps exsltDateDayName() for use by the XPath engine.
3409 exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3414 if ((nargs < 0) || (nargs > 1)) {
3415 xmlXPathSetArityError(ctxt);
3420 dt = xmlXPathPopString(ctxt);
3421 if (xmlXPathCheckError(ctxt)) {
3422 xmlXPathSetTypeError(ctxt);
3427 ret = exsltDateDayName(dt);
3433 xmlXPathReturnEmptyString(ctxt);
3435 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3439 * exsltDateMonthDayFunction:
3440 * @ctxt: an XPath parser context
3441 * @nargs : the number of arguments
3443 * Wraps exsltDateDayAbbreviation() for use by the XPath engine.
3446 exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3451 if ((nargs < 0) || (nargs > 1)) {
3452 xmlXPathSetArityError(ctxt);
3457 dt = xmlXPathPopString(ctxt);
3458 if (xmlXPathCheckError(ctxt)) {
3459 xmlXPathSetTypeError(ctxt);
3464 ret = exsltDateDayAbbreviation(dt);
3470 xmlXPathReturnEmptyString(ctxt);
3472 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3477 * exsltDateHourInDayFunction:
3478 * @ctxt: an XPath parser context
3479 * @nargs : the number of arguments
3481 * Wraps exsltDateHourInDay() for use by the XPath engine.
3486 * exsltDateMinuteInHourFunction:
3487 * @ctxt: an XPath parser context
3488 * @nargs : the number of arguments
3490 * Wraps exsltDateMinuteInHour() for use by the XPath engine.
3495 * exsltDateSecondInMinuteFunction:
3496 * @ctxt: an XPath parser context
3497 * @nargs : the number of arguments
3499 * Wraps exsltDateSecondInMinute() for use by the XPath engine.
3501 X_IN_Y(Second,Minute)
3504 * exsltDateSecondsFunction:
3505 * @ctxt: an XPath parser context
3506 * @nargs : the number of arguments
3508 * Wraps exsltDateSeconds() for use by the XPath engine.
3511 exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
3513 xmlChar *str = NULL;
3517 xmlXPathSetArityError(ctxt);
3522 str = xmlXPathPopString(ctxt);
3523 if (xmlXPathCheckError(ctxt)) {
3524 xmlXPathSetTypeError(ctxt);
3529 ret = exsltDateSeconds(str);
3533 xmlXPathReturnNumber(ctxt, ret);
3537 * exsltDateAddFunction:
3538 * @ctxt: an XPath parser context
3539 * @nargs: the number of arguments
3541 * Wraps exsltDateAdd() for use by the XPath processor.
3544 exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
3546 xmlChar *ret, *xstr, *ystr;
3549 xmlXPathSetArityError(ctxt);
3552 ystr = xmlXPathPopString(ctxt);
3553 if (xmlXPathCheckError(ctxt))
3556 xstr = xmlXPathPopString(ctxt);
3557 if (xmlXPathCheckError(ctxt)) {
3562 ret = exsltDateAdd(xstr, ystr);
3568 xmlXPathReturnEmptyString(ctxt);
3570 xmlXPathReturnString(ctxt, ret);
3574 * exsltDateAddDurationFunction:
3575 * @ctxt: an XPath parser context
3576 * @nargs: the number of arguments
3578 * Wraps exsltDateAddDuration() for use by the XPath processor.
3581 exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3583 xmlChar *ret, *xstr, *ystr;
3586 xmlXPathSetArityError(ctxt);
3589 ystr = xmlXPathPopString(ctxt);
3590 if (xmlXPathCheckError(ctxt))
3593 xstr = xmlXPathPopString(ctxt);
3594 if (xmlXPathCheckError(ctxt)) {
3599 ret = exsltDateAddDuration(xstr, ystr);
3605 xmlXPathReturnEmptyString(ctxt);
3607 xmlXPathReturnString(ctxt, ret);
3611 * exsltDateDifferenceFunction:
3612 * @ctxt: an XPath parser context
3613 * @nargs: the number of arguments
3615 * Wraps exsltDateDifference() for use by the XPath processor.
3618 exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
3620 xmlChar *ret, *xstr, *ystr;
3623 xmlXPathSetArityError(ctxt);
3626 ystr = xmlXPathPopString(ctxt);
3627 if (xmlXPathCheckError(ctxt))
3630 xstr = xmlXPathPopString(ctxt);
3631 if (xmlXPathCheckError(ctxt)) {
3636 ret = exsltDateDifference(xstr, ystr);
3642 xmlXPathReturnEmptyString(ctxt);
3644 xmlXPathReturnString(ctxt, ret);
3648 * exsltDateDurationFunction:
3649 * @ctxt: an XPath parser context
3650 * @nargs : the number of arguments
3652 * Wraps exsltDateDuration() for use by the XPath engine
3655 exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3658 xmlChar *number = NULL;
3660 if ((nargs < 0) || (nargs > 1)) {
3661 xmlXPathSetArityError(ctxt);
3666 number = xmlXPathPopString(ctxt);
3667 if (xmlXPathCheckError(ctxt)) {
3668 xmlXPathSetTypeError(ctxt);
3673 ret = exsltDateDuration(number);
3679 xmlXPathReturnEmptyString(ctxt);
3681 xmlXPathReturnString(ctxt, ret);
3685 * exsltDateRegister:
3687 * Registers the EXSLT - Dates and Times module
3690 exsltDateRegister (void)
3692 xsltRegisterExtModuleFunction ((const xmlChar *) "add",
3693 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3694 exsltDateAddFunction);
3695 xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
3696 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3697 exsltDateAddDurationFunction);
3698 xsltRegisterExtModuleFunction ((const xmlChar *) "date",
3699 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3700 exsltDateDateFunction);
3702 xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
3703 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3704 exsltDateDateTimeFunction);
3706 xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
3707 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3708 exsltDateDayAbbreviationFunction);
3709 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
3710 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3711 exsltDateDayInMonthFunction);
3712 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
3713 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3714 exsltDateDayInWeekFunction);
3715 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
3716 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3717 exsltDateDayInYearFunction);
3718 xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
3719 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3720 exsltDateDayNameFunction);
3721 xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
3722 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3723 exsltDateDayOfWeekInMonthFunction);
3724 xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
3725 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3726 exsltDateDifferenceFunction);
3727 xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
3728 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3729 exsltDateDurationFunction);
3730 xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
3731 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3732 exsltDateHourInDayFunction);
3733 xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
3734 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3735 exsltDateLeapYearFunction);
3736 xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
3737 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3738 exsltDateMinuteInHourFunction);
3739 xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
3740 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3741 exsltDateMonthAbbreviationFunction);
3742 xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
3743 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3744 exsltDateMonthInYearFunction);
3745 xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
3746 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3747 exsltDateMonthNameFunction);
3748 xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
3749 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3750 exsltDateSecondInMinuteFunction);
3751 xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
3752 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3753 exsltDateSecondsFunction);
3754 xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
3755 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3756 exsltDateSumFunction);
3757 xsltRegisterExtModuleFunction ((const xmlChar *) "time",
3758 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3759 exsltDateTimeFunction);
3760 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
3761 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3762 exsltDateWeekInMonthFunction);
3763 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
3764 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3765 exsltDateWeekInYearFunction);
3766 xsltRegisterExtModuleFunction ((const xmlChar *) "year",
3767 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3768 exsltDateYearFunction);