2 * date.c: Implementation of the EXSLT -- Dates and Times module
5 * http://www.exslt.org/date/date.html
7 * See Copyright for the status of this software.
10 * Charlie Bozeman <cbozeman@HiWAAY.net>
11 * Thomas Broyer <tbroyer@ltgt.net>
23 #include "libexslt/libexslt.h"
25 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
26 #include <win32config.h>
31 #if HAVE_LOCALTIME_R /* _POSIX_SOURCE required by gnu libc */
35 #include <libxml/tree.h>
36 #include <libxml/xpath.h>
37 #include <libxml/xpathInternals.h>
39 #include <libxslt/xsltconfig.h>
40 #include <libxslt/xsltutils.h>
41 #include <libxslt/xsltInternals.h>
42 #include <libxslt/extensions.h>
52 /* needed to get localtime_r on Solaris */
54 #ifndef __EXTENSIONS__
55 #define __EXTENSIONS__
64 * types of date and/or time (from schema datatypes)
65 * somewhat ordered from least specific to most specific (i.e.
66 * most truncated to least truncated).
70 XS_TIME = 1, /* time is left-truncated */
71 XS_GDAY = (XS_TIME << 1),
72 XS_GMONTH = (XS_GDAY << 1),
73 XS_GMONTHDAY = (XS_GMONTH | XS_GDAY),
74 XS_GYEAR = (XS_GMONTH << 1),
75 XS_GYEARMONTH = (XS_GYEAR | XS_GMONTH),
76 XS_DATE = (XS_GYEAR | XS_GMONTH | XS_GDAY),
77 XS_DATETIME = (XS_DATE | XS_TIME),
78 XS_DURATION = (XS_GYEAR << 1)
82 typedef struct _exsltDateValDate exsltDateValDate;
83 typedef exsltDateValDate *exsltDateValDatePtr;
84 struct _exsltDateValDate {
86 unsigned int mon :4; /* 1 <= mon <= 12 */
87 unsigned int day :5; /* 1 <= day <= 31 */
88 unsigned int hour :5; /* 0 <= hour <= 23 */
89 unsigned int min :6; /* 0 <= min <= 59 */
91 unsigned int tz_flag :1; /* is tzo explicitely set? */
92 int tzo :11; /* -1440 <= tzo <= 1440 */
96 typedef struct _exsltDateValDuration exsltDateValDuration;
97 typedef exsltDateValDuration *exsltDateValDurationPtr;
98 struct _exsltDateValDuration {
99 long mon; /* mon stores years also */
101 double sec; /* sec stores min and hour also */
104 typedef struct _exsltDateVal exsltDateVal;
105 typedef exsltDateVal *exsltDateValPtr;
106 struct _exsltDateVal {
109 exsltDateValDate date;
110 exsltDateValDuration dur;
114 /****************************************************************
116 * Compat./Port. macros *
118 ****************************************************************/
120 #if defined(HAVE_TIME_H) \
121 && (defined(HAVE_LOCALTIME) || defined(HAVE_LOCALTIME_R)) \
122 && (defined(HAVE_GMTIME) || defined(HAVE_GMTIME_R)) \
123 && defined(HAVE_TIME)
127 /****************************************************************
129 * Convenience macros and functions *
131 ****************************************************************/
133 #define IS_TZO_CHAR(c) \
134 ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
136 #define VALID_ALWAYS(num) (num >= 0)
137 #define VALID_YEAR(yr) (yr != 0)
138 #define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
139 /* VALID_DAY should only be used when month is unknown */
140 #define VALID_DAY(day) ((day >= 1) && (day <= 31))
141 #define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
142 #define VALID_MIN(min) ((min >= 0) && (min <= 59))
143 #define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
144 #define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
146 (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
148 static const unsigned long daysInMonth[12] =
149 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
150 static const unsigned long daysInMonthLeap[12] =
151 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
153 #define MAX_DAYINMONTH(yr,mon) \
154 (IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
156 #define VALID_MDAY(dt) \
157 (IS_LEAP(dt->year) ? \
158 (dt->day <= daysInMonthLeap[dt->mon - 1]) : \
159 (dt->day <= daysInMonth[dt->mon - 1]))
161 #define VALID_DATE(dt) \
162 (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
165 hour and min structure vals are unsigned, so normal macros give
166 warnings on some compilers.
168 #define VALID_TIME(dt) \
169 ((dt->hour <=23 ) && (dt->min <= 59) && \
170 VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
172 #define VALID_DATETIME(dt) \
173 (VALID_DATE(dt) && VALID_TIME(dt))
175 #define SECS_PER_MIN (60)
176 #define SECS_PER_HOUR (60 * SECS_PER_MIN)
177 #define SECS_PER_DAY (24 * SECS_PER_HOUR)
179 static const unsigned long dayInYearByMonth[12] =
180 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
181 static const unsigned long dayInLeapYearByMonth[12] =
182 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
184 #define DAY_IN_YEAR(day, month, year) \
186 dayInLeapYearByMonth[month - 1] : \
187 dayInYearByMonth[month - 1]) + day)
190 * _exsltDateParseGYear:
191 * @dt: pointer to a date structure
192 * @str: pointer to the string to analyze
194 * Parses a xs:gYear without time zone and fills in the appropriate
195 * field of the @dt structure. @str is updated to point just after the
196 * xs:gYear. It is supposed that @dt->year is big enough to contain
199 * Returns 0 or the error code
202 _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
204 const xmlChar *cur = *str, *firstChar;
205 int isneg = 0, digcnt = 0;
207 if (((*cur < '0') || (*cur > '9')) &&
208 (*cur != '-') && (*cur != '+'))
218 while ((*cur >= '0') && (*cur <= '9')) {
219 dt->year = dt->year * 10 + (*cur - '0');
224 /* year must be at least 4 digits (CCYY); over 4
225 * digits cannot have a leading zero. */
226 if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
230 dt->year = - dt->year;
232 if (!VALID_YEAR(dt->year))
237 #ifdef DEBUG_EXSLT_DATE
238 xsltGenericDebug(xsltGenericDebugContext,
239 "Parsed year %04i\n", dt->year);
247 * @yr: the year to format
248 * @cur: a pointer to an allocated buffer
250 * Formats @yr in xsl:gYear format. Result is appended to @cur and
251 * @cur is updated to point after the xsl:gYear.
253 #define FORMAT_GYEAR(yr, cur) \
259 long year = (yr < 0) ? - yr : yr; \
260 xmlChar tmp_buf[100], *tmp = tmp_buf; \
261 /* result is in reverse-order */ \
263 *tmp = '0' + (xmlChar)(year % 10); \
267 /* virtually adds leading zeros */ \
268 while ((tmp - tmp_buf) < 4) \
270 /* restore the correct order */ \
271 while (tmp > tmp_buf) { \
280 * @num: the integer to fill in
281 * @cur: an #xmlChar *
282 * @func: validation function for the number
283 * @invalid: an integer
285 * Parses a 2-digits integer and updates @num with the value. @cur is
286 * updated to point just after the integer.
287 * In case of error, @invalid is set to %TRUE, values of @num and
288 * @cur are undefined.
290 #define PARSE_2_DIGITS(num, cur, func, invalid) \
291 if ((cur[0] < '0') || (cur[0] > '9') || \
292 (cur[1] < '0') || (cur[1] > '9')) \
296 val = (cur[0] - '0') * 10 + (cur[1] - '0'); \
306 * @num: the integer to format
307 * @cur: a pointer to an allocated buffer
309 * Formats a 2-digits integer. Result is appended to @cur and
310 * @cur is updated to point after the integer.
312 #define FORMAT_2_DIGITS(num, cur) \
313 *cur = '0' + ((num / 10) % 10); \
315 *cur = '0' + (num % 10); \
320 * @num: the double to fill in
321 * @cur: an #xmlChar *
322 * @invalid: an integer
324 * Parses a float and updates @num with the value. @cur is
325 * updated to point just after the float. The float must have a
326 * 2-digits integer part and may or may not have a decimal part.
327 * In case of error, @invalid is set to %TRUE, values of @num and
328 * @cur are undefined.
330 #define PARSE_FLOAT(num, cur, invalid) \
331 PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid); \
332 if (!invalid && (*cur == '.')) { \
335 if ((*cur < '0') || (*cur > '9')) \
337 while ((*cur >= '0') && (*cur <= '9')) { \
339 num += (*cur - '0') * mult; \
346 * @num: the double to format
347 * @cur: a pointer to an allocated buffer
348 * @pad: a flag for padding to 2 integer digits
350 * Formats a float. Result is appended to @cur and @cur is updated to
351 * point after the integer. If the @pad flag is non-zero, then the
352 * float representation has a minimum 2-digits integer part. The
353 * fractional part is formatted if @num has a fractional value.
355 #define FORMAT_FLOAT(num, cur, pad) \
357 xmlChar *sav, *str; \
358 if ((pad) && (num < 10.0)) \
360 str = xmlXPathCastNumberToString(num); \
368 * _exsltDateParseGMonth:
369 * @dt: pointer to a date structure
370 * @str: pointer to the string to analyze
372 * Parses a xs:gMonth without time zone and fills in the appropriate
373 * field of the @dt structure. @str is updated to point just after the
376 * Returns 0 or the error code
379 _exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
381 const xmlChar *cur = *str;
384 PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret);
390 #ifdef DEBUG_EXSLT_DATE
391 xsltGenericDebug(xsltGenericDebugContext,
392 "Parsed month %02i\n", dt->mon);
400 * @mon: the month to format
401 * @cur: a pointer to an allocated buffer
403 * Formats @mon in xsl:gMonth format. Result is appended to @cur and
404 * @cur is updated to point after the xsl:gMonth.
406 #define FORMAT_GMONTH(mon, cur) \
407 FORMAT_2_DIGITS(mon, cur)
410 * _exsltDateParseGDay:
411 * @dt: pointer to a date structure
412 * @str: pointer to the string to analyze
414 * Parses a xs:gDay without time zone and fills in the appropriate
415 * field of the @dt structure. @str is updated to point just after the
418 * Returns 0 or the error code
421 _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
423 const xmlChar *cur = *str;
426 PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret);
432 #ifdef DEBUG_EXSLT_DATE
433 xsltGenericDebug(xsltGenericDebugContext,
434 "Parsed day %02i\n", dt->day);
442 * @dt: the #exsltDateValDate to format
443 * @cur: a pointer to an allocated buffer
445 * Formats @dt in xsl:gDay format. Result is appended to @cur and
446 * @cur is updated to point after the xsl:gDay.
448 #define FORMAT_GDAY(dt, cur) \
449 FORMAT_2_DIGITS(dt->day, cur)
453 * @dt: the #exsltDateValDate to format
454 * @cur: a pointer to an allocated buffer
456 * Formats @dt in xsl:date format. Result is appended to @cur and
457 * @cur is updated to point after the xsl:date.
459 #define FORMAT_DATE(dt, cur) \
460 FORMAT_GYEAR(dt->year, cur); \
463 FORMAT_GMONTH(dt->mon, cur); \
466 FORMAT_GDAY(dt, cur);
469 * _exsltDateParseTime:
470 * @dt: pointer to a date structure
471 * @str: pointer to the string to analyze
473 * Parses a xs:time without time zone and fills in the appropriate
474 * fields of the @dt structure. @str is updated to point just after the
476 * In case of error, values of @dt fields are undefined.
478 * Returns 0 or the error code
481 _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
483 const xmlChar *cur = *str;
484 unsigned int hour = 0; /* use temp var in case str is not xs:time */
487 PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret);
495 /* the ':' insures this string is xs:time */
498 PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret);
506 PARSE_FLOAT(dt->sec, cur, ret);
515 #ifdef DEBUG_EXSLT_DATE
516 xsltGenericDebug(xsltGenericDebugContext,
517 "Parsed time %02i:%02i:%02.f\n",
518 dt->hour, dt->min, dt->sec);
526 * @dt: the #exsltDateValDate to format
527 * @cur: a pointer to an allocated buffer
529 * Formats @dt in xsl:time format. Result is appended to @cur and
530 * @cur is updated to point after the xsl:time.
532 #define FORMAT_TIME(dt, cur) \
533 FORMAT_2_DIGITS(dt->hour, cur); \
536 FORMAT_2_DIGITS(dt->min, cur); \
539 FORMAT_FLOAT(dt->sec, cur, 1);
542 * _exsltDateParseTimeZone:
543 * @dt: pointer to a date structure
544 * @str: pointer to the string to analyze
546 * Parses a time zone without time zone and fills in the appropriate
547 * field of the @dt structure. @str is updated to point just after the
550 * Returns 0 or the error code
553 _exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str)
555 const xmlChar *cur = *str;
575 int isneg = 0, tmp = 0;
576 isneg = (*cur == '-');
580 PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret);
590 PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret);
598 if (!VALID_TZO(dt->tzo))
609 #ifdef DEBUG_EXSLT_DATE
610 xsltGenericDebug(xsltGenericDebugContext,
611 "Parsed time zone offset (%s) %i\n",
612 dt->tz_flag ? "explicit" : "implicit", dt->tzo);
620 * @tzo: the timezone offset to format
621 * @cur: a pointer to an allocated buffer
623 * Formats @tzo timezone. Result is appended to @cur and
624 * @cur is updated to point after the timezone.
626 #define FORMAT_TZ(tzo, cur) \
631 int aTzo = (tzo < 0) ? - tzo : tzo; \
632 int tzHh = aTzo / 60, tzMm = aTzo % 60; \
633 *cur = (tzo < 0) ? '-' : '+' ; \
635 FORMAT_2_DIGITS(tzHh, cur); \
638 FORMAT_2_DIGITS(tzMm, cur); \
641 /****************************************************************
643 * XML Schema Dates/Times Datatypes Handling *
645 ****************************************************************/
648 * exsltDateCreateDate:
649 * @type: type to create
651 * Creates a new #exsltDateVal, uninitialized.
653 * Returns the #exsltDateValPtr
655 static exsltDateValPtr
656 exsltDateCreateDate (exsltDateType type)
660 ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal));
662 xsltGenericError(xsltGenericErrorContext,
663 "exsltDateCreateDate: out of memory\n");
666 memset (ret, 0, sizeof(exsltDateVal));
668 if (type != EXSLT_UNKNOWN)
676 * @date: an #exsltDateValPtr
681 exsltDateFreeDate (exsltDateValPtr date) {
690 * @num: the integer to fill in
691 * @cur: an #xmlChar *
692 * @num_type: an integer flag
694 * Parses a digits integer and updates @num with the value. @cur is
695 * updated to point just after the integer.
696 * In case of error, @num_type is set to -1, values of @num and
697 * @cur are undefined.
699 #define PARSE_DIGITS(num, cur, num_type) \
700 if ((*cur < '0') || (*cur > '9')) \
703 while ((*cur >= '0') && (*cur <= '9')) { \
704 num = num * 10 + (*cur - '0'); \
710 * @num: the double to fill in
711 * @cur: an #xmlChar *
712 * @num_type: an integer flag
714 * Parses a float or integer and updates @num with the value. @cur is
715 * updated to point just after the number. If the number is a float,
716 * then it must have an integer part and a decimal part; @num_type will
717 * be set to 1. If there is no decimal part, @num_type is set to zero.
718 * In case of error, @num_type is set to -1, values of @num and
719 * @cur are undefined.
721 #define PARSE_NUM(num, cur, num_type) \
723 PARSE_DIGITS(num, cur, num_type); \
724 if (!num_type && (*cur == '.')) { \
727 if ((*cur < '0') || (*cur > '9')) \
731 while ((*cur >= '0') && (*cur <= '9')) { \
733 num += (*cur - '0') * mult; \
742 * Returns the current date and time.
744 static exsltDateValPtr
745 exsltDateCurrent (void)
747 struct tm *localTm, *gmTm;
757 ret = exsltDateCreateDate(XS_DATETIME);
761 /* get current time */
764 localtime_r(&secs, &localTmS);
767 localTm = localtime(&secs);
770 /* get real year, not years since 1900 */
771 ret->value.date.year = localTm->tm_year + 1900;
773 ret->value.date.mon = localTm->tm_mon + 1;
774 ret->value.date.day = localTm->tm_mday;
775 ret->value.date.hour = localTm->tm_hour;
776 ret->value.date.min = localTm->tm_min;
778 /* floating point seconds */
779 ret->value.date.sec = (double) localTm->tm_sec;
781 /* determine the time zone offset from local to gm time */
783 gmtime_r(&secs, &gmTmS);
786 gmTm = gmtime(&secs);
788 ret->value.date.tz_flag = 0;
789 ret->value.date.tzo = (((ret->value.date.day * 1440) +
790 (ret->value.date.hour * 60) +
791 ret->value.date.min) -
792 ((gmTm->tm_mday * 1440) + (gmTm->tm_hour * 60) +
801 * @dateTime: string to analyze
803 * Parses a date/time string
805 * Returns a newly built #exsltDateValPtr of NULL in case of error
807 static exsltDateValPtr
808 exsltDateParse (const xmlChar *dateTime)
812 const xmlChar *cur = dateTime;
814 #define RETURN_TYPE_IF_VALID(t) \
815 if (IS_TZO_CHAR(*cur)) { \
816 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \
825 if (dateTime == NULL)
828 if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
831 dt = exsltDateCreateDate(EXSLT_UNKNOWN);
835 if ((cur[0] == '-') && (cur[1] == '-')) {
837 * It's an incomplete date (xs:gMonthDay, xs:gMonth or
842 /* is it an xs:gDay? */
845 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
849 RETURN_TYPE_IF_VALID(XS_GDAY);
855 * it should be an xs:gMonthDay or xs:gMonth
857 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
865 /* is it an xs:gMonth? */
868 RETURN_TYPE_IF_VALID(XS_GMONTH);
872 /* it should be an xs:gMonthDay */
873 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
877 RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
883 * It's a right-truncated date or an xs:time.
884 * Try to parse an xs:time then fallback on right-truncated dates.
886 if ((*cur >= '0') && (*cur <= '9')) {
887 ret = _exsltDateParseTime(&(dt->value.date), &cur);
889 /* it's an xs:time */
890 RETURN_TYPE_IF_VALID(XS_TIME);
894 /* fallback on date parsing */
897 ret = _exsltDateParseGYear(&(dt->value.date), &cur);
901 /* is it an xs:gYear? */
902 RETURN_TYPE_IF_VALID(XS_GYEAR);
908 ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
912 /* is it an xs:gYearMonth? */
913 RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
919 ret = _exsltDateParseGDay(&(dt->value.date), &cur);
920 if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
923 /* is it an xs:date? */
924 RETURN_TYPE_IF_VALID(XS_DATE);
930 /* it should be an xs:dateTime */
931 ret = _exsltDateParseTime(&(dt->value.date), &cur);
935 ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
936 if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
939 dt->type = XS_DATETIME;
945 exsltDateFreeDate(dt);
950 * exsltDateParseDuration:
951 * @duration: string to analyze
953 * Parses a duration string
955 * Returns a newly built #exsltDateValPtr of NULL in case of error
957 static exsltDateValPtr
958 exsltDateParseDuration (const xmlChar *duration)
960 const xmlChar *cur = duration;
963 unsigned int seq = 0;
965 if (duration == NULL)
973 /* duration must start with 'P' (after sign) */
977 dur = exsltDateCreateDate(XS_DURATION);
983 int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
984 const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
985 const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
987 /* input string should be empty or invalid date/time item */
988 if (seq >= sizeof(desig))
991 /* T designator must be present for time items */
1001 /* parse the number portion of the item */
1002 PARSE_NUM(num, cur, num_type);
1004 if ((num_type == -1) || (*cur == 0))
1007 /* update duration based on item type */
1008 while (seq < sizeof(desig)) {
1009 if (*cur == desig[seq]) {
1011 /* verify numeric type; only seconds can be float */
1012 if ((num_type != 0) && (seq < (sizeof(desig)-1)))
1017 dur->value.dur.mon = (long)num * 12;
1020 dur->value.dur.mon += (long)num;
1023 /* convert to seconds using multiplier */
1024 dur->value.dur.sec += num * multi[seq];
1029 break; /* exit loop */
1031 /* no date designators found? */
1039 dur->value.dur.mon = -dur->value.dur.mon;
1040 dur->value.dur.day = -dur->value.dur.day;
1041 dur->value.dur.sec = -dur->value.dur.sec;
1044 #ifdef DEBUG_EXSLT_DATE
1045 xsltGenericDebug(xsltGenericDebugContext,
1046 "Parsed duration %f\n", dur->value.dur.sec);
1053 exsltDateFreeDate(dur);
1059 * @num: number to format
1060 * @cur: current location to convert number
1062 * @item: char designator
1065 #define FORMAT_ITEM(num, cur, limit, item) \
1067 long comp = (long)num / limit; \
1069 FORMAT_FLOAT((double)comp, cur, 0); \
1071 num -= (double)(comp * limit); \
1076 * exsltDateFormatDuration:
1077 * @dt: an #exsltDateValDurationPtr
1079 * Formats @dt in xs:duration format.
1081 * Returns a newly allocated string, or NULL in case of error
1084 exsltDateFormatDuration (const exsltDateValDurationPtr dt)
1086 xmlChar buf[100], *cur = buf;
1088 double years, months;
1093 /* quick and dirty check */
1094 if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
1095 return xmlStrdup((xmlChar*)"P0D");
1098 days = (double)dt->day;
1099 years = (double)(dt->mon / 12);
1100 months = (double)(dt->mon % 12);
1125 FORMAT_ITEM(years, cur, 1, 'Y');
1128 if (months != 0.0) {
1129 FORMAT_ITEM(months, cur, 1, 'M');
1132 if (secs >= SECS_PER_DAY) {
1133 double tmp = floor(secs / SECS_PER_DAY);
1135 secs -= (tmp * SECS_PER_DAY);
1138 FORMAT_ITEM(days, cur, 1, 'D');
1142 FORMAT_ITEM(secs, cur, SECS_PER_HOUR, 'H');
1143 FORMAT_ITEM(secs, cur, SECS_PER_MIN, 'M');
1145 FORMAT_FLOAT(secs, cur, 0);
1151 return xmlStrdup(buf);
1155 * exsltDateFormatDateTime:
1156 * @dt: an #exsltDateValDatePtr
1158 * Formats @dt in xs:dateTime format.
1160 * Returns a newly allocated string, or NULL in case of error
1163 exsltDateFormatDateTime (const exsltDateValDatePtr dt)
1165 xmlChar buf[100], *cur = buf;
1167 if ((dt == NULL) || !VALID_DATETIME(dt))
1170 FORMAT_DATE(dt, cur);
1173 FORMAT_TIME(dt, cur);
1174 FORMAT_TZ(dt->tzo, cur);
1177 return xmlStrdup(buf);
1181 * exsltDateFormatDate:
1182 * @dt: an #exsltDateValDatePtr
1184 * Formats @dt in xs:date format.
1186 * Returns a newly allocated string, or NULL in case of error
1189 exsltDateFormatDate (const exsltDateValDatePtr dt)
1191 xmlChar buf[100], *cur = buf;
1193 if ((dt == NULL) || !VALID_DATETIME(dt))
1196 FORMAT_DATE(dt, cur);
1197 if (dt->tz_flag || (dt->tzo != 0)) {
1198 FORMAT_TZ(dt->tzo, cur);
1202 return xmlStrdup(buf);
1206 * exsltDateFormatTime:
1207 * @dt: an #exsltDateValDatePtr
1209 * Formats @dt in xs:time format.
1211 * Returns a newly allocated string, or NULL in case of error
1214 exsltDateFormatTime (const exsltDateValDatePtr dt)
1216 xmlChar buf[100], *cur = buf;
1218 if ((dt == NULL) || !VALID_TIME(dt))
1221 FORMAT_TIME(dt, cur);
1222 if (dt->tz_flag || (dt->tzo != 0)) {
1223 FORMAT_TZ(dt->tzo, cur);
1227 return xmlStrdup(buf);
1232 * @dt: an #exsltDateValPtr
1234 * Formats @dt in the proper format.
1235 * Note: xs:gmonth and xs:gday are not formatted as there are no
1236 * routines that output them.
1238 * Returns a newly allocated string, or NULL in case of error
1241 exsltDateFormat (const exsltDateValPtr dt)
1249 return exsltDateFormatDuration(&(dt->value.dur));
1251 return exsltDateFormatDateTime(&(dt->value.date));
1253 return exsltDateFormatDate(&(dt->value.date));
1255 return exsltDateFormatTime(&(dt->value.date));
1260 if (dt->type & XS_GYEAR) {
1261 xmlChar buf[20], *cur = buf;
1263 FORMAT_GYEAR(dt->value.date.year, cur);
1264 if (dt->type == XS_GYEARMONTH) {
1267 FORMAT_GMONTH(dt->value.date.mon, cur);
1270 if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
1271 FORMAT_TZ(dt->value.date.tzo, cur);
1274 return xmlStrdup(buf);
1281 * _exsltDateCastYMToDays:
1282 * @dt: an #exsltDateValPtr
1284 * Convert mon and year of @dt to total number of days. Take the
1285 * number of years since (or before) 1 AD and add the number of leap
1286 * years. This is a function because negative
1287 * years must be handled a little differently and there is no zero year.
1289 * Returns number of days.
1292 _exsltDateCastYMToDays (const exsltDateValPtr dt)
1296 if (dt->value.date.year < 0)
1297 ret = (dt->value.date.year * 365) +
1298 (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
1299 ((dt->value.date.year+1)/400)) +
1300 DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
1302 ret = ((dt->value.date.year-1) * 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);
1312 * @dt: an #exsltDateValPtr
1314 * Calculates the number of seconds in the time portion of @dt.
1318 #define TIME_TO_NUMBER(dt) \
1319 ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
1320 (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
1323 * exsltDateCastDateToNumber:
1324 * @dt: an #exsltDateValPtr
1326 * Calculates the number of seconds from year zero.
1328 * Returns seconds from zero year.
1331 exsltDateCastDateToNumber (const exsltDateValPtr dt)
1338 if ((dt->type & XS_GYEAR) == XS_GYEAR) {
1339 ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
1343 if (dt->type == XS_DURATION) {
1344 ret += (double)dt->value.dur.day * SECS_PER_DAY;
1345 ret += dt->value.dur.sec;
1347 ret += (double)dt->value.date.day * SECS_PER_DAY;
1349 ret += TIME_TO_NUMBER(dt);
1357 * _exsltDateTruncateDate:
1358 * @dt: an #exsltDateValPtr
1359 * @type: dateTime type to set to
1361 * Set @dt to truncated @type.
1363 * Returns 0 success, non-zero otherwise.
1366 _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
1371 if ((type & XS_TIME) != XS_TIME) {
1372 dt->value.date.hour = 0;
1373 dt->value.date.min = 0;
1374 dt->value.date.sec = 0.0;
1377 if ((type & XS_GDAY) != XS_GDAY)
1378 dt->value.date.day = 0;
1380 if ((type & XS_GMONTH) != XS_GMONTH)
1381 dt->value.date.mon = 0;
1383 if ((type & XS_GYEAR) != XS_GYEAR)
1384 dt->value.date.year = 0;
1393 * @yday: year day (1-366)
1396 * Determine the day-in-week from @yday and @yr. 0001-01-01 was
1397 * a Monday so all other days are calculated from there. Take the
1398 * number of years since (or before) add the number of leap years and
1399 * the day-in-year and mod by 7. This is a function because negative
1400 * years must be handled a little differently and there is no zero year.
1402 * Returns day in week (Sunday = 0).
1405 _exsltDateDayInWeek(long yday, long yr)
1410 ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
1414 ret = (((yr-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
1420 * macros for adding date/times and durations
1422 #define FQUOTIENT(a,b) ((floor(((double)a/(double)b))))
1423 #define MODULO(a,b) ((a - FQUOTIENT(a,b) * b))
1424 #define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
1425 #define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
1429 * @dt: an #exsltDateValPtr
1430 * @dur: an #exsltDateValPtr of type #XS_DURATION
1432 * Compute a new date/time from @dt and @dur. This function assumes @dt
1433 * is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
1435 * Returns date/time pointer or NULL.
1437 static exsltDateValPtr
1438 _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
1440 exsltDateValPtr ret;
1441 long carry, tempdays, temp;
1442 exsltDateValDatePtr r, d;
1443 exsltDateValDurationPtr u;
1445 if ((dt == NULL) || (dur == NULL))
1448 ret = exsltDateCreateDate(dt->type);
1452 r = &(ret->value.date);
1453 d = &(dt->value.date);
1454 u = &(dur->value.dur);
1460 /* normalize for time zone offset */
1461 u->sec -= (d->tzo * 60); /* changed from + to - (bug 153000) */
1469 carry = d->mon + u->mon;
1470 r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
1471 carry = (long)FQUOTIENT_RANGE(carry, 1, 13);
1473 /* year (may be modified later) */
1474 r->year = d->year + carry;
1484 r->tz_flag = d->tz_flag;
1487 r->sec = d->sec + u->sec;
1488 carry = (long)FQUOTIENT((long)r->sec, 60);
1489 if (r->sec != 0.0) {
1490 r->sec = MODULO(r->sec, 60.0);
1495 r->min = (unsigned int)MODULO(carry, 60);
1496 carry = (long)FQUOTIENT(carry, 60);
1500 r->hour = (unsigned int)MODULO(carry, 24);
1501 carry = (long)FQUOTIENT(carry, 24);
1505 * Note we use tempdays because the temporary values may need more
1508 if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
1509 (d->day > MAX_DAYINMONTH(r->year, r->mon)))
1510 tempdays = MAX_DAYINMONTH(r->year, r->mon);
1511 else if (d->day < 1)
1516 tempdays += u->day + carry;
1520 long tmon = (long)MODULO_RANGE(r->mon-1, 1, 13);
1521 long tyr = r->year + (long)FQUOTIENT_RANGE(r->mon-1, 1, 13);
1524 tempdays += MAX_DAYINMONTH(tyr, tmon);
1526 } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
1527 tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
1532 temp = r->mon + carry;
1533 r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
1534 r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
1546 * adjust the date/time type to the date values
1548 if (ret->type != XS_DATETIME) {
1549 if ((r->hour) || (r->min) || (r->sec))
1550 ret->type = XS_DATETIME;
1551 else if (ret->type != XS_DATE) {
1552 if ((r->mon != 1) && (r->day != 1))
1553 ret->type = XS_DATE;
1554 else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
1555 ret->type = XS_GYEARMONTH;
1563 * exsltDateNormalize:
1564 * @dt: an #exsltDateValPtr
1566 * Normalize @dt to GMT time.
1570 exsltDateNormalize (exsltDateValPtr dt)
1572 exsltDateValPtr dur, tmp;
1577 if (((dt->type & XS_TIME) != XS_TIME) || (dt->value.date.tzo == 0))
1580 dur = exsltDateCreateDate(XS_DURATION);
1584 tmp = _exsltDateAdd(dt, dur);
1588 memcpy(dt, tmp, sizeof(exsltDateVal));
1590 exsltDateFreeDate(tmp);
1591 exsltDateFreeDate(dur);
1593 dt->value.date.tzo = 0;
1597 * _exsltDateDifference:
1598 * @x: an #exsltDateValPtr
1599 * @y: an #exsltDateValPtr
1600 * @flag: force difference in days
1602 * Calculate the difference between @x and @y as a duration
1603 * (i.e. y - x). If the @flag is set then even if the least specific
1604 * format of @x or @y is xs:gYear or xs:gYearMonth.
1606 * Returns date/time pointer or NULL.
1608 static exsltDateValPtr
1609 _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
1611 exsltDateValPtr ret;
1613 if ((x == NULL) || (y == NULL))
1616 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
1617 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
1620 exsltDateNormalize(x);
1621 exsltDateNormalize(y);
1624 * the operand with the most specific format must be converted to
1625 * the same type as the operand with the least specific format.
1627 if (x->type != y->type) {
1628 if (x->type < y->type) {
1629 _exsltDateTruncateDate(y, x->type);
1631 _exsltDateTruncateDate(x, y->type);
1635 ret = exsltDateCreateDate(XS_DURATION);
1639 if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
1640 /* compute the difference in months */
1641 ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
1642 ((x->value.date.year * 12) + x->value.date.mon);
1643 /* The above will give a wrong result if x and y are on different sides
1644 of the September 1752. Resolution is welcome :-) */
1646 ret->value.dur.day = _exsltDateCastYMToDays(y) -
1647 _exsltDateCastYMToDays(x);
1648 ret->value.dur.day += y->value.date.day - x->value.date.day;
1649 ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
1650 if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
1651 ret->value.dur.day -= 1;
1652 ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
1653 } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
1654 ret->value.dur.day += 1;
1655 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1663 * _exsltDateAddDurCalc
1664 * @ret: an exsltDateValPtr for the return value:
1665 * @x: an exsltDateValPtr for the first operand
1666 * @y: an exsltDateValPtr for the second operand
1668 * Add two durations, catering for possible negative values.
1669 * The sum is placed in @ret.
1671 * Returns 1 for success, 0 if error detected.
1674 _exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
1680 ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
1683 ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
1684 carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
1685 if (ret->value.dur.sec != 0.0) {
1686 ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
1688 * Our function MODULO always gives us a positive value, so
1689 * if we end up with a "-ve" carry we need to adjust it
1690 * appropriately (bug 154021)
1692 if ((carry < 0) && (ret->value.dur.sec != 0)) {
1693 /* change seconds to equiv negative modulus */
1694 ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
1700 ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
1703 * are the results indeterminate? i.e. how do you subtract days from
1706 if ((((ret->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
1707 (ret->value.dur.mon < 0)) ||
1708 (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
1709 (ret->value.dur.mon > 0))) {
1716 * _exsltDateAddDuration:
1717 * @x: an #exsltDateValPtr of type #XS_DURATION
1718 * @y: an #exsltDateValPtr of type #XS_DURATION
1720 * Compute a new duration from @x and @y.
1722 * Returns date/time pointer or NULL.
1724 static exsltDateValPtr
1725 _exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
1727 exsltDateValPtr ret;
1729 if ((x == NULL) || (y == NULL))
1732 ret = exsltDateCreateDate(XS_DURATION);
1736 if (_exsltDateAddDurCalc(ret, x, y))
1739 exsltDateFreeDate(ret);
1743 /****************************************************************
1745 * EXSLT - Dates and Times functions *
1747 ****************************************************************/
1750 * exsltDateDateTime:
1752 * Implements the EXSLT - Dates and Times date-time() function:
1753 * string date:date-time()
1755 * Returns the current date and time as a date/time string.
1758 exsltDateDateTime (void)
1760 xmlChar *ret = NULL;
1762 exsltDateValPtr cur;
1764 cur = exsltDateCurrent();
1766 ret = exsltDateFormatDateTime(&(cur->value.date));
1767 exsltDateFreeDate(cur);
1776 * @dateTime: a date/time string
1778 * Implements the EXSLT - Dates and Times date() function:
1779 * string date:date (string?)
1781 * Returns the date specified in the date/time string given as the
1782 * argument. If no argument is given, then the current local
1783 * date/time, as returned by date:date-time is used as a default
1785 * The date/time string specified as an argument must be a string in
1786 * the format defined as the lexical representation of either
1787 * xs:dateTime or xs:date. If the argument is not in either of these
1788 * formats, returns NULL.
1791 exsltDateDate (const xmlChar *dateTime)
1793 exsltDateValPtr dt = NULL;
1794 xmlChar *ret = NULL;
1796 if (dateTime == NULL) {
1798 dt = exsltDateCurrent();
1803 dt = exsltDateParse(dateTime);
1806 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
1807 exsltDateFreeDate(dt);
1812 ret = exsltDateFormatDate(&(dt->value.date));
1813 exsltDateFreeDate(dt);
1820 * @dateTime: a date/time string
1822 * Implements the EXSLT - Dates and Times time() function:
1823 * string date:time (string?)
1825 * Returns the time specified in the date/time string given as the
1826 * argument. If no argument is given, then the current local
1827 * date/time, as returned by date:date-time is used as a default
1829 * The date/time string specified as an argument must be a string in
1830 * the format defined as the lexical representation of either
1831 * xs:dateTime or xs:time. If the argument is not in either of these
1832 * formats, returns NULL.
1835 exsltDateTime (const xmlChar *dateTime)
1837 exsltDateValPtr dt = NULL;
1838 xmlChar *ret = NULL;
1840 if (dateTime == NULL) {
1842 dt = exsltDateCurrent();
1847 dt = exsltDateParse(dateTime);
1850 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
1851 exsltDateFreeDate(dt);
1856 ret = exsltDateFormatTime(&(dt->value.date));
1857 exsltDateFreeDate(dt);
1864 * @dateTime: a date/time string
1866 * Implements the EXSLT - Dates and Times year() function
1867 * number date:year (string?)
1868 * Returns the year of a date as a number. If no argument is given,
1869 * then the current local date/time, as returned by date:date-time is
1870 * used as a default argument.
1871 * The date/time string specified as the first argument must be a
1872 * right-truncated string in the format defined as the lexical
1873 * representation of xs:dateTime in one of the formats defined in [XML
1874 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1875 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1876 * - xs:date (CCYY-MM-DD)
1877 * - xs:gYearMonth (CCYY-MM)
1879 * If the date/time string is not in one of these formats, then NaN is
1883 exsltDateYear (const xmlChar *dateTime)
1888 if (dateTime == NULL) {
1890 dt = exsltDateCurrent();
1895 dt = exsltDateParse(dateTime);
1898 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1899 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
1900 exsltDateFreeDate(dt);
1905 ret = (double) dt->value.date.year;
1906 exsltDateFreeDate(dt);
1912 * exsltDateLeapYear:
1913 * @dateTime: a date/time string
1915 * Implements the EXSLT - Dates and Times leap-year() function:
1916 * boolean date:leap-yea (string?)
1917 * Returns true if the year given in a date is a leap year. If no
1918 * argument is given, then the current local date/time, as returned by
1919 * date:date-time is used as a default argument.
1920 * The date/time string specified as the first argument must be a
1921 * right-truncated string in the format defined as the lexical
1922 * representation of xs:dateTime in one of the formats defined in [XML
1923 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1924 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1925 * - xs:date (CCYY-MM-DD)
1926 * - xs:gYearMonth (CCYY-MM)
1928 * If the date/time string is not in one of these formats, then NaN is
1931 static xmlXPathObjectPtr
1932 exsltDateLeapYear (const xmlChar *dateTime)
1936 year = exsltDateYear(dateTime);
1937 if (xmlXPathIsNaN(year))
1938 return xmlXPathNewFloat(xmlXPathNAN);
1940 if (IS_LEAP((long)year))
1941 return xmlXPathNewBoolean(1);
1943 return xmlXPathNewBoolean(0);
1947 * exsltDateMonthInYear:
1948 * @dateTime: a date/time string
1950 * Implements the EXSLT - Dates and Times month-in-year() function:
1951 * number date:month-in-year (string?)
1952 * Returns the month of a date as a number. If no argument is given,
1953 * then the current local date/time, as returned by date:date-time is
1954 * used the default argument.
1955 * The date/time string specified as the argument is a left or
1956 * right-truncated string in the format defined as the lexical
1957 * representation of xs:dateTime in one of the formats defined in [XML
1958 * Schema Part 2: Datatypes]. The permitted formats are as follows:
1959 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
1960 * - xs:date (CCYY-MM-DD)
1961 * - xs:gYearMonth (CCYY-MM)
1962 * - xs:gMonth (--MM--)
1963 * - xs:gMonthDay (--MM-DD)
1964 * If the date/time string is not in one of these formats, then NaN is
1968 exsltDateMonthInYear (const xmlChar *dateTime)
1973 if (dateTime == NULL) {
1975 dt = exsltDateCurrent();
1980 dt = exsltDateParse(dateTime);
1983 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
1984 (dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
1985 (dt->type != XS_GMONTHDAY)) {
1986 exsltDateFreeDate(dt);
1991 ret = (double) dt->value.date.mon;
1992 exsltDateFreeDate(dt);
1998 * exsltDateMonthName:
1999 * @dateTime: a date/time string
2001 * Implements the EXSLT - Dates and Time month-name() function
2002 * string date:month-name (string?)
2003 * Returns the full name of the month of a date. If no argument is
2004 * given, then the current local date/time, as returned by
2005 * date:date-time is used the default argument.
2006 * The date/time string specified as the argument is a left or
2007 * right-truncated string in the format defined as the lexical
2008 * representation of xs:dateTime in one of the formats defined in [XML
2009 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2010 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2011 * - xs:date (CCYY-MM-DD)
2012 * - xs:gYearMonth (CCYY-MM)
2013 * - xs:gMonth (--MM--)
2014 * If the date/time string is not in one of these formats, then an
2015 * empty string ('') is returned.
2016 * The result is an English month name: one of 'January', 'February',
2017 * 'March', 'April', 'May', 'June', 'July', 'August', 'September',
2018 * 'October', 'November' or 'December'.
2020 static const xmlChar *
2021 exsltDateMonthName (const xmlChar *dateTime)
2023 static const xmlChar monthNames[13][10] = {
2025 { 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
2026 { 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
2027 { 'M', 'a', 'r', 'c', 'h', 0 },
2028 { 'A', 'p', 'r', 'i', 'l', 0 },
2029 { 'M', 'a', 'y', 0 },
2030 { 'J', 'u', 'n', 'e', 0 },
2031 { 'J', 'u', 'l', 'y', 0 },
2032 { 'A', 'u', 'g', 'u', 's', 't', 0 },
2033 { 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
2034 { 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
2035 { 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
2036 { 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
2039 month = (int) exsltDateMonthInYear(dateTime);
2040 if (!VALID_MONTH(month))
2042 return monthNames[month];
2046 * exsltDateMonthAbbreviation:
2047 * @dateTime: a date/time string
2049 * Implements the EXSLT - Dates and Time month-abbreviation() function
2050 * string date:month-abbreviation (string?)
2051 * Returns the abbreviation of the month of a date. If no argument is
2052 * given, then the current local date/time, as returned by
2053 * date:date-time is used the default argument.
2054 * The date/time string specified as the argument is a left or
2055 * right-truncated string in the format defined as the lexical
2056 * representation of xs:dateTime in one of the formats defined in [XML
2057 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2058 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2059 * - xs:date (CCYY-MM-DD)
2060 * - xs:gYearMonth (CCYY-MM)
2061 * - xs:gMonth (--MM--)
2062 * If the date/time string is not in one of these formats, then an
2063 * empty string ('') is returned.
2064 * The result is an English month abbreviation: one of 'Jan', 'Feb',
2065 * 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
2068 static const xmlChar *
2069 exsltDateMonthAbbreviation (const xmlChar *dateTime)
2071 static const xmlChar monthAbbreviations[13][4] = {
2073 { 'J', 'a', 'n', 0 },
2074 { 'F', 'e', 'b', 0 },
2075 { 'M', 'a', 'r', 0 },
2076 { 'A', 'p', 'r', 0 },
2077 { 'M', 'a', 'y', 0 },
2078 { 'J', 'u', 'n', 0 },
2079 { 'J', 'u', 'l', 0 },
2080 { 'A', 'u', 'g', 0 },
2081 { 'S', 'e', 'p', 0 },
2082 { 'O', 'c', 't', 0 },
2083 { 'N', 'o', 'v', 0 },
2084 { 'D', 'e', 'c', 0 }
2087 month = (int) exsltDateMonthInYear(dateTime);
2088 if(!VALID_MONTH(month))
2090 return monthAbbreviations[month];
2094 * exsltDateWeekInYear:
2095 * @dateTime: a date/time string
2097 * Implements the EXSLT - Dates and Times week-in-year() function
2098 * number date:week-in-year (string?)
2099 * Returns the week of the year as a number. If no argument is given,
2100 * then the current local date/time, as returned by date:date-time is
2101 * used as the default argument. For the purposes of numbering,
2102 * counting follows ISO 8601: week 1 in a year is the week containing
2103 * the first Thursday of the year, with new weeks beginning on a
2105 * The date/time string specified as the argument is a right-truncated
2106 * string in the format defined as the lexical representation of
2107 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2108 * Datatypes]. The permitted formats are as follows:
2109 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2110 * - xs:date (CCYY-MM-DD)
2111 * If the date/time string is not in one of these formats, then NaN is
2115 exsltDateWeekInYear (const xmlChar *dateTime)
2118 long fdiy, fdiw, ret;
2120 if (dateTime == NULL) {
2122 dt = exsltDateCurrent();
2127 dt = exsltDateParse(dateTime);
2130 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2131 exsltDateFreeDate(dt);
2136 fdiy = DAY_IN_YEAR(1, 1, dt->value.date.year);
2139 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2140 * is the first day-in-week
2142 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2144 ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2145 dt->value.date.year) / 7;
2147 /* ISO 8601 adjustment, 3 is Thu */
2151 exsltDateFreeDate(dt);
2153 return (double) ret;
2157 * exsltDateWeekInMonth:
2158 * @dateTime: a date/time string
2160 * Implements the EXSLT - Dates and Times week-in-month() function
2161 * number date:week-in-month (string?)
2162 * The date:week-in-month function returns the week in a month of a
2163 * date as a number. If no argument is given, then the current local
2164 * date/time, as returned by date:date-time is used the default
2165 * argument. For the purposes of numbering, the first day of the month
2166 * is in week 1 and new weeks begin on a Monday (so the first and last
2167 * weeks in a month will often have less than 7 days in them).
2168 * The date/time string specified as the argument is a right-truncated
2169 * string in the format defined as the lexical representation of
2170 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2171 * Datatypes]. The permitted formats are as follows:
2172 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2173 * - xs:date (CCYY-MM-DD)
2174 * If the date/time string is not in one of these formats, then NaN is
2178 exsltDateWeekInMonth (const xmlChar *dateTime)
2181 long fdiy, fdiw, ret;
2183 if (dateTime == NULL) {
2185 dt = exsltDateCurrent();
2190 dt = exsltDateParse(dateTime);
2193 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2194 exsltDateFreeDate(dt);
2199 fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
2201 * Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
2202 * is the first day-in-week
2204 fdiw = (_exsltDateDayInWeek(fdiy, dt->value.date.year) + 6) % 7;
2206 ret = ((dt->value.date.day + fdiw) / 7) + 1;
2208 exsltDateFreeDate(dt);
2210 return (double) ret;
2214 * exsltDateDayInYear:
2215 * @dateTime: a date/time string
2217 * Implements the EXSLT - Dates and Times day-in-year() function
2218 * number date:day-in-year (string?)
2219 * Returns the day of a date in a year as a number. If no argument is
2220 * given, then the current local date/time, as returned by
2221 * date:date-time is used the default argument.
2222 * The date/time string specified as the argument is a right-truncated
2223 * string in the format defined as the lexical representation of
2224 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2225 * Datatypes]. The permitted formats are as follows:
2226 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2227 * - xs:date (CCYY-MM-DD)
2228 * If the date/time string is not in one of these formats, then NaN is
2232 exsltDateDayInYear (const xmlChar *dateTime)
2237 if (dateTime == NULL) {
2239 dt = exsltDateCurrent();
2244 dt = exsltDateParse(dateTime);
2247 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2248 exsltDateFreeDate(dt);
2253 ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2254 dt->value.date.year);
2256 exsltDateFreeDate(dt);
2258 return (double) ret;
2262 * exsltDateDayInMonth:
2263 * @dateTime: a date/time string
2265 * Implements the EXSLT - Dates and Times day-in-month() function:
2266 * number date:day-in-month (string?)
2267 * Returns the day of a date as a number. If no argument is given,
2268 * then the current local date/time, as returned by date:date-time is
2269 * used the default argument.
2270 * The date/time string specified as the argument is a left or
2271 * right-truncated string in the format defined as the lexical
2272 * representation of xs:dateTime in one of the formats defined in [XML
2273 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2274 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2275 * - xs:date (CCYY-MM-DD)
2276 * - xs:gMonthDay (--MM-DD)
2278 * If the date/time string is not in one of these formats, then NaN is
2282 exsltDateDayInMonth (const xmlChar *dateTime)
2287 if (dateTime == NULL) {
2289 dt = exsltDateCurrent();
2294 dt = exsltDateParse(dateTime);
2297 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
2298 (dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
2299 exsltDateFreeDate(dt);
2304 ret = (double) dt->value.date.day;
2305 exsltDateFreeDate(dt);
2311 * exsltDateDayOfWeekInMonth:
2312 * @dateTime: a date/time string
2314 * Implements the EXSLT - Dates and Times day-of-week-in-month() function:
2315 * number date:day-of-week-in-month (string?)
2316 * Returns the day-of-the-week in a month of a date as a number
2317 * (e.g. 3 for the 3rd Tuesday in May). If no argument is
2318 * given, then the current local date/time, as returned by
2319 * date:date-time is used the default argument.
2320 * The date/time string specified as the argument is a right-truncated
2321 * string in the format defined as the lexical representation of
2322 * xs:dateTime in one of the formats defined in [XML Schema Part 2:
2323 * Datatypes]. The permitted formats are as follows:
2324 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2325 * - xs:date (CCYY-MM-DD)
2326 * If the date/time string is not in one of these formats, then NaN is
2330 exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
2335 if (dateTime == NULL) {
2337 dt = exsltDateCurrent();
2342 dt = exsltDateParse(dateTime);
2345 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2346 exsltDateFreeDate(dt);
2351 ret = (dt->value.date.day / 7) + 1;
2353 exsltDateFreeDate(dt);
2355 return (double) ret;
2359 * exsltDateDayInWeek:
2360 * @dateTime: a date/time string
2362 * Implements the EXSLT - Dates and Times day-in-week() function:
2363 * number date:day-in-week (string?)
2364 * Returns the day of the week given in a date as a number. If no
2365 * argument is given, then the current local date/time, as returned by
2366 * date:date-time is used the default argument.
2367 * The date/time string specified as the argument is a left or
2368 * right-truncated string in the format defined as the lexical
2369 * representation of xs:dateTime in one of the formats defined in [XML
2370 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2371 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2372 * - xs:date (CCYY-MM-DD)
2373 * If the date/time string is not in one of these formats, then NaN is
2375 * The numbering of days of the week starts at 1 for Sunday, 2 for
2376 * Monday and so on up to 7 for Saturday.
2379 exsltDateDayInWeek (const xmlChar *dateTime)
2384 if (dateTime == NULL) {
2386 dt = exsltDateCurrent();
2391 dt = exsltDateParse(dateTime);
2394 if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
2395 exsltDateFreeDate(dt);
2400 diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
2401 dt->value.date.year);
2403 ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
2405 exsltDateFreeDate(dt);
2407 return (double) ret;
2412 * @dateTime: a date/time string
2414 * Implements the EXSLT - Dates and Time day-name() function
2415 * string date:day-name (string?)
2416 * Returns the full name of the day of the week of a date. If no
2417 * argument is given, then the current local date/time, as returned by
2418 * date:date-time is used the default argument.
2419 * The date/time string specified as the argument is a left or
2420 * right-truncated string in the format defined as the lexical
2421 * representation of xs:dateTime in one of the formats defined in [XML
2422 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2423 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2424 * - xs:date (CCYY-MM-DD)
2425 * If the date/time string is not in one of these formats, then an
2426 * empty string ('') is returned.
2427 * The result is an English day name: one of 'Sunday', 'Monday',
2428 * 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
2430 static const xmlChar *
2431 exsltDateDayName (const xmlChar *dateTime)
2433 static const xmlChar dayNames[8][10] = {
2435 { 'S', 'u', 'n', 'd', 'a', 'y', 0 },
2436 { 'M', 'o', 'n', 'd', 'a', 'y', 0 },
2437 { 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
2438 { 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
2439 { 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
2440 { 'F', 'r', 'i', 'd', 'a', 'y', 0 },
2441 { 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
2444 day = (int) exsltDateDayInWeek(dateTime);
2445 if((day < 1) || (day > 7))
2447 return dayNames[day];
2451 * exsltDateDayAbbreviation:
2452 * @dateTime: a date/time string
2454 * Implements the EXSLT - Dates and Time day-abbreviation() function
2455 * string date:day-abbreviation (string?)
2456 * Returns the abbreviation of the day of the week of a date. If no
2457 * argument is given, then the current local date/time, as returned by
2458 * date:date-time is used the default argument.
2459 * The date/time string specified as the argument is a left or
2460 * right-truncated string in the format defined as the lexical
2461 * representation of xs:dateTime in one of the formats defined in [XML
2462 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2463 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2464 * - xs:date (CCYY-MM-DD)
2465 * If the date/time string is not in one of these formats, then an
2466 * empty string ('') is returned.
2467 * The result is a three-letter English day abbreviation: one of
2468 * 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
2470 static const xmlChar *
2471 exsltDateDayAbbreviation (const xmlChar *dateTime)
2473 static const xmlChar dayAbbreviations[8][4] = {
2475 { 'S', 'u', 'n', 0 },
2476 { 'M', 'o', 'n', 0 },
2477 { 'T', 'u', 'e', 0 },
2478 { 'W', 'e', 'd', 0 },
2479 { 'T', 'h', 'u', 0 },
2480 { 'F', 'r', 'i', 0 },
2481 { 'S', 'a', 't', 0 }
2484 day = (int) exsltDateDayInWeek(dateTime);
2485 if((day < 1) || (day > 7))
2487 return dayAbbreviations[day];
2491 * exsltDateHourInDay:
2492 * @dateTime: a date/time string
2494 * Implements the EXSLT - Dates and Times day-in-month() function:
2495 * number date:day-in-month (string?)
2496 * Returns the hour of the day as a number. If no argument is given,
2497 * then the current local date/time, as returned by date:date-time is
2498 * used the default argument.
2499 * The date/time string specified as the argument is a left or
2500 * right-truncated string in the format defined as the lexical
2501 * representation of xs:dateTime in one of the formats defined in [XML
2502 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2503 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2504 * - xs:time (hh:mm:ss)
2505 * If the date/time string is not in one of these formats, then NaN is
2509 exsltDateHourInDay (const xmlChar *dateTime)
2514 if (dateTime == NULL) {
2516 dt = exsltDateCurrent();
2521 dt = exsltDateParse(dateTime);
2524 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2525 exsltDateFreeDate(dt);
2530 ret = (double) dt->value.date.hour;
2531 exsltDateFreeDate(dt);
2537 * exsltDateMinuteInHour:
2538 * @dateTime: a date/time string
2540 * Implements the EXSLT - Dates and Times day-in-month() function:
2541 * number date:day-in-month (string?)
2542 * Returns the minute of the hour as a number. If no argument is
2543 * given, then the current local date/time, as returned by
2544 * date:date-time is used the default argument.
2545 * The date/time string specified as the argument is a left or
2546 * right-truncated string in the format defined as the lexical
2547 * representation of xs:dateTime in one of the formats defined in [XML
2548 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2549 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2550 * - xs:time (hh:mm:ss)
2551 * If the date/time string is not in one of these formats, then NaN is
2555 exsltDateMinuteInHour (const xmlChar *dateTime)
2560 if (dateTime == NULL) {
2562 dt = exsltDateCurrent();
2567 dt = exsltDateParse(dateTime);
2570 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2571 exsltDateFreeDate(dt);
2576 ret = (double) dt->value.date.min;
2577 exsltDateFreeDate(dt);
2583 * exsltDateSecondInMinute:
2584 * @dateTime: a date/time string
2586 * Implements the EXSLT - Dates and Times second-in-minute() function:
2587 * number date:day-in-month (string?)
2588 * Returns the second of the minute as a number. If no argument is
2589 * given, then the current local date/time, as returned by
2590 * date:date-time is used the default argument.
2591 * The date/time string specified as the argument is a left or
2592 * right-truncated string in the format defined as the lexical
2593 * representation of xs:dateTime in one of the formats defined in [XML
2594 * Schema Part 2: Datatypes]. The permitted formats are as follows:
2595 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2596 * - xs:time (hh:mm:ss)
2597 * If the date/time string is not in one of these formats, then NaN is
2600 * Returns the second or NaN.
2603 exsltDateSecondInMinute (const xmlChar *dateTime)
2608 if (dateTime == NULL) {
2610 dt = exsltDateCurrent();
2615 dt = exsltDateParse(dateTime);
2618 if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
2619 exsltDateFreeDate(dt);
2624 ret = dt->value.date.sec;
2625 exsltDateFreeDate(dt);
2632 * @xstr: date/time string
2633 * @ystr: date/time string
2635 * Implements the date:add (string,string) function which returns the
2636 * date/time * resulting from adding a duration to a date/time.
2637 * The first argument (@xstr) must be right-truncated date/time
2638 * strings in one of the formats defined in [XML Schema Part 2:
2639 * Datatypes]. The permitted formats are as follows:
2640 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2641 * - xs:date (CCYY-MM-DD)
2642 * - xs:gYearMonth (CCYY-MM)
2644 * The second argument (@ystr) is a string in the format defined for
2645 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2646 * The return value is a right-truncated date/time strings in one of
2647 * the formats defined in [XML Schema Part 2: Datatypes] and listed
2648 * above. This value is calculated using the algorithm described in
2649 * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
2652 * Returns date/time string or NULL.
2655 exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
2657 exsltDateValPtr dt, dur, res;
2660 if ((xstr == NULL) || (ystr == NULL))
2663 dt = exsltDateParse(xstr);
2666 else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
2667 exsltDateFreeDate(dt);
2671 dur = exsltDateParseDuration(ystr);
2673 exsltDateFreeDate(dt);
2677 res = _exsltDateAdd(dt, dur);
2679 exsltDateFreeDate(dt);
2680 exsltDateFreeDate(dur);
2685 ret = exsltDateFormat(res);
2686 exsltDateFreeDate(res);
2692 * exsltDateAddDuration:
2693 * @xstr: first duration string
2694 * @ystr: second duration string
2696 * Implements the date:add-duration (string,string) function which returns
2697 * the duration resulting from adding two durations together.
2698 * Both arguments are strings in the format defined for xs:duration
2699 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
2700 * argument is not in this format, the function returns an empty string
2702 * The return value is a string in the format defined for xs:duration
2703 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2704 * The durations can usually be added by summing the numbers given for
2705 * each of the components in the durations. However, if the durations
2706 * are differently signed, then this sometimes results in durations
2707 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2708 * In these cases, the function returns an empty string ('').
2710 * Returns duration string or NULL.
2713 exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
2715 exsltDateValPtr x, y, res;
2718 if ((xstr == NULL) || (ystr == NULL))
2721 x = exsltDateParseDuration(xstr);
2725 y = exsltDateParseDuration(ystr);
2727 exsltDateFreeDate(x);
2731 res = _exsltDateAddDuration(x, y);
2733 exsltDateFreeDate(x);
2734 exsltDateFreeDate(y);
2739 ret = exsltDateFormatDuration(&(res->value.dur));
2740 exsltDateFreeDate(res);
2746 * exsltDateSumFunction:
2747 * @ns: a node set of duration strings
2749 * The date:sum function adds a set of durations together.
2750 * The string values of the nodes in the node set passed as an argument
2751 * are interpreted as durations and added together as if using the
2752 * date:add-duration function. (from exslt.org)
2754 * The return value is a string in the format defined for xs:duration
2755 * in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
2756 * The durations can usually be added by summing the numbers given for
2757 * each of the components in the durations. However, if the durations
2758 * are differently signed, then this sometimes results in durations
2759 * that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
2760 * In these cases, the function returns an empty string ('').
2762 * Returns duration string or NULL.
2765 exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
2770 exsltDateValPtr x, total;
2775 xmlXPathSetArityError (ctxt);
2779 /* We need to delay the freeing of value->user */
2780 if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
2781 user = ctxt->value->user;
2782 ctxt->value->boolval = 0;
2783 ctxt->value->user = NULL;
2786 ns = xmlXPathPopNodeSet (ctxt);
2787 if (xmlXPathCheckError (ctxt))
2790 if ((ns == NULL) || (ns->nodeNr == 0)) {
2791 xmlXPathReturnEmptyString (ctxt);
2793 xmlXPathFreeNodeSet (ns);
2797 total = exsltDateCreateDate (XS_DURATION);
2798 if (total == NULL) {
2799 xmlXPathFreeNodeSet (ns);
2803 for (i = 0; i < ns->nodeNr; i++) {
2805 tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
2807 xmlXPathFreeNodeSet (ns);
2808 exsltDateFreeDate (total);
2812 x = exsltDateParseDuration (tmp);
2815 exsltDateFreeDate (total);
2816 xmlXPathFreeNodeSet (ns);
2817 xmlXPathReturnEmptyString (ctxt);
2821 result = _exsltDateAddDurCalc(total, total, x);
2823 exsltDateFreeDate (x);
2826 exsltDateFreeDate (total);
2827 xmlXPathFreeNodeSet (ns);
2828 xmlXPathReturnEmptyString (ctxt);
2833 ret = exsltDateFormatDuration (&(total->value.dur));
2834 exsltDateFreeDate (total);
2836 xmlXPathFreeNodeSet (ns);
2838 xmlFreeNodeList ((xmlNodePtr) user);
2841 xmlXPathReturnEmptyString (ctxt);
2843 xmlXPathReturnString (ctxt, ret);
2848 * @dateTime: a date/time string
2850 * Implements the EXSLT - Dates and Times seconds() function:
2851 * number date:seconds(string?)
2852 * The date:seconds function returns the number of seconds specified
2853 * by the argument string. If no argument is given, then the current
2854 * local date/time, as returned by exsltDateCurrent() is used as the
2855 * default argument. If the date/time string is a xs:duration, then the
2856 * years and months must be zero (or not present). Parsing a duration
2857 * converts the fields to seconds. If the date/time string is not a
2858 * duration (and not null), then the legal formats are:
2859 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2860 * - xs:date (CCYY-MM-DD)
2861 * - xs:gYearMonth (CCYY-MM)
2863 * In these cases the difference between the @dateTime and
2864 * 1970-01-01T00:00:00Z is calculated and converted to seconds.
2866 * Note that there was some confusion over whether "difference" meant
2867 * that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
2868 * a negative one. After correspondence with exslt.org, it was determined
2869 * that the intent of the specification was to have it positive. The
2870 * coding was modified in July 2003 to reflect this.
2872 * Returns seconds or Nan.
2875 exsltDateSeconds (const xmlChar *dateTime)
2878 double ret = xmlXPathNAN;
2880 if (dateTime == NULL) {
2882 dt = exsltDateCurrent();
2887 dt = exsltDateParseDuration(dateTime);
2889 dt = exsltDateParse(dateTime);
2895 if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
2896 exsltDateValPtr y, dur;
2899 * compute the difference between the given (or current) date
2902 y = exsltDateCreateDate(XS_DATETIME);
2904 y->value.date.year = 1970;
2905 y->value.date.mon = 1;
2906 y->value.date.day = 1;
2907 y->value.date.tz_flag = 1;
2909 dur = _exsltDateDifference(y, dt, 1);
2911 ret = exsltDateCastDateToNumber(dur);
2912 exsltDateFreeDate(dur);
2914 exsltDateFreeDate(y);
2917 } else if ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
2918 ret = exsltDateCastDateToNumber(dt);
2920 exsltDateFreeDate(dt);
2926 * exsltDateDifference:
2927 * @xstr: date/time string
2928 * @ystr: date/time string
2930 * Implements the date:difference (string,string) function which returns
2931 * the duration between the first date and the second date. If the first
2932 * date occurs before the second date, then the result is a positive
2933 * duration; if it occurs after the second date, the result is a
2934 * negative duration. The two dates must both be right-truncated
2935 * date/time strings in one of the formats defined in [XML Schema Part
2936 * 2: Datatypes]. The date/time with the most specific format (i.e. the
2937 * least truncation) is converted into the same format as the date with
2938 * the least specific format (i.e. the most truncation). The permitted
2939 * formats are as follows, from most specific to least specific:
2940 * - xs:dateTime (CCYY-MM-DDThh:mm:ss)
2941 * - xs:date (CCYY-MM-DD)
2942 * - xs:gYearMonth (CCYY-MM)
2944 * If either of the arguments is not in one of these formats,
2945 * date:difference returns the empty string ('').
2946 * The difference between the date/times is returned as a string in the
2947 * format defined for xs:duration in [3.2.6 duration] of [XML Schema
2948 * Part 2: Datatypes].
2949 * If the date/time string with the least specific format is in either
2950 * xs:gYearMonth or xs:gYear format, then the number of days, hours,
2951 * minutes and seconds in the duration string must be equal to zero.
2952 * (The format of the string will be PnYnM.) The number of months
2953 * specified in the duration must be less than 12.
2954 * Otherwise, the number of years and months in the duration string
2955 * must be equal to zero. (The format of the string will be
2956 * PnDTnHnMnS.) The number of seconds specified in the duration string
2957 * must be less than 60; the number of minutes must be less than 60;
2958 * the number of hours must be less than 24.
2960 * Returns duration string or NULL.
2963 exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
2965 exsltDateValPtr x, y, dur;
2966 xmlChar *ret = NULL;
2968 if ((xstr == NULL) || (ystr == NULL))
2971 x = exsltDateParse(xstr);
2975 y = exsltDateParse(ystr);
2977 exsltDateFreeDate(x);
2981 if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
2982 ((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) {
2983 exsltDateFreeDate(x);
2984 exsltDateFreeDate(y);
2988 dur = _exsltDateDifference(x, y, 0);
2990 exsltDateFreeDate(x);
2991 exsltDateFreeDate(y);
2996 ret = exsltDateFormatDuration(&(dur->value.dur));
2997 exsltDateFreeDate(dur);
3003 * exsltDateDuration:
3004 * @number: a xmlChar string
3006 * Implements the The date:duration function returns a duration string
3007 * representing the number of seconds specified by the argument string.
3008 * If no argument is given, then the result of calling date:seconds
3009 * without any arguments is used as a default argument.
3010 * The duration is returned as a string in the format defined for
3011 * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
3012 * The number of years and months in the duration string must be equal
3013 * to zero. (The format of the string will be PnDTnHnMnS.) The number
3014 * of seconds specified in the duration string must be less than 60;
3015 * the number of minutes must be less than 60; the number of hours must
3017 * If the argument is Infinity, -Infinity or NaN, then date:duration
3018 * returns an empty string ('').
3020 * Returns duration string or NULL.
3023 exsltDateDuration (const xmlChar *number)
3025 exsltDateValPtr dur;
3030 secs = exsltDateSeconds(number);
3032 secs = xmlXPathCastStringToNumber(number);
3034 if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
3037 dur = exsltDateCreateDate(XS_DURATION);
3041 dur->value.dur.sec = secs;
3043 ret = exsltDateFormatDuration(&(dur->value.dur));
3044 exsltDateFreeDate(dur);
3049 /****************************************************************
3051 * Wrappers for use by the XPath engine *
3053 ****************************************************************/
3057 * exsltDateDateTimeFunction:
3058 * @ctxt: an XPath parser context
3059 * @nargs : the number of arguments
3061 * Wraps exsltDateDateTime() for use by the XPath engine.
3064 exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3069 xmlXPathSetArityError(ctxt);
3073 ret = exsltDateDateTime();
3075 xmlXPathReturnEmptyString(ctxt);
3077 xmlXPathReturnString(ctxt, ret);
3082 * exsltDateDateFunction:
3083 * @ctxt: an XPath parser context
3084 * @nargs : the number of arguments
3086 * Wraps exsltDateDate() for use by the XPath engine.
3089 exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
3091 xmlChar *ret, *dt = NULL;
3093 if ((nargs < 0) || (nargs > 1)) {
3094 xmlXPathSetArityError(ctxt);
3098 dt = xmlXPathPopString(ctxt);
3099 if (xmlXPathCheckError(ctxt)) {
3100 xmlXPathSetTypeError(ctxt);
3105 ret = exsltDateDate(dt);
3108 xsltGenericDebug(xsltGenericDebugContext,
3109 "{http://exslt.org/dates-and-times}date: "
3110 "invalid date or format %s\n", dt);
3111 xmlXPathReturnEmptyString(ctxt);
3113 xmlXPathReturnString(ctxt, ret);
3121 * exsltDateTimeFunction:
3122 * @ctxt: an XPath parser context
3123 * @nargs : the number of arguments
3125 * Wraps exsltDateTime() for use by the XPath engine.
3128 exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
3130 xmlChar *ret, *dt = NULL;
3132 if ((nargs < 0) || (nargs > 1)) {
3133 xmlXPathSetArityError(ctxt);
3137 dt = xmlXPathPopString(ctxt);
3138 if (xmlXPathCheckError(ctxt)) {
3139 xmlXPathSetTypeError(ctxt);
3144 ret = exsltDateTime(dt);
3147 xsltGenericDebug(xsltGenericDebugContext,
3148 "{http://exslt.org/dates-and-times}time: "
3149 "invalid date or format %s\n", dt);
3150 xmlXPathReturnEmptyString(ctxt);
3152 xmlXPathReturnString(ctxt, ret);
3160 * exsltDateYearFunction:
3161 * @ctxt: an XPath parser context
3162 * @nargs : the number of arguments
3164 * Wraps exsltDateYear() for use by the XPath engine.
3167 exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3172 if ((nargs < 0) || (nargs > 1)) {
3173 xmlXPathSetArityError(ctxt);
3178 dt = xmlXPathPopString(ctxt);
3179 if (xmlXPathCheckError(ctxt)) {
3180 xmlXPathSetTypeError(ctxt);
3185 ret = exsltDateYear(dt);
3190 xmlXPathReturnNumber(ctxt, ret);
3194 * exsltDateLeapYearFunction:
3195 * @ctxt: an XPath parser context
3196 * @nargs : the number of arguments
3198 * Wraps exsltDateLeapYear() for use by the XPath engine.
3201 exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
3204 xmlXPathObjectPtr ret;
3206 if ((nargs < 0) || (nargs > 1)) {
3207 xmlXPathSetArityError(ctxt);
3212 dt = xmlXPathPopString(ctxt);
3213 if (xmlXPathCheckError(ctxt)) {
3214 xmlXPathSetTypeError(ctxt);
3219 ret = exsltDateLeapYear(dt);
3224 valuePush(ctxt, ret);
3227 #define X_IN_Y(x, y) \
3229 exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \
3231 xmlChar *dt = NULL; \
3234 if ((nargs < 0) || (nargs > 1)) { \
3235 xmlXPathSetArityError(ctxt); \
3240 dt = xmlXPathPopString(ctxt); \
3241 if (xmlXPathCheckError(ctxt)) { \
3242 xmlXPathSetTypeError(ctxt); \
3247 ret = exsltDate##x##In##y(dt); \
3252 xmlXPathReturnNumber(ctxt, ret); \
3256 * exsltDateMonthInYearFunction:
3257 * @ctxt: an XPath parser context
3258 * @nargs : the number of arguments
3260 * Wraps exsltDateMonthInYear() for use by the XPath engine.
3265 * exsltDateMonthNameFunction:
3266 * @ctxt: an XPath parser context
3267 * @nargs : the number of arguments
3269 * Wraps exsltDateMonthName() for use by the XPath engine.
3272 exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3277 if ((nargs < 0) || (nargs > 1)) {
3278 xmlXPathSetArityError(ctxt);
3283 dt = xmlXPathPopString(ctxt);
3284 if (xmlXPathCheckError(ctxt)) {
3285 xmlXPathSetTypeError(ctxt);
3290 ret = exsltDateMonthName(dt);
3296 xmlXPathReturnEmptyString(ctxt);
3298 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3302 * exsltDateMonthAbbreviationFunction:
3303 * @ctxt: an XPath parser context
3304 * @nargs : the number of arguments
3306 * Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
3309 exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3314 if ((nargs < 0) || (nargs > 1)) {
3315 xmlXPathSetArityError(ctxt);
3320 dt = xmlXPathPopString(ctxt);
3321 if (xmlXPathCheckError(ctxt)) {
3322 xmlXPathSetTypeError(ctxt);
3327 ret = exsltDateMonthAbbreviation(dt);
3333 xmlXPathReturnEmptyString(ctxt);
3335 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3339 * exsltDateWeekInYearFunction:
3340 * @ctxt: an XPath parser context
3341 * @nargs : the number of arguments
3343 * Wraps exsltDateWeekInYear() for use by the XPath engine.
3348 * exsltDateWeekInMonthFunction:
3349 * @ctxt: an XPath parser context
3350 * @nargs : the number of arguments
3352 * Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
3357 * exsltDateDayInYearFunction:
3358 * @ctxt: an XPath parser context
3359 * @nargs : the number of arguments
3361 * Wraps exsltDateDayInYear() for use by the XPath engine.
3366 * exsltDateDayInMonthFunction:
3367 * @ctxt: an XPath parser context
3368 * @nargs : the number of arguments
3370 * Wraps exsltDateDayInMonth() for use by the XPath engine.
3375 * exsltDateDayOfWeekInMonthFunction:
3376 * @ctxt: an XPath parser context
3377 * @nargs : the number of arguments
3379 * Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
3381 X_IN_Y(DayOfWeek,Month)
3384 * exsltDateDayInWeekFunction:
3385 * @ctxt: an XPath parser context
3386 * @nargs : the number of arguments
3388 * Wraps exsltDateDayInWeek() for use by the XPath engine.
3393 * exsltDateDayNameFunction:
3394 * @ctxt: an XPath parser context
3395 * @nargs : the number of arguments
3397 * Wraps exsltDateDayName() for use by the XPath engine.
3400 exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
3405 if ((nargs < 0) || (nargs > 1)) {
3406 xmlXPathSetArityError(ctxt);
3411 dt = xmlXPathPopString(ctxt);
3412 if (xmlXPathCheckError(ctxt)) {
3413 xmlXPathSetTypeError(ctxt);
3418 ret = exsltDateDayName(dt);
3424 xmlXPathReturnEmptyString(ctxt);
3426 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3430 * exsltDateMonthDayFunction:
3431 * @ctxt: an XPath parser context
3432 * @nargs : the number of arguments
3434 * Wraps exsltDateDayAbbreviation() for use by the XPath engine.
3437 exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3442 if ((nargs < 0) || (nargs > 1)) {
3443 xmlXPathSetArityError(ctxt);
3448 dt = xmlXPathPopString(ctxt);
3449 if (xmlXPathCheckError(ctxt)) {
3450 xmlXPathSetTypeError(ctxt);
3455 ret = exsltDateDayAbbreviation(dt);
3461 xmlXPathReturnEmptyString(ctxt);
3463 xmlXPathReturnString(ctxt, xmlStrdup(ret));
3468 * exsltDateHourInDayFunction:
3469 * @ctxt: an XPath parser context
3470 * @nargs : the number of arguments
3472 * Wraps exsltDateHourInDay() for use by the XPath engine.
3477 * exsltDateMinuteInHourFunction:
3478 * @ctxt: an XPath parser context
3479 * @nargs : the number of arguments
3481 * Wraps exsltDateMinuteInHour() for use by the XPath engine.
3486 * exsltDateSecondInMinuteFunction:
3487 * @ctxt: an XPath parser context
3488 * @nargs : the number of arguments
3490 * Wraps exsltDateSecondInMinute() for use by the XPath engine.
3492 X_IN_Y(Second,Minute)
3495 * exsltDateSecondsFunction:
3496 * @ctxt: an XPath parser context
3497 * @nargs : the number of arguments
3499 * Wraps exsltDateSeconds() for use by the XPath engine.
3502 exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
3504 xmlChar *str = NULL;
3508 xmlXPathSetArityError(ctxt);
3513 str = xmlXPathPopString(ctxt);
3514 if (xmlXPathCheckError(ctxt)) {
3515 xmlXPathSetTypeError(ctxt);
3520 ret = exsltDateSeconds(str);
3524 xmlXPathReturnNumber(ctxt, ret);
3528 * exsltDateAddFunction:
3529 * @ctxt: an XPath parser context
3530 * @nargs: the number of arguments
3532 * Wraps exsltDateAdd() for use by the XPath processor.
3535 exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
3537 xmlChar *ret, *xstr, *ystr;
3540 xmlXPathSetArityError(ctxt);
3543 ystr = xmlXPathPopString(ctxt);
3544 if (xmlXPathCheckError(ctxt))
3547 xstr = xmlXPathPopString(ctxt);
3548 if (xmlXPathCheckError(ctxt)) {
3553 ret = exsltDateAdd(xstr, ystr);
3559 xmlXPathReturnEmptyString(ctxt);
3561 xmlXPathReturnString(ctxt, ret);
3565 * exsltDateAddDurationFunction:
3566 * @ctxt: an XPath parser context
3567 * @nargs: the number of arguments
3569 * Wraps exsltDateAddDuration() for use by the XPath processor.
3572 exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3574 xmlChar *ret, *xstr, *ystr;
3577 xmlXPathSetArityError(ctxt);
3580 ystr = xmlXPathPopString(ctxt);
3581 if (xmlXPathCheckError(ctxt))
3584 xstr = xmlXPathPopString(ctxt);
3585 if (xmlXPathCheckError(ctxt)) {
3590 ret = exsltDateAddDuration(xstr, ystr);
3596 xmlXPathReturnEmptyString(ctxt);
3598 xmlXPathReturnString(ctxt, ret);
3602 * exsltDateDifferenceFunction:
3603 * @ctxt: an XPath parser context
3604 * @nargs: the number of arguments
3606 * Wraps exsltDateDifference() for use by the XPath processor.
3609 exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
3611 xmlChar *ret, *xstr, *ystr;
3614 xmlXPathSetArityError(ctxt);
3617 ystr = xmlXPathPopString(ctxt);
3618 if (xmlXPathCheckError(ctxt))
3621 xstr = xmlXPathPopString(ctxt);
3622 if (xmlXPathCheckError(ctxt)) {
3627 ret = exsltDateDifference(xstr, ystr);
3633 xmlXPathReturnEmptyString(ctxt);
3635 xmlXPathReturnString(ctxt, ret);
3639 * exsltDateDurationFunction:
3640 * @ctxt: an XPath parser context
3641 * @nargs : the number of arguments
3643 * Wraps exsltDateDuration() for use by the XPath engine
3646 exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
3649 xmlChar *number = NULL;
3651 if ((nargs < 0) || (nargs > 1)) {
3652 xmlXPathSetArityError(ctxt);
3657 number = xmlXPathPopString(ctxt);
3658 if (xmlXPathCheckError(ctxt)) {
3659 xmlXPathSetTypeError(ctxt);
3664 ret = exsltDateDuration(number);
3670 xmlXPathReturnEmptyString(ctxt);
3672 xmlXPathReturnString(ctxt, ret);
3676 * exsltDateRegister:
3678 * Registers the EXSLT - Dates and Times module
3681 exsltDateRegister (void)
3683 xsltRegisterExtModuleFunction ((const xmlChar *) "add",
3684 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3685 exsltDateAddFunction);
3686 xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
3687 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3688 exsltDateAddDurationFunction);
3689 xsltRegisterExtModuleFunction ((const xmlChar *) "date",
3690 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3691 exsltDateDateFunction);
3693 xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
3694 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3695 exsltDateDateTimeFunction);
3697 xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
3698 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3699 exsltDateDayAbbreviationFunction);
3700 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
3701 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3702 exsltDateDayInMonthFunction);
3703 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
3704 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3705 exsltDateDayInWeekFunction);
3706 xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
3707 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3708 exsltDateDayInYearFunction);
3709 xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
3710 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3711 exsltDateDayNameFunction);
3712 xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
3713 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3714 exsltDateDayOfWeekInMonthFunction);
3715 xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
3716 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3717 exsltDateDifferenceFunction);
3718 xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
3719 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3720 exsltDateDurationFunction);
3721 xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
3722 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3723 exsltDateHourInDayFunction);
3724 xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
3725 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3726 exsltDateLeapYearFunction);
3727 xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
3728 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3729 exsltDateMinuteInHourFunction);
3730 xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
3731 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3732 exsltDateMonthAbbreviationFunction);
3733 xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
3734 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3735 exsltDateMonthInYearFunction);
3736 xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
3737 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3738 exsltDateMonthNameFunction);
3739 xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
3740 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3741 exsltDateSecondInMinuteFunction);
3742 xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
3743 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3744 exsltDateSecondsFunction);
3745 xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
3746 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3747 exsltDateSumFunction);
3748 xsltRegisterExtModuleFunction ((const xmlChar *) "time",
3749 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3750 exsltDateTimeFunction);
3751 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
3752 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3753 exsltDateWeekInMonthFunction);
3754 xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
3755 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3756 exsltDateWeekInYearFunction);
3757 xsltRegisterExtModuleFunction ((const xmlChar *) "year",
3758 (const xmlChar *) EXSLT_DATE_NAMESPACE,
3759 exsltDateYearFunction);