/*
* date.c: Implementation of the EXSLT -- Dates and Times module
*
* References:
* http://www.exslt.org/date/date.html
*
* See Copyright for the status of this software.
*
* Authors:
* Charlie Bozeman <[email protected]>
* Thomas Broyer <[email protected]>
*
* TODO:
* elements:
* date-format
* functions:
* format-date
* parse-date
* sum
*/
#define IN_LIBEXSLT
#include "libexslt/libexslt.h"
#if defined(HAVE_LOCALTIME_R) && defined(__GLIBC__) /* _POSIX_SOURCE required by gnu libc */
#ifndef _AIX51 /* but on AIX we're not using gnu libc */
#define _POSIX_SOURCE
#endif
#endif
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxslt/xsltutils.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/extensions.h>
#include "exslt.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <math.h>
/* needed to get localtime_r on Solaris */
#ifdef __sun
#ifndef __EXTENSIONS__
#define __EXTENSIONS__
#endif
#endif
#include <time.h>
#if defined(_MSC_VER) && _MSC_VER >= 1400 || \
defined(_WIN32) && \
defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 4
#define HAVE_MSVCRT
#endif
/*
* types of date and/or time (from schema datatypes)
* somewhat ordered from least specific to most specific (i.e.
* most truncated to least truncated).
*/
typedef enum {
EXSLT_UNKNOWN = 0,
XS_TIME = 1, /* time is left-truncated */
XS_GDAY = (XS_TIME << 1),
XS_GMONTH = (XS_GDAY << 1),
XS_GMONTHDAY = (XS_GMONTH | XS_GDAY),
XS_GYEAR = (XS_GMONTH << 1),
XS_GYEARMONTH = (XS_GYEAR | XS_GMONTH),
XS_DATE = (XS_GYEAR | XS_GMONTH | XS_GDAY),
XS_DATETIME = (XS_DATE | XS_TIME)
} exsltDateType;
/* Date value */
typedef struct _exsltDateVal exsltDateVal;
typedef exsltDateVal *exsltDateValPtr;
struct _exsltDateVal {
exsltDateType type;
long year;
unsigned int mon :4; /* 1 <= mon <= 12 */
unsigned int day :5; /* 1 <= day <= 31 */
unsigned int hour :5; /* 0 <= hour <= 23 */
unsigned int min :6; /* 0 <= min <= 59 */
double sec;
unsigned int tz_flag :1; /* is tzo explicitely set? */
signed int tzo :12; /* -1440 <= tzo <= 1440 currently only -840 to +840 are needed */
};
/* Duration value */
typedef struct _exsltDateDurVal exsltDateDurVal;
typedef exsltDateDurVal *exsltDateDurValPtr;
struct _exsltDateDurVal {
long mon; /* mon stores years also */
long day;
double sec; /* sec stores min and hour also
0 <= sec < SECS_PER_DAY */
};
/****************************************************************
* *
* Convenience macros and functions *
* *
****************************************************************/
#define IS_TZO_CHAR(c) \
((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
#define VALID_ALWAYS(num) (num >= 0)
#define VALID_MONTH(mon) ((mon >= 1) && (mon <= 12))
/* VALID_DAY should only be used when month is unknown */
#define VALID_DAY(day) ((day >= 1) && (day <= 31))
#define VALID_HOUR(hr) ((hr >= 0) && (hr <= 23))
#define VALID_MIN(min) ((min >= 0) && (min <= 59))
#define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
#define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
#define IS_LEAP(y) \
(((y & 3) == 0) && ((y % 25 != 0) || ((y & 15) == 0)))
static const long daysInMonth[12] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static const long daysInMonthLeap[12] =
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
#define MAX_DAYINMONTH(yr,mon) \
(IS_LEAP(yr) ? daysInMonthLeap[mon - 1] : daysInMonth[mon - 1])
#define VALID_MDAY(dt) \
(IS_LEAP(dt->year) ? \
(dt->day <= daysInMonthLeap[dt->mon - 1]) : \
(dt->day <= daysInMonth[dt->mon - 1]))
#define VALID_DATE(dt) \
(VALID_MONTH(dt->mon) && VALID_MDAY(dt))
/*
hour and min structure vals are unsigned, so normal macros give
warnings on some compilers.
*/
#define VALID_TIME(dt) \
((dt->hour <=23 ) && (dt->min <= 59) && \
VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
#define VALID_DATETIME(dt) \
(VALID_DATE(dt) && VALID_TIME(dt))
#define SECS_PER_MIN 60
#define MINS_PER_HOUR 60
#define HOURS_PER_DAY 24
#define SECS_PER_HOUR (MINS_PER_HOUR * SECS_PER_MIN)
#define SECS_PER_DAY (HOURS_PER_DAY * SECS_PER_HOUR)
#define MINS_PER_DAY (HOURS_PER_DAY * MINS_PER_HOUR)
#define DAYS_PER_EPOCH (400 * 365 + 100 - 4 + 1)
#define YEARS_PER_EPOCH 400
static const long dayInYearByMonth[12] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
static const long dayInLeapYearByMonth[12] =
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };
#define DAY_IN_YEAR(day, month, year) \
((IS_LEAP(year) ? \
dayInLeapYearByMonth[month - 1] : \
dayInYearByMonth[month - 1]) + day)
#define YEAR_MAX LONG_MAX
#define YEAR_MIN (-LONG_MAX + 1)
/**
* _exsltDateParseGYear:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
*
* Parses a xs:gYear without time zone and fills in the appropriate
* field of the @dt structure. @str is updated to point just after the
* xs:gYear. It is supposed that @dt->year is big enough to contain
* the year.
*
* According to XML Schema Part 2, the year "0000" is an illegal year value
* which probably means that the year preceding AD 1 is BC 1. Internally,
* we allow a year 0 and adjust the value when parsing and formatting.
*
* Returns 0 or the error code
*/
static int
_exsltDateParseGYear (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str, *firstChar;
int isneg = 0, digcnt = 0;
if (((*cur < '0') || (*cur > '9')) &&
(*cur != '-') && (*cur != '+'))
return -1;
if (*cur == '-') {
isneg = 1;
cur++;
}
firstChar = cur;
while ((*cur >= '0') && (*cur <= '9')) {
if (dt->year >= YEAR_MAX / 10) /* Not really exact */
return -1;
dt->year = dt->year * 10 + (*cur - '0');
cur++;
digcnt++;
}
/* year must be at least 4 digits (CCYY); over 4
* digits cannot have a leading zero. */
if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
return 1;
if (dt->year == 0)
return 2;
/* The internal representation of negative years is continuous. */
if (isneg)
dt->year = -dt->year + 1;
*str = cur;
#ifdef DEBUG_EXSLT_DATE
xsltGenericDebug(xsltGenericDebugContext,
"Parsed year %04ld\n", dt->year);
#endif
return 0;
}
/**
* exsltFormatGYear:
* @cur: a pointer to a pointer to an allocated buffer
* @end: a pointer to the end of @cur buffer
* @yr: the year to format
*
* Formats @yr in xsl:gYear format. Result is appended to @cur and
* @cur is updated to point after the xsl:gYear.
*/
static void
exsltFormatGYear(xmlChar **cur, xmlChar *end, long yr)
{
long year;
xmlChar tmp_buf[100], *tmp = tmp_buf, *tmp_end = tmp_buf + 99;
if (yr <= 0 && *cur < end) {
*(*cur)++ = '-';
}
year = (yr <= 0) ? -yr + 1 : yr;
/* result is in reverse-order */
while (year > 0 && tmp < tmp_end) {
*tmp++ = '0' + (xmlChar)(year % 10);
year /= 10;
}
/* virtually adds leading zeros */
while ((tmp - tmp_buf) < 4)
*tmp++ = '0';
/* restore the correct order */
while (tmp > tmp_buf && *cur < end) {
tmp--;
*(*cur)++ = *tmp;
}
}
/**
* PARSE_2_DIGITS:
* @num: the integer to fill in
* @cur: an #xmlChar *
* @func: validation function for the number
* @invalid: an integer
*
* Parses a 2-digits integer and updates @num with the value. @cur is
* updated to point just after the integer.
* In case of error, @invalid is set to %TRUE, values of @num and
* @cur are undefined.
*/
#define PARSE_2_DIGITS(num, cur, func, invalid) \
if ((cur[0] < '0') || (cur[0] > '9') || \
(cur[1] < '0') || (cur[1] > '9')) \
invalid = 1; \
else { \
int val; \
val = (cur[0] - '0') * 10 + (cur[1] - '0'); \
if (!func(val)) \
invalid = 2; \
else \
num = val; \
} \
cur += 2;
/**
* exsltFormat2Digits:
* @cur: a pointer to a pointer to an allocated buffer
* @end: a pointer to the end of @cur buffer
* @num: the integer to format
*
* Formats a 2-digits integer. Result is appended to @cur and
* @cur is updated to point after the integer.
*/
static void
exsltFormat2Digits(xmlChar **cur, xmlChar *end, unsigned int num)
{
if (*cur < end)
*(*cur)++ = '0' + ((num / 10) % 10);
if (*cur < end)
*(*cur)++ = '0' + (num % 10);
}
/**
* PARSE_FLOAT:
* @num: the double to fill in
* @cur: an #xmlChar *
* @invalid: an integer
*
* Parses a float and updates @num with the value. @cur is
* updated to point just after the float. The float must have a
* 2-digits integer part and may or may not have a decimal part.
* In case of error, @invalid is set to %TRUE, values of @num and
* @cur are undefined.
*/
#define PARSE_FLOAT(num, cur, invalid) \
PARSE_2_DIGITS(num, cur, VALID_ALWAYS, invalid); \
if (!invalid && (*cur == '.')) { \
double mult = 1; \
cur++; \
if ((*cur < '0') || (*cur > '9')) \
invalid = 1; \
while ((*cur >= '0') && (*cur <= '9')) { \
mult /= 10; \
num += (*cur - '0') * mult; \
cur++; \
} \
}
/**
* _exsltDateParseGMonth:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
*
* Parses a xs:gMonth without time zone and fills in the appropriate
* field of the @dt structure. @str is updated to point just after the
* xs:gMonth.
*
* Returns 0 or the error code
*/
static int
_exsltDateParseGMonth (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str;
int ret = 0;
PARSE_2_DIGITS(dt->mon, cur, VALID_MONTH, ret);
if (ret != 0)
return ret;
*str = cur;
#ifdef DEBUG_EXSLT_DATE
xsltGenericDebug(xsltGenericDebugContext,
"Parsed month %02i\n", dt->mon);
#endif
return 0;
}
/**
* _exsltDateParseGDay:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
*
* Parses a xs:gDay without time zone and fills in the appropriate
* field of the @dt structure. @str is updated to point just after the
* xs:gDay.
*
* Returns 0 or the error code
*/
static int
_exsltDateParseGDay (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str;
int ret = 0;
PARSE_2_DIGITS(dt->day, cur, VALID_DAY, ret);
if (ret != 0)
return ret;
*str = cur;
#ifdef DEBUG_EXSLT_DATE
xsltGenericDebug(xsltGenericDebugContext,
"Parsed day %02i\n", dt->day);
#endif
return 0;
}
/**
* exsltFormatYearMonthDay:
* @cur: a pointer to a pointer to an allocated buffer
* @end: a pointer to the end of @cur buffer
* @dt: the #exsltDateVal to format
*
* Formats @dt in xsl:date format. Result is appended to @cur and
* @cur is updated to point after the xsl:date.
*/
static void
exsltFormatYearMonthDay(xmlChar **cur, xmlChar *end, const exsltDateValPtr dt)
{
exsltFormatGYear(cur, end, dt->year);
if (*cur < end)
*(*cur)++ = '-';
exsltFormat2Digits(cur, end, dt->mon);
if (*cur < end)
*(*cur)++ = '-';
exsltFormat2Digits(cur, end, dt->day);
}
/**
* _exsltDateParseTime:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
*
* Parses a xs:time without time zone and fills in the appropriate
* fields of the @dt structure. @str is updated to point just after the
* xs:time.
* In case of error, values of @dt fields are undefined.
*
* Returns 0 or the error code
*/
static int
_exsltDateParseTime (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str;
unsigned int hour = 0; /* use temp var in case str is not xs:time */
int ret = 0;
PARSE_2_DIGITS(hour, cur, VALID_HOUR, ret);
if (ret != 0)
return ret;
if (*cur != ':')
return 1;
cur++;
/* the ':' insures this string is xs:time */
dt->hour = hour;
PARSE_2_DIGITS(dt->min, cur, VALID_MIN, ret);
if (ret != 0)
return ret;
if (*cur != ':')
return 1;
cur++;
PARSE_FLOAT(dt->sec, cur, ret);
if (ret != 0)
return ret;
if (!VALID_TIME(dt))
return 2;
*str = cur;
#ifdef DEBUG_EXSLT_DATE
xsltGenericDebug(xsltGenericDebugContext,
"Parsed time %02i:%02i:%02.f\n",
dt->hour, dt->min, dt->sec);
#endif
return 0;
}
/**
* _exsltDateParseTimeZone:
* @dt: pointer to a date structure
* @str: pointer to the string to analyze
*
* Parses a time zone without time zone and fills in the appropriate
* field of the @dt structure. @str is updated to point just after the
* time zone.
*
* Returns 0 or the error code
*/
static int
_exsltDateParseTimeZone (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur;
int ret = 0;
if (str == NULL)
return -1;
cur = *str;
switch (*cur) {
case 0:
dt->tz_flag = 0;
dt->tzo = 0;
break;
case 'Z':
dt->tz_flag = 1;
dt->tzo = 0;
cur++;
break;
case '+':
case '-': {
int isneg = 0, tmp = 0;
isneg = (*cur == '-');
cur++;
PARSE_2_DIGITS(tmp, cur, VALID_HOUR, ret);
if (ret != 0)
return ret;
if (*cur != ':')
return 1;
cur++;
dt->tzo = tmp * 60;
PARSE_2_DIGITS(tmp, cur, VALID_MIN, ret);
if (ret != 0)
return ret;
dt->tzo += tmp;
if (isneg)
dt->tzo = - dt->tzo;
if (!VALID_TZO(dt->tzo))
return 2;
break;
}
default:
return 1;
}
*str = cur;
#ifdef DEBUG_EXSLT_DATE
xsltGenericDebug(xsltGenericDebugContext,
"Parsed time zone offset (%s) %i\n",
dt->tz_flag ? "explicit" : "implicit", dt->tzo);
#endif
return 0;
}
/**
* exsltFormatTimeZone:
* @cur: a pointer to a pointer to an allocated buffer
* @end: a pointer to the end of @cur buffer
* @tzo: the timezone offset to format
*
* Formats @tzo timezone. Result is appended to @cur and
* @cur is updated to point after the timezone.
*/
static void
exsltFormatTimeZone(xmlChar **cur, xmlChar *end, int tzo)
{
if (tzo == 0) {
if (*cur < end)
*(*cur)++ = 'Z';
} else {
unsigned int aTzo = (tzo < 0) ? -tzo : tzo;
unsigned int tzHh = aTzo / 60, tzMm = aTzo % 60;
if (*cur < end)
*(*cur)++ = (tzo < 0) ? '-' : '+';
exsltFormat2Digits(cur, end, tzHh);
if (*cur < end)
*(*cur)++ = ':';
exsltFormat2Digits(cur, end, tzMm);
}
}
/****************************************************************
* *
* XML Schema Dates/Times Datatypes Handling *
* *
****************************************************************/
/**
* exsltDateCreateDate:
* @type: type to create
*
* Creates a new #exsltDateVal, uninitialized.
*
* Returns the #exsltDateValPtr
*/
static exsltDateValPtr
exsltDateCreateDate (exsltDateType type)
{
exsltDateValPtr ret;
ret = (exsltDateValPtr) xmlMalloc(sizeof(exsltDateVal));
if (ret == NULL) {
xsltGenericError(xsltGenericErrorContext,
"exsltDateCreateDate: out of memory\n");
return (NULL);
}
memset (ret, 0, sizeof(exsltDateVal));
ret->mon = 1;
ret->day = 1;
if (type != EXSLT_UNKNOWN)
ret->type = type;
return ret;
}
/**
* exsltDateFreeDate:
* @date: an #exsltDateValPtr
*
* Frees up the @date
*/
static void
exsltDateFreeDate (exsltDateValPtr date) {
if (date == NULL)
return;
xmlFree(date);
}
/**
* exsltDateCreateDuration:
*
* Creates a new #exsltDateDurVal, uninitialized.
*
* Returns the #exsltDateDurValPtr
*/
static exsltDateDurValPtr
exsltDateCreateDuration (void)
{
exsltDateDurValPtr ret;
ret = (exsltDateDurValPtr) xmlMalloc(sizeof(exsltDateDurVal));
if (ret == NULL) {
xsltGenericError(xsltGenericErrorContext,
"exsltDateCreateDuration: out of memory\n");
return (NULL);
}
memset (ret, 0, sizeof(exsltDateDurVal));
return ret;
}
/**
* exsltDateFreeDuration:
* @date: an #exsltDateDurValPtr
*
* Frees up the @duration
*/
static void
exsltDateFreeDuration (exsltDateDurValPtr duration) {
if (duration == NULL)
return;
xmlFree(duration);
}
/**
* exsltDateCurrent:
*
* Returns the current date and time.
*/
static exsltDateValPtr
exsltDateCurrent (void)
{
struct tm localTm, gmTm;
#if !defined(HAVE_GMTIME_R) && !defined(HAVE_MSVCRT)
struct tm *tb = NULL;
#endif
time_t secs;
int local_s, gm_s;
exsltDateValPtr ret;
char *source_date_epoch;
int override = 0;
ret = exsltDateCreateDate(XS_DATETIME);
if (ret == NULL)
return NULL;
/*
* Allow the date and time to be set externally by an exported
* environment variable to enable reproducible builds.
*/
source_date_epoch = getenv("SOURCE_DATE_EPOCH");
if (source_date_epoch) {
errno = 0;
secs = (time_t) strtol (source_date_epoch, NULL, 10);
if (errno == 0) {
#ifdef HAVE_MSVCRT
struct tm *gm = gmtime_s(&localTm, &secs) ? NULL : &localTm;
if (gm != NULL)
override = 1;
#elif HAVE_GMTIME_R
if (gmtime_r(&secs, &localTm) != NULL)
override = 1;
#else
tb = gmtime(&secs);
if (tb != NULL) {
localTm = *tb;
override = 1;
}
#endif
}
}
if (override == 0) {
/* get current time */
secs = time(NULL);
#ifdef HAVE_MSVCRT
localtime_s(&localTm, &secs);
#elif HAVE_LOCALTIME_R
localtime_r(&secs, &localTm);
#else
localTm = *localtime(&secs);
#endif
}
/* get real year, not years since 1900 */
ret->year = localTm.tm_year + 1900;
ret->mon = localTm.tm_mon + 1;
ret->day = localTm.tm_mday;
ret->hour = localTm.tm_hour;
ret->min = localTm.tm_min;
/* floating point seconds */
ret->sec = (double) localTm.tm_sec;
/* determine the time zone offset from local to gm time */
#ifdef HAVE_MSVCRT
gmtime_s(&gmTm, &secs);
#elif HAVE_GMTIME_R
gmtime_r(&secs, &gmTm);
#else
tb = gmtime(&secs);
if (tb == NULL)
return NULL;
gmTm = *tb;
#endif
ret->tz_flag = 0;
#if 0
ret->tzo = (((ret->day * 1440) +
(ret->hour * 60) +
ret->min) -
((gmTm.tm_mday * 1440) + (gmTm.tm_hour * 60) +
gmTm.tm_min));
#endif
local_s = localTm.tm_hour * SECS_PER_HOUR +
localTm.tm_min * SECS_PER_MIN +
localTm.tm_sec;
gm_s = gmTm.tm_hour * SECS_PER_HOUR +
gmTm.tm_min * SECS_PER_MIN +
gmTm.tm_sec;
if (localTm.tm_year < gmTm.tm_year) {
ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
} else if (localTm.tm_year > gmTm.tm_year) {
ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
} else if (localTm.tm_mon < gmTm.tm_mon) {
ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
} else if (localTm.tm_mon > gmTm.tm_mon) {
ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
} else if (localTm.tm_mday < gmTm.tm_mday) {
ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
} else if (localTm.tm_mday > gmTm.tm_mday) {
ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
} else {
ret->tzo = (local_s - gm_s)/60;
}
return ret;
}
/**
* exsltDateParse:
* @dateTime: string to analyze
*
* Parses a date/time string
*
* Returns a newly built #exsltDateValPtr of NULL in case of error
*/
static exsltDateValPtr
exsltDateParse (const xmlChar *dateTime)
{
exsltDateValPtr dt;
int ret;
const xmlChar *cur = dateTime;
#define RETURN_TYPE_IF_VALID(t) \
if (IS_TZO_CHAR(*cur)) { \
ret = _exsltDateParseTimeZone(dt, &cur); \
if (ret == 0) { \
if (*cur != 0) \
goto error; \
dt->type = t; \
return dt; \
} \
}
if (dateTime == NULL)
return NULL;
if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
return NULL;
dt = exsltDateCreateDate(EXSLT_UNKNOWN);
if (dt == NULL)
return NULL;
if ((cur[0] == '-') && (cur[1] == '-')) {
/*
* It's an incomplete date (xs:gMonthDay, xs:gMonth or
* xs:gDay)
*/
cur += 2;
/* is it an xs:gDay? */
if (*cur == '-') {
++cur;
ret = _exsltDateParseGDay(dt, &cur);
if (ret != 0)
goto error;
RETURN_TYPE_IF_VALID(XS_GDAY);
goto error;
}
/*
* it should be an xs:gMonthDay or xs:gMonth
*/
ret = _exsltDateParseGMonth(dt, &cur);
if (ret != 0)
goto error;
if (*cur != '-')
goto error;
cur++;
/* is it an xs:gMonth? */
if (*cur == '-') {
cur++;
RETURN_TYPE_IF_VALID(XS_GMONTH);
goto error;
}
/* it should be an xs:gMonthDay */
ret = _exsltDateParseGDay(dt, &cur);
if (ret != 0)
goto error;
RETURN_TYPE_IF_VALID(XS_GMONTHDAY);
goto error;
}
/*
* It's a right-truncated date or an xs:time.
* Try to parse an xs:time then fallback on right-truncated dates.
*/
if ((*cur >= '0') && (*cur <= '9')) {
ret = _exsltDateParseTime(dt, &cur);
if (ret == 0) {
/* it's an xs:time */
RETURN_TYPE_IF_VALID(XS_TIME);
}
}
/* fallback on date parsing */
cur = dateTime;
ret = _exsltDateParseGYear(dt, &cur);
if (ret != 0)
goto error;
/* is it an xs:gYear? */
RETURN_TYPE_IF_VALID(XS_GYEAR);
if (*cur != '-')
goto error;
cur++;
ret = _exsltDateParseGMonth(dt, &cur);
if (ret != 0)
goto error;
/* is it an xs:gYearMonth? */
RETURN_TYPE_IF_VALID(XS_GYEARMONTH);
if (*cur != '-')
goto error;
cur++;
ret = _exsltDateParseGDay(dt, &cur);
if ((ret != 0) || !VALID_DATE(dt))
goto error;
/* is it an xs:date? */
RETURN_TYPE_IF_VALID(XS_DATE);
if (*cur != 'T')
goto error;
cur++;
/* it should be an xs:dateTime */
ret = _exsltDateParseTime(dt, &cur);
if (ret != 0)
goto error;
ret = _exsltDateParseTimeZone(dt, &cur);
if ((ret != 0) || (*cur != 0) || !VALID_DATETIME(dt))
goto error;
dt->type = XS_DATETIME;
return dt;
error:
if (dt != NULL)
exsltDateFreeDate(dt);
return NULL;
}
/**
* exsltDateParseDuration:
* @duration: string to analyze
*
* Parses a duration string
*
* Returns a newly built #exsltDateDurValPtr of NULL in case of error
*/
static exsltDateDurValPtr
exsltDateParseDuration (const xmlChar *duration)
{
const xmlChar *cur = duration;
exsltDateDurValPtr dur;
int isneg = 0;
unsigned int seq = 0;
long days, secs = 0;
double sec_frac = 0.0;
if (duration == NULL)
return NULL;
if (*cur == '-') {
isneg = 1;
cur++;
}
/* duration must start with 'P' (after sign) */
if (*cur++ != 'P')
return NULL;
if (*cur == 0)
return NULL;
dur = exsltDateCreateDuration();
if (dur == NULL)
return NULL;
while (*cur != 0) {
long num = 0;
size_t has_digits = 0;
int has_frac = 0;
const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
/* input string should be empty or invalid date/time item */
if (seq >= sizeof(desig))
goto error;
/* T designator must be present for time items */
if (*cur == 'T') {
if (seq > 3)
goto error;
cur++;
seq = 3;
} else if (seq == 3)
goto error;
/* Parse integral part. */
while (*cur >= '0' && *cur <= '9') {
long digit = *cur - '0';
if (num > LONG_MAX / 10)
goto error;
num *= 10;
if (num > LONG_MAX - digit)
goto error;
num += digit;
has_digits = 1;
cur++;
}
if (*cur == '.') {
/* Parse fractional part. */
double mult = 1.0;
cur++;
has_frac = 1;
while (*cur >= '0' && *cur <= '9') {
mult /= 10.0;
sec_frac += (*cur - '0') * mult;
has_digits = 1;
cur++;
}
}
while (*cur != desig[seq]) {
seq++;
/* No T designator or invalid char. */
if (seq == 3 || seq == sizeof(desig))
goto error;
}
cur++;
if (!has_digits || (has_frac && (seq != 5)))
goto error;
switch (seq) {
case 0:
/* Year */
if (num > LONG_MAX / 12)
goto error;
dur->mon = num * 12;
break;
case 1:
/* Month */
if (dur->mon > LONG_MAX - num)
goto error;
dur->mon += num;
break;
case 2:
/* Day */
dur->day = num;
break;
case 3:
/* Hour */
days = num / HOURS_PER_DAY;
if (dur->day > LONG_MAX - days)
goto error;
dur->day += days;
secs = (num % HOURS_PER_DAY) * SECS_PER_HOUR;
break;
case 4:
/* Minute */
days = num / MINS_PER_DAY;
if (dur->day > LONG_MAX - days)
goto error;
dur->day += days;
secs += (num % MINS_PER_DAY) * SECS_PER_MIN;
break;
case 5:
/* Second */
days = num / SECS_PER_DAY;
if (dur->day > LONG_MAX - days)
goto error;
dur->day += days;
secs += num % SECS_PER_DAY;
break;
}
seq++;
}
days = secs / SECS_PER_DAY;
if (dur->day > LONG_MAX - days)
goto error;
dur->day += days;
dur->sec = (secs % SECS_PER_DAY) + sec_frac;
if (isneg) {
dur->mon = -dur->mon;
dur->day = -dur->day;
if (dur->sec != 0.0) {
dur->sec = SECS_PER_DAY - dur->sec;
dur->day -= 1;
}
}
#ifdef DEBUG_EXSLT_DATE
xsltGenericDebug(xsltGenericDebugContext,
"Parsed duration %f\n", dur->sec);
#endif
return dur;
error:
if (dur != NULL)
exsltDateFreeDuration(dur);
return NULL;
}
static void
exsltFormatLong(xmlChar **cur, xmlChar *end, long num) {
xmlChar buf[20];
int i = 0;
while (i < 20) {
buf[i++] = '0' + num % 10;
num /= 10;
if (num == 0)
break;
}
while (i > 0) {
if (*cur < end)
*(*cur)++ = buf[--i];
}
}
static void
exsltFormatNanoseconds(xmlChar **cur, xmlChar *end, long nsecs) {
long p10, digit;
if (nsecs > 0) {
if (*cur < end)
*(*cur)++ = '.';
p10 = 100000000;
while (nsecs > 0) {
digit = nsecs / p10;
if (*cur < end)
*(*cur)++ = '0' + digit;
nsecs -= digit * p10;
p10 /= 10;
}
}
}
/**
* exsltDateFormatDuration:
* @dur: an #exsltDateDurValPtr
*
* Formats the duration.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
exsltDateFormatDuration (const exsltDateDurValPtr dur)
{
xmlChar buf[100], *cur = buf, *end = buf + 99;
double secs, tmp;
long days, months, intSecs, nsecs;
if (dur == NULL)
return NULL;
/* quick and dirty check */
if ((dur->sec == 0.0) && (dur->day == 0) && (dur->mon == 0))
return xmlStrdup((xmlChar*)"P0D");
secs = dur->sec;
days = dur->day;
months = dur->mon;
*cur = '\0';
if (days < 0) {
if (secs != 0.0) {
secs = SECS_PER_DAY - secs;
days += 1;
}
days = -days;
*cur = '-';
}
if (months < 0) {
months = -months;
*cur = '-';
}
if (*cur == '-')
cur++;
*cur++ = 'P';
if (months >= 12) {
long years = months / 12;
months -= years * 12;
exsltFormatLong(&cur, end, years);
if (cur < end)
*cur++ = 'Y';
}
if (months != 0) {
exsltFormatLong(&cur, end, months);
if (cur < end)
*cur++ = 'M';
}
if (days != 0) {
exsltFormatLong(&cur, end, days);
if (cur < end)
*cur++ = 'D';
}
tmp = floor(secs);
intSecs = (long) tmp;
/* Round to nearest to avoid issues with floating point precision */
nsecs = (long) floor((secs - tmp) * 1000000000 + 0.5);
if (nsecs >= 1000000000) {
nsecs -= 1000000000;
intSecs += 1;
}
if ((intSecs > 0) || (nsecs > 0)) {
if (cur < end)
*cur++ = 'T';
if (intSecs >= SECS_PER_HOUR) {
long hours = intSecs / SECS_PER_HOUR;
intSecs -= hours * SECS_PER_HOUR;
exsltFormatLong(&cur, end, hours);
if (cur < end)
*cur++ = 'H';
}
if (intSecs >= SECS_PER_MIN) {
long mins = intSecs / SECS_PER_MIN;
intSecs -= mins * SECS_PER_MIN;
exsltFormatLong(&cur, end, mins);
if (cur < end)
*cur++ = 'M';
}
if ((intSecs > 0) || (nsecs > 0)) {
exsltFormatLong(&cur, end, intSecs);
exsltFormatNanoseconds(&cur, end, nsecs);
if (cur < end)
*cur++ = 'S';
}
}
*cur = 0;
return xmlStrdup(buf);
}
static void
exsltFormatTwoDigits(xmlChar **cur, xmlChar *end, int num) {
if (num < 0 || num >= 100)
return;
if (*cur < end)
*(*cur)++ = '0' + num / 10;
if (*cur < end)
*(*cur)++ = '0' + num % 10;
}
static void
exsltFormatTime(xmlChar **cur, xmlChar *end, exsltDateValPtr dt) {
double tmp;
long intSecs, nsecs;
exsltFormatTwoDigits(cur, end, dt->hour);
if (*cur < end)
*(*cur)++ = ':';
exsltFormatTwoDigits(cur, end, dt->min);
if (*cur < end)
*(*cur)++ = ':';
tmp = floor(dt->sec);
intSecs = (long) tmp;
/*
* Round to nearest to avoid issues with floating point precision,
* but don't carry over so seconds stay below 60.
*/
nsecs = (long) floor((dt->sec - tmp) * 1000000000 + 0.5);
if (nsecs > 999999999)
nsecs = 999999999;
exsltFormatTwoDigits(cur, end, intSecs);
exsltFormatNanoseconds(cur, end, nsecs);
}
/**
* exsltDateFormatDateTime:
* @dt: an #exsltDateValPtr
*
* Formats @dt in xs:dateTime format.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
exsltDateFormatDateTime (const exsltDateValPtr dt)
{
xmlChar buf[100], *cur = buf, *end = buf + 99;
if ((dt == NULL) || !VALID_DATETIME(dt))
return NULL;
exsltFormatYearMonthDay(&cur, end, dt);
if (cur < end)
*cur++ = 'T';
exsltFormatTime(&cur, end, dt);
exsltFormatTimeZone(&cur, end, dt->tzo);
*cur = 0;
return xmlStrdup(buf);
}
/**
* exsltDateFormatDate:
* @dt: an #exsltDateValPtr
*
* Formats @dt in xs:date format.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
exsltDateFormatDate (const exsltDateValPtr dt)
{
xmlChar buf[100], *cur = buf, *end = buf + 99;
if ((dt == NULL) || !VALID_DATETIME(dt))
return NULL;
exsltFormatYearMonthDay(&cur, end, dt);
if (dt->tz_flag || (dt->tzo != 0)) {
exsltFormatTimeZone(&cur, end, dt->tzo);
}
*cur = 0;
return xmlStrdup(buf);
}
/**
* exsltDateFormatTime:
* @dt: an #exsltDateValPtr
*
* Formats @dt in xs:time format.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
exsltDateFormatTime (const exsltDateValPtr dt)
{
xmlChar buf[100], *cur = buf, *end = buf + 99;
if ((dt == NULL) || !VALID_TIME(dt))
return NULL;
exsltFormatTime(&cur, end, dt);
if (dt->tz_flag || (dt->tzo != 0)) {
exsltFormatTimeZone(&cur, end, dt->tzo);
}
*cur = 0;
return xmlStrdup(buf);
}
/**
* exsltDateFormat:
* @dt: an #exsltDateValPtr
*
* Formats @dt in the proper format.
* Note: xs:gmonth and xs:gday are not formatted as there are no
* routines that output them.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
exsltDateFormat (const exsltDateValPtr dt)
{
if (dt == NULL)
return NULL;
switch (dt->type) {
case XS_DATETIME:
return exsltDateFormatDateTime(dt);
case XS_DATE:
return exsltDateFormatDate(dt);
case XS_TIME:
return exsltDateFormatTime(dt);
default:
break;
}
if (dt->type & XS_GYEAR) {
xmlChar buf[100], *cur = buf, *end = buf + 99;
exsltFormatGYear(&cur, end, dt->year);
if (dt->type == XS_GYEARMONTH) {
if (cur < end)
*cur++ = '-';
exsltFormat2Digits(&cur, end, dt->mon);
}
if (dt->tz_flag || (dt->tzo != 0)) {
exsltFormatTimeZone(&cur, end, dt->tzo);
}
*cur = 0;
return xmlStrdup(buf);
}
return NULL;
}
/**
* _exsltDateCastYMToDays:
* @dt: an #exsltDateValPtr
*
* Convert mon and year of @dt to total number of days. Take the
* number of years since (or before) 1 AD and add the number of leap
* years. This is a function because negative
* years must be handled a little differently.
*
* Returns number of days.
*/
static long
_exsltDateCastYMToDays (const exsltDateValPtr dt)
{
long ret;
if (dt->year <= 0)
ret = ((dt->year-1) * 365) +
(((dt->year)/4)-((dt->year)/100)+
((dt->year)/400)) +
DAY_IN_YEAR(0, dt->mon, dt->year) - 1;
else
ret = ((dt->year-1) * 365) +
(((dt->year-1)/4)-((dt->year-1)/100)+
((dt->year-1)/400)) +
DAY_IN_YEAR(0, dt->mon, dt->year);
return ret;
}
/**
* TIME_TO_NUMBER:
* @dt: an #exsltDateValPtr
*
* Calculates the number of seconds in the time portion of @dt.
*
* Returns seconds.
*/
#define TIME_TO_NUMBER(dt) \
((double)((dt->hour * SECS_PER_HOUR) + \
(dt->min * SECS_PER_MIN)) + dt->sec)
/**
* _exsltDateTruncateDate:
* @dt: an #exsltDateValPtr
* @type: dateTime type to set to
*
* Set @dt to truncated @type.
*
* Returns 0 success, non-zero otherwise.
*/
static int
_exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
{
if (dt == NULL)
return 1;
if ((type & XS_TIME) != XS_TIME) {
dt->hour = 0;
dt->min = 0;
dt->sec = 0.0;
}
if ((type & XS_GDAY) != XS_GDAY)
dt->day = 1;
if ((type & XS_GMONTH) != XS_GMONTH)
dt->mon = 1;
if ((type & XS_GYEAR) != XS_GYEAR)
dt->year = 0;
dt->type = type;
return 0;
}
/**
* _exsltDayInWeek:
* @yday: year day (1-366)
* @yr: year
*
* Determine the day-in-week from @yday and @yr. 0001-01-01 was
* a Monday so all other days are calculated from there. Take the
* number of years since (or before) add the number of leap years and
* the day-in-year and mod by 7. This is a function because negative
* years must be handled a little differently.
*
* Returns day in week (Sunday = 0).
*/
static long
_exsltDateDayInWeek(long yday, long yr)
{
long ret;
if (yr <= 0) {
/* Compute modulus twice to avoid integer overflow */
ret = ((yr%7-2 + ((yr/4)-(yr/100)+(yr/400)) + yday) % 7);
if (ret < 0)
ret += 7;
} else
ret = (((yr%7-1) + (((yr-1)/4)-((yr-1)/100)+((yr-1)/400)) + yday) % 7);
return ret;
}
/**
* _exsltDateAdd:
* @dt: an #exsltDateValPtr
* @dur: an #exsltDateDurValPtr
*
* Compute a new date/time from @dt and @dur. This function assumes @dt
* is either #XS_DATETIME, #XS_DATE, #XS_GYEARMONTH, or #XS_GYEAR.
*
* Returns date/time pointer or NULL.
*/
static exsltDateValPtr
_exsltDateAdd (exsltDateValPtr dt, exsltDateDurValPtr dur)
{
exsltDateValPtr ret;
long carry, temp;
double sum;
if ((dt == NULL) || (dur == NULL))
return NULL;
ret = exsltDateCreateDate(dt->type);
if (ret == NULL)
return NULL;
/*
* Note that temporary values may need more bits than the values in
* bit field.
*/
/* month */
temp = dt->mon + dur->mon % 12;
carry = dur->mon / 12;
if (temp < 1) {
temp += 12;
carry -= 1;
}
else if (temp > 12) {
temp -= 12;
carry += 1;
}
ret->mon = temp;
/*
* year (may be modified later)
*
* Add epochs from dur->day now to avoid overflow later and to speed up
* pathological cases.
*/
carry += (dur->day / DAYS_PER_EPOCH) * YEARS_PER_EPOCH;
if ((carry > 0 && dt->year > YEAR_MAX - carry) ||
(carry < 0 && dt->year < YEAR_MIN - carry)) {
/* Overflow */
exsltDateFreeDate(ret);
return NULL;
}
ret->year = dt->year + carry;
/* time zone */
ret->tzo = dt->tzo;
ret->tz_flag = dt->tz_flag;
/* seconds */
sum = dt->sec + dur->sec;
ret->sec = fmod(sum, 60.0);
carry = (long)(sum / 60.0);
/* minute */
temp = dt->min + carry % 60;
carry = carry / 60;
if (temp >= 60) {
temp -= 60;
carry += 1;
}
ret->min = temp;
/* hours */
temp = dt->hour + carry % 24;
carry = carry / 24;
if (temp >= 24) {
temp -= 24;
carry += 1;
}
ret->hour = temp;
/* days */
if (dt->day > MAX_DAYINMONTH(ret->year, ret->mon))
temp = MAX_DAYINMONTH(ret->year, ret->mon);
else if (dt->day < 1)
temp = 1;
else
temp = dt->day;
temp += dur->day % DAYS_PER_EPOCH + carry;
while (1) {
if (temp < 1) {
if (ret->mon > 1) {
ret->mon -= 1;
}
else {
if (ret->year == YEAR_MIN) {
exsltDateFreeDate(ret);
return NULL;
}
ret->mon = 12;
ret->year -= 1;
}
temp += MAX_DAYINMONTH(ret->year, ret->mon);
} else if (temp > (long)MAX_DAYINMONTH(ret->year, ret->mon)) {
temp -= MAX_DAYINMONTH(ret->year, ret->mon);
if (ret->mon < 12) {
ret->mon += 1;
}
else {
if (ret->year == YEAR_MAX) {
exsltDateFreeDate(ret);
return NULL;
}
ret->mon = 1;
ret->year += 1;
}
} else
break;
}
ret->day = temp;
/*
* adjust the date/time type to the date values
*/
if (ret->type != XS_DATETIME) {
if ((ret->hour) || (ret->min) || (ret->sec))
ret->type = XS_DATETIME;
else if (ret->type != XS_DATE) {
if (ret->day != 1)
ret->type = XS_DATE;
else if ((ret->type != XS_GYEARMONTH) && (ret->mon != 1))
ret->type = XS_GYEARMONTH;
}
}
return ret;
}
/**
* _exsltDateDifference:
* @x: an #exsltDateValPtr
* @y: an #exsltDateValPtr
* @flag: force difference in days
*
* Calculate the difference between @x and @y as a duration
* (i.e. y - x). If the @flag is set then even if the least specific
* format of @x or @y is xs:gYear or xs:gYearMonth.
*
* Returns a duration pointer or NULL.
*/
static exsltDateDurValPtr
_exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
{
exsltDateDurValPtr ret;
if ((x == NULL) || (y == NULL))
return NULL;
if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
((y->type < XS_GYEAR) || (y->type > XS_DATETIME)))
return NULL;
/*
* the operand with the most specific format must be converted to
* the same type as the operand with the least specific format.
*/
if (x->type != y->type) {
if (x->type < y->type) {
_exsltDateTruncateDate(y, x->type);
} else {
_exsltDateTruncateDate(x, y->type);
}
}
ret = exsltDateCreateDuration();
if (ret == NULL)
return NULL;
if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
/* compute the difference in months */
if ((x->year >= LONG_MAX / 24) || (x->year <= LONG_MIN / 24) ||
(y->year >= LONG_MAX / 24) || (y->year <= LONG_MIN / 24)) {
/* Possible overflow. */
exsltDateFreeDuration(ret);
return NULL;
}
ret->mon = (y->year - x->year) * 12 + (y->mon - x->mon);
} else {
long carry;
if ((x->year > LONG_MAX / 731) || (x->year < LONG_MIN / 731) ||
(y->year > LONG_MAX / 731) || (y->year < LONG_MIN / 731)) {
/* Possible overflow. */
exsltDateFreeDuration(ret);
return NULL;
}
ret->sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
ret->sec += (x->tzo - y->tzo) * SECS_PER_MIN;
carry = (long)floor(ret->sec / SECS_PER_DAY);
ret->sec = ret->sec - carry * SECS_PER_DAY;
ret->day = _exsltDateCastYMToDays(y) - _exsltDateCastYMToDays(x);
ret->day += y->day - x->day;
ret->day += carry;
}
return ret;
}
/**
* _exsltDateAddDurCalc
* @ret: an exsltDateDurValPtr for the return value:
* @x: an exsltDateDurValPtr for the first operand
* @y: an exsltDateDurValPtr for the second operand
*
* Add two durations, catering for possible negative values.
* The sum is placed in @ret.
*
* Returns 1 for success, 0 if error detected.
*/
static int
_exsltDateAddDurCalc (exsltDateDurValPtr ret, exsltDateDurValPtr x,
exsltDateDurValPtr y)
{
/* months */
if ((x->mon > 0 && y->mon > LONG_MAX - x->mon) ||
(x->mon < 0 && y->mon <= LONG_MIN - x->mon)) {
/* Overflow */
return 0;
}
ret->mon = x->mon + y->mon;
/* days */
if ((x->day > 0 && y->day > LONG_MAX - x->day) ||
(x->day < 0 && y->day <= LONG_MIN - x->day)) {
/* Overflow */
return 0;
}
ret->day = x->day + y->day;
/* seconds */
ret->sec = x->sec + y->sec;
if (ret->sec >= SECS_PER_DAY) {
if (ret->day == LONG_MAX) {
/* Overflow */
return 0;
}
ret->sec -= SECS_PER_DAY;
ret->day += 1;
}
/*
* are the results indeterminate? i.e. how do you subtract days from
* months or years?
*/
if (ret->day >= 0) {
if (((ret->day > 0) || (ret->sec > 0)) && (ret->mon < 0))
return 0;
}
else {
if (ret->mon > 0)
return 0;
}
return 1;
}
/**
* _exsltDateAddDuration:
* @x: an #exsltDateDurValPtr
* @y: an #exsltDateDurValPtr
*
* Compute a new duration from @x and @y.
*
* Returns a duration pointer or NULL.
*/
static exsltDateDurValPtr
_exsltDateAddDuration (exsltDateDurValPtr x, exsltDateDurValPtr y)
{
exsltDateDurValPtr ret;
if ((x == NULL) || (y == NULL))
return NULL;
ret = exsltDateCreateDuration();
if (ret == NULL)
return NULL;
if (_exsltDateAddDurCalc(ret, x, y))
return ret;
exsltDateFreeDuration(ret);
return NULL;
}
/****************************************************************
* *
* EXSLT - Dates and Times functions *
* *
****************************************************************/
/**
* exsltDateDateTime:
*
* Implements the EXSLT - Dates and Times date-time() function:
* string date:date-time()
*
* Returns the current date and time as a date/time string.
*/
static xmlChar *
exsltDateDateTime (void)
{
xmlChar *ret = NULL;
exsltDateValPtr cur;
cur = exsltDateCurrent();
if (cur != NULL) {
ret = exsltDateFormatDateTime(cur);
exsltDateFreeDate(cur);
}
return ret;
}
/**
* exsltDateDate:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times date() function:
* string date:date (string?)
*
* Returns the date specified in the date/time string given as the
* argument. If no argument is given, then the current local
* date/time, as returned by date:date-time is used as a default
* argument.
* The date/time string specified as an argument must be a string in
* the format defined as the lexical representation of either
* xs:dateTime or xs:date. If the argument is not in either of these
* formats, returns NULL.
*/
static xmlChar *
exsltDateDate (const xmlChar *dateTime)
{
exsltDateValPtr dt = NULL;
xmlChar *ret = NULL;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return NULL;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return NULL;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
exsltDateFreeDate(dt);
return NULL;
}
}
ret = exsltDateFormatDate(dt);
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateTime:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times time() function:
* string date:time (string?)
*
* Returns the time specified in the date/time string given as the
* argument. If no argument is given, then the current local
* date/time, as returned by date:date-time is used as a default
* argument.
* The date/time string specified as an argument must be a string in
* the format defined as the lexical representation of either
* xs:dateTime or xs:time. If the argument is not in either of these
* formats, returns NULL.
*/
static xmlChar *
exsltDateTime (const xmlChar *dateTime)
{
exsltDateValPtr dt = NULL;
xmlChar *ret = NULL;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return NULL;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return NULL;
if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
exsltDateFreeDate(dt);
return NULL;
}
}
ret = exsltDateFormatTime(dt);
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateYear:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times year() function
* number date:year (string?)
* Returns the year of a date as a number. If no argument is given,
* then the current local date/time, as returned by date:date-time is
* used as a default argument.
* The date/time string specified as the first argument must be a
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gYear (CCYY)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateYear (const xmlChar *dateTime)
{
exsltDateValPtr dt;
long year;
double ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
(dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
year = dt->year;
if (year <= 0) year -= 1; /* Adjust for missing year 0. */
ret = (double) year;
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateLeapYear:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times leap-year() function:
* boolean date:leap-yea (string?)
* Returns true if the year given in a date is a leap year. If no
* argument is given, then the current local date/time, as returned by
* date:date-time is used as a default argument.
* The date/time string specified as the first argument must be a
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gYear (CCYY)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static xmlXPathObjectPtr
exsltDateLeapYear (const xmlChar *dateTime)
{
exsltDateValPtr dt = NULL;
xmlXPathObjectPtr ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
} else {
dt = exsltDateParse(dateTime);
if ((dt != NULL) &&
(dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
(dt->type != XS_GYEARMONTH) && (dt->type != XS_GYEAR)) {
exsltDateFreeDate(dt);
dt = NULL;
}
}
if (dt == NULL) {
ret = xmlXPathNewFloat(xmlXPathNAN);
}
else {
ret = xmlXPathNewBoolean(IS_LEAP(dt->year));
exsltDateFreeDate(dt);
}
return ret;
}
/**
* exsltDateMonthInYear:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times month-in-year() function:
* number date:month-in-year (string?)
* Returns the month of a date as a number. If no argument is given,
* then the current local date/time, as returned by date:date-time is
* used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gMonth (--MM--)
* - xs:gMonthDay (--MM-DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateMonthInYear (const xmlChar *dateTime)
{
exsltDateValPtr dt;
double ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
(dt->type != XS_GYEARMONTH) && (dt->type != XS_GMONTH) &&
(dt->type != XS_GMONTHDAY)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = (double) dt->mon;
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateMonthName:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Time month-name() function
* string date:month-name (string?)
* Returns the full name of the month of a date. If no argument is
* given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gMonth (--MM--)
* If the date/time string is not in one of these formats, then an
* empty string ('') is returned.
* The result is an English month name: one of 'January', 'February',
* 'March', 'April', 'May', 'June', 'July', 'August', 'September',
* 'October', 'November' or 'December'.
*/
static const xmlChar *
exsltDateMonthName (const xmlChar *dateTime)
{
static const xmlChar monthNames[13][10] = {
{ 0 },
{ 'J', 'a', 'n', 'u', 'a', 'r', 'y', 0 },
{ 'F', 'e', 'b', 'r', 'u', 'a', 'r', 'y', 0 },
{ 'M', 'a', 'r', 'c', 'h', 0 },
{ 'A', 'p', 'r', 'i', 'l', 0 },
{ 'M', 'a', 'y', 0 },
{ 'J', 'u', 'n', 'e', 0 },
{ 'J', 'u', 'l', 'y', 0 },
{ 'A', 'u', 'g', 'u', 's', 't', 0 },
{ 'S', 'e', 'p', 't', 'e', 'm', 'b', 'e', 'r', 0 },
{ 'O', 'c', 't', 'o', 'b', 'e', 'r', 0 },
{ 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
{ 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
};
double month;
int index = 0;
month = exsltDateMonthInYear(dateTime);
if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0))
index = (int) month;
return monthNames[index];
}
/**
* exsltDateMonthAbbreviation:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Time month-abbreviation() function
* string date:month-abbreviation (string?)
* Returns the abbreviation of the month of a date. If no argument is
* given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gMonth (--MM--)
* If the date/time string is not in one of these formats, then an
* empty string ('') is returned.
* The result is an English month abbreviation: one of 'Jan', 'Feb',
* 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or
* 'Dec'.
*/
static const xmlChar *
exsltDateMonthAbbreviation (const xmlChar *dateTime)
{
static const xmlChar monthAbbreviations[13][4] = {
{ 0 },
{ 'J', 'a', 'n', 0 },
{ 'F', 'e', 'b', 0 },
{ 'M', 'a', 'r', 0 },
{ 'A', 'p', 'r', 0 },
{ 'M', 'a', 'y', 0 },
{ 'J', 'u', 'n', 0 },
{ 'J', 'u', 'l', 0 },
{ 'A', 'u', 'g', 0 },
{ 'S', 'e', 'p', 0 },
{ 'O', 'c', 't', 0 },
{ 'N', 'o', 'v', 0 },
{ 'D', 'e', 'c', 0 }
};
double month;
int index = 0;
month = exsltDateMonthInYear(dateTime);
if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0))
index = (int) month;
return monthAbbreviations[index];
}
/**
* exsltDateWeekInYear:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times week-in-year() function
* number date:week-in-year (string?)
* Returns the week of the year as a number. If no argument is given,
* then the current local date/time, as returned by date:date-time is
* used as the default argument. For the purposes of numbering,
* counting follows ISO 8601: week 1 in a year is the week containing
* the first Thursday of the year, with new weeks beginning on a
* Monday.
* The date/time string specified as the argument is a right-truncated
* string in the format defined as the lexical representation of
* xs:dateTime in one of the formats defined in [XML Schema Part 2:
* Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateWeekInYear (const xmlChar *dateTime)
{
exsltDateValPtr dt;
long diy, diw, year, ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
/*
* Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
* is the first day-in-week
*/
diw = (_exsltDateDayInWeek(diy, dt->year) + 6) % 7;
/* ISO 8601 adjustment, 3 is Thu */
diy += (3 - diw);
if(diy < 1) {
year = dt->year - 1;
if(year == 0) year--;
diy = DAY_IN_YEAR(31, 12, year) + diy;
} else if (diy > (long)DAY_IN_YEAR(31, 12, dt->year)) {
diy -= DAY_IN_YEAR(31, 12, dt->year);
}
ret = ((diy - 1) / 7) + 1;
exsltDateFreeDate(dt);
return (double) ret;
}
/**
* exsltDateWeekInMonth:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times week-in-month() function
* number date:week-in-month (string?)
* The date:week-in-month function returns the week in a month of a
* date as a number. If no argument is given, then the current local
* date/time, as returned by date:date-time is used the default
* argument. For the purposes of numbering, the first day of the month
* is in week 1 and new weeks begin on a Monday (so the first and last
* weeks in a month will often have less than 7 days in them).
* The date/time string specified as the argument is a right-truncated
* string in the format defined as the lexical representation of
* xs:dateTime in one of the formats defined in [XML Schema Part 2:
* Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateWeekInMonth (const xmlChar *dateTime)
{
exsltDateValPtr dt;
long fdiy, fdiw, ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
fdiy = DAY_IN_YEAR(1, dt->mon, dt->year);
/*
* Determine day-in-week (0=Sun, 1=Mon, etc.) then adjust so Monday
* is the first day-in-week
*/
fdiw = (_exsltDateDayInWeek(fdiy, dt->year) + 6) % 7;
ret = ((dt->day + fdiw - 1) / 7) + 1;
exsltDateFreeDate(dt);
return (double) ret;
}
/**
* exsltDateDayInYear:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times day-in-year() function
* number date:day-in-year (string?)
* Returns the day of a date in a year as a number. If no argument is
* given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a right-truncated
* string in the format defined as the lexical representation of
* xs:dateTime in one of the formats defined in [XML Schema Part 2:
* Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateDayInYear (const xmlChar *dateTime)
{
exsltDateValPtr dt;
long ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
exsltDateFreeDate(dt);
return (double) ret;
}
/**
* exsltDateDayInMonth:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times day-in-month() function:
* number date:day-in-month (string?)
* Returns the day of a date as a number. If no argument is given,
* then the current local date/time, as returned by date:date-time is
* used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gMonthDay (--MM-DD)
* - xs:gDay (---DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateDayInMonth (const xmlChar *dateTime)
{
exsltDateValPtr dt;
double ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE) &&
(dt->type != XS_GMONTHDAY) && (dt->type != XS_GDAY)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = (double) dt->day;
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateDayOfWeekInMonth:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times day-of-week-in-month() function:
* number date:day-of-week-in-month (string?)
* Returns the day-of-the-week in a month of a date as a number
* (e.g. 3 for the 3rd Tuesday in May). If no argument is
* given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a right-truncated
* string in the format defined as the lexical representation of
* xs:dateTime in one of the formats defined in [XML Schema Part 2:
* Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
{
exsltDateValPtr dt;
long ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = ((dt->day -1) / 7) + 1;
exsltDateFreeDate(dt);
return (double) ret;
}
/**
* exsltDateDayInWeek:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times day-in-week() function:
* number date:day-in-week (string?)
* Returns the day of the week given in a date as a number. If no
* argument is given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then NaN is
* returned.
* The numbering of days of the week starts at 1 for Sunday, 2 for
* Monday and so on up to 7 for Saturday.
*/
static double
exsltDateDayInWeek (const xmlChar *dateTime)
{
exsltDateValPtr dt;
long diy, ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_DATE)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
ret = _exsltDateDayInWeek(diy, dt->year) + 1;
exsltDateFreeDate(dt);
return (double) ret;
}
/**
* exsltDateDayName:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Time day-name() function
* string date:day-name (string?)
* Returns the full name of the day of the week of a date. If no
* argument is given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then an
* empty string ('') is returned.
* The result is an English day name: one of 'Sunday', 'Monday',
* 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'.
*/
static const xmlChar *
exsltDateDayName (const xmlChar *dateTime)
{
static const xmlChar dayNames[8][10] = {
{ 0 },
{ 'S', 'u', 'n', 'd', 'a', 'y', 0 },
{ 'M', 'o', 'n', 'd', 'a', 'y', 0 },
{ 'T', 'u', 'e', 's', 'd', 'a', 'y', 0 },
{ 'W', 'e', 'd', 'n', 'e', 's', 'd', 'a', 'y', 0 },
{ 'T', 'h', 'u', 'r', 's', 'd', 'a', 'y', 0 },
{ 'F', 'r', 'i', 'd', 'a', 'y', 0 },
{ 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
};
double day;
int index = 0;
day = exsltDateDayInWeek(dateTime);
if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0))
index = (int) day;
return dayNames[index];
}
/**
* exsltDateDayAbbreviation:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Time day-abbreviation() function
* string date:day-abbreviation (string?)
* Returns the abbreviation of the day of the week of a date. If no
* argument is given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* If the date/time string is not in one of these formats, then an
* empty string ('') is returned.
* The result is a three-letter English day abbreviation: one of
* 'Sun', 'Mon', 'Tue', 'Wed', 'Thu' or 'Fri'.
*/
static const xmlChar *
exsltDateDayAbbreviation (const xmlChar *dateTime)
{
static const xmlChar dayAbbreviations[8][4] = {
{ 0 },
{ 'S', 'u', 'n', 0 },
{ 'M', 'o', 'n', 0 },
{ 'T', 'u', 'e', 0 },
{ 'W', 'e', 'd', 0 },
{ 'T', 'h', 'u', 0 },
{ 'F', 'r', 'i', 0 },
{ 'S', 'a', 't', 0 }
};
double day;
int index = 0;
day = exsltDateDayInWeek(dateTime);
if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0))
index = (int) day;
return dayAbbreviations[index];
}
/**
* exsltDateHourInDay:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times day-in-month() function:
* number date:day-in-month (string?)
* Returns the hour of the day as a number. If no argument is given,
* then the current local date/time, as returned by date:date-time is
* used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:time (hh:mm:ss)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateHourInDay (const xmlChar *dateTime)
{
exsltDateValPtr dt;
double ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = (double) dt->hour;
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateMinuteInHour:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times day-in-month() function:
* number date:day-in-month (string?)
* Returns the minute of the hour as a number. If no argument is
* given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:time (hh:mm:ss)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*/
static double
exsltDateMinuteInHour (const xmlChar *dateTime)
{
exsltDateValPtr dt;
double ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = (double) dt->min;
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateSecondInMinute:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times second-in-minute() function:
* number date:day-in-month (string?)
* Returns the second of the minute as a number. If no argument is
* given, then the current local date/time, as returned by
* date:date-time is used the default argument.
* The date/time string specified as the argument is a left or
* right-truncated string in the format defined as the lexical
* representation of xs:dateTime in one of the formats defined in [XML
* Schema Part 2: Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:time (hh:mm:ss)
* If the date/time string is not in one of these formats, then NaN is
* returned.
*
* Returns the second or NaN.
*/
static double
exsltDateSecondInMinute (const xmlChar *dateTime)
{
exsltDateValPtr dt;
double ret;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
return xmlXPathNAN;
if ((dt->type != XS_DATETIME) && (dt->type != XS_TIME)) {
exsltDateFreeDate(dt);
return xmlXPathNAN;
}
}
ret = dt->sec;
exsltDateFreeDate(dt);
return ret;
}
/**
* exsltDateAdd:
* @xstr: date/time string
* @ystr: date/time string
*
* Implements the date:add (string,string) function which returns the
* date/time * resulting from adding a duration to a date/time.
* The first argument (@xstr) must be right-truncated date/time
* strings in one of the formats defined in [XML Schema Part 2:
* Datatypes]. The permitted formats are as follows:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gYear (CCYY)
* The second argument (@ystr) is a string in the format defined for
* xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
* The return value is a right-truncated date/time strings in one of
* the formats defined in [XML Schema Part 2: Datatypes] and listed
* above. This value is calculated using the algorithm described in
* [Appendix E Adding durations to dateTimes] of [XML Schema Part 2:
* Datatypes].
* Returns date/time string or NULL.
*/
static xmlChar *
exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
{
exsltDateValPtr dt, res;
exsltDateDurValPtr dur;
xmlChar *ret;
if ((xstr == NULL) || (ystr == NULL))
return NULL;
dt = exsltDateParse(xstr);
if (dt == NULL)
return NULL;
else if ((dt->type < XS_GYEAR) || (dt->type > XS_DATETIME)) {
exsltDateFreeDate(dt);
return NULL;
}
dur = exsltDateParseDuration(ystr);
if (dur == NULL) {
exsltDateFreeDate(dt);
return NULL;
}
res = _exsltDateAdd(dt, dur);
exsltDateFreeDate(dt);
exsltDateFreeDuration(dur);
if (res == NULL)
return NULL;
ret = exsltDateFormat(res);
exsltDateFreeDate(res);
return ret;
}
/**
* exsltDateAddDuration:
* @xstr: first duration string
* @ystr: second duration string
*
* Implements the date:add-duration (string,string) function which returns
* the duration resulting from adding two durations together.
* Both arguments are strings in the format defined for xs:duration
* in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. If either
* argument is not in this format, the function returns an empty string
* ('').
* The return value is a string in the format defined for xs:duration
* in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
* The durations can usually be added by summing the numbers given for
* each of the components in the durations. However, if the durations
* are differently signed, then this sometimes results in durations
* that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
* In these cases, the function returns an empty string ('').
*
* Returns duration string or NULL.
*/
static xmlChar *
exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
{
exsltDateDurValPtr x, y, res;
xmlChar *ret;
if ((xstr == NULL) || (ystr == NULL))
return NULL;
x = exsltDateParseDuration(xstr);
if (x == NULL)
return NULL;
y = exsltDateParseDuration(ystr);
if (y == NULL) {
exsltDateFreeDuration(x);
return NULL;
}
res = _exsltDateAddDuration(x, y);
exsltDateFreeDuration(x);
exsltDateFreeDuration(y);
if (res == NULL)
return NULL;
ret = exsltDateFormatDuration(res);
exsltDateFreeDuration(res);
return ret;
}
/**
* exsltDateSumFunction:
* @ns: a node set of duration strings
*
* The date:sum function adds a set of durations together.
* The string values of the nodes in the node set passed as an argument
* are interpreted as durations and added together as if using the
* date:add-duration function. (from exslt.org)
*
* The return value is a string in the format defined for xs:duration
* in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
* The durations can usually be added by summing the numbers given for
* each of the components in the durations. However, if the durations
* are differently signed, then this sometimes results in durations
* that are impossible to express in this syntax (e.g. 'P1M' + '-P1D').
* In these cases, the function returns an empty string ('').
*
* Returns duration string or NULL.
*/
static void
exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlNodeSetPtr ns;
void *user = NULL;
xmlChar *tmp;
exsltDateDurValPtr x, total;
xmlChar *ret;
int i;
if (nargs != 1) {
xmlXPathSetArityError (ctxt);
return;
}
/* We need to delay the freeing of value->user */
if ((ctxt->value != NULL) && ctxt->value->boolval != 0) {
user = ctxt->value->user;
ctxt->value->boolval = 0;
ctxt->value->user = NULL;
}
ns = xmlXPathPopNodeSet (ctxt);
if (xmlXPathCheckError (ctxt))
return;
if ((ns == NULL) || (ns->nodeNr == 0)) {
xmlXPathReturnEmptyString (ctxt);
if (ns != NULL)
xmlXPathFreeNodeSet (ns);
return;
}
total = exsltDateCreateDuration ();
if (total == NULL) {
xmlXPathFreeNodeSet (ns);
return;
}
for (i = 0; i < ns->nodeNr; i++) {
int result;
tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
if (tmp == NULL) {
xmlXPathFreeNodeSet (ns);
exsltDateFreeDuration (total);
return;
}
x = exsltDateParseDuration (tmp);
if (x == NULL) {
xmlFree (tmp);
exsltDateFreeDuration (total);
xmlXPathFreeNodeSet (ns);
xmlXPathReturnEmptyString (ctxt);
return;
}
result = _exsltDateAddDurCalc(total, total, x);
exsltDateFreeDuration (x);
xmlFree (tmp);
if (!result) {
exsltDateFreeDuration (total);
xmlXPathFreeNodeSet (ns);
xmlXPathReturnEmptyString (ctxt);
return;
}
}
ret = exsltDateFormatDuration (total);
exsltDateFreeDuration (total);
xmlXPathFreeNodeSet (ns);
if (user != NULL)
xmlFreeNodeList ((xmlNodePtr) user);
if (ret == NULL)
xmlXPathReturnEmptyString (ctxt);
else
xmlXPathReturnString (ctxt, ret);
}
/**
* exsltDateSeconds:
* @dateTime: a date/time string
*
* Implements the EXSLT - Dates and Times seconds() function:
* number date:seconds(string?)
* The date:seconds function returns the number of seconds specified
* by the argument string. If no argument is given, then the current
* local date/time, as returned by exsltDateCurrent() is used as the
* default argument. If the date/time string is a xs:duration, then the
* years and months must be zero (or not present). Parsing a duration
* converts the fields to seconds. If the date/time string is not a
* duration (and not null), then the legal formats are:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gYear (CCYY)
* In these cases the difference between the @dateTime and
* 1970-01-01T00:00:00Z is calculated and converted to seconds.
*
* Note that there was some confusion over whether "difference" meant
* that a dateTime of 1970-01-01T00:00:01Z should be a positive one or
* a negative one. After correspondence with exslt.org, it was determined
* that the intent of the specification was to have it positive. The
* coding was modified in July 2003 to reflect this.
*
* Returns seconds or Nan.
*/
static double
exsltDateSeconds (const xmlChar *dateTime)
{
exsltDateValPtr dt;
exsltDateDurValPtr dur = NULL;
double ret = xmlXPathNAN;
if (dateTime == NULL) {
dt = exsltDateCurrent();
if (dt == NULL)
return xmlXPathNAN;
} else {
dt = exsltDateParse(dateTime);
if (dt == NULL)
dur = exsltDateParseDuration(dateTime);
}
if ((dt != NULL) && (dt->type >= XS_GYEAR)) {
exsltDateValPtr y;
exsltDateDurValPtr diff;
/*
* compute the difference between the given (or current) date
* and epoch date
*/
y = exsltDateCreateDate(XS_DATETIME);
if (y != NULL) {
y->year = 1970;
y->mon = 1;
y->day = 1;
y->tz_flag = 1;
diff = _exsltDateDifference(y, dt, 1);
if (diff != NULL) {
ret = (double)diff->day * SECS_PER_DAY + diff->sec;
exsltDateFreeDuration(diff);
}
exsltDateFreeDate(y);
}
} else if ((dur != NULL) && (dur->mon == 0)) {
ret = (double)dur->day * SECS_PER_DAY + dur->sec;
}
if (dt != NULL)
exsltDateFreeDate(dt);
if (dur != NULL)
exsltDateFreeDuration(dur);
return ret;
}
/**
* exsltDateDifference:
* @xstr: date/time string
* @ystr: date/time string
*
* Implements the date:difference (string,string) function which returns
* the duration between the first date and the second date. If the first
* date occurs before the second date, then the result is a positive
* duration; if it occurs after the second date, the result is a
* negative duration. The two dates must both be right-truncated
* date/time strings in one of the formats defined in [XML Schema Part
* 2: Datatypes]. The date/time with the most specific format (i.e. the
* least truncation) is converted into the same format as the date with
* the least specific format (i.e. the most truncation). The permitted
* formats are as follows, from most specific to least specific:
* - xs:dateTime (CCYY-MM-DDThh:mm:ss)
* - xs:date (CCYY-MM-DD)
* - xs:gYearMonth (CCYY-MM)
* - xs:gYear (CCYY)
* If either of the arguments is not in one of these formats,
* date:difference returns the empty string ('').
* The difference between the date/times is returned as a string in the
* format defined for xs:duration in [3.2.6 duration] of [XML Schema
* Part 2: Datatypes].
* If the date/time string with the least specific format is in either
* xs:gYearMonth or xs:gYear format, then the number of days, hours,
* minutes and seconds in the duration string must be equal to zero.
* (The format of the string will be PnYnM.) The number of months
* specified in the duration must be less than 12.
* Otherwise, the number of years and months in the duration string
* must be equal to zero. (The format of the string will be
* PnDTnHnMnS.) The number of seconds specified in the duration string
* must be less than 60; the number of minutes must be less than 60;
* the number of hours must be less than 24.
*
* Returns duration string or NULL.
*/
static xmlChar *
exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
{
exsltDateValPtr x, y;
exsltDateDurValPtr dur;
xmlChar *ret = NULL;
if ((xstr == NULL) || (ystr == NULL))
return NULL;
x = exsltDateParse(xstr);
if (x == NULL)
return NULL;
y = exsltDateParse(ystr);
if (y == NULL) {
exsltDateFreeDate(x);
return NULL;
}
if (((x->type < XS_GYEAR) || (x->type > XS_DATETIME)) ||
((y->type < XS_GYEAR) || (y->type > XS_DATETIME))) {
exsltDateFreeDate(x);
exsltDateFreeDate(y);
return NULL;
}
dur = _exsltDateDifference(x, y, 0);
exsltDateFreeDate(x);
exsltDateFreeDate(y);
if (dur == NULL)
return NULL;
ret = exsltDateFormatDuration(dur);
exsltDateFreeDuration(dur);
return ret;
}
/**
* exsltDateDuration:
* @number: a xmlChar string
*
* Implements the The date:duration function returns a duration string
* representing the number of seconds specified by the argument string.
* If no argument is given, then the result of calling date:seconds
* without any arguments is used as a default argument.
* The duration is returned as a string in the format defined for
* xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes].
* The number of years and months in the duration string must be equal
* to zero. (The format of the string will be PnDTnHnMnS.) The number
* of seconds specified in the duration string must be less than 60;
* the number of minutes must be less than 60; the number of hours must
* be less than 24.
* If the argument is Infinity, -Infinity or NaN, then date:duration
* returns an empty string ('').
*
* Returns duration string or NULL.
*/
static xmlChar *
exsltDateDuration (const xmlChar *number)
{
exsltDateDurValPtr dur;
double secs, days;
xmlChar *ret;
if (number == NULL)
secs = exsltDateSeconds(number);
else
secs = xmlXPathCastStringToNumber(number);
if (xmlXPathIsNaN(secs))
return NULL;
days = floor(secs / SECS_PER_DAY);
if ((days <= (double)LONG_MIN) || (days >= (double)LONG_MAX))
return NULL;
dur = exsltDateCreateDuration();
if (dur == NULL)
return NULL;
dur->day = (long)days;
dur->sec = secs - days * SECS_PER_DAY;
ret = exsltDateFormatDuration(dur);
exsltDateFreeDuration(dur);
return ret;
}
/****************************************************************
* *
* Wrappers for use by the XPath engine *
* *
****************************************************************/
/**
* exsltDateDateTimeFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDateTime() for use by the XPath engine.
*/
static void
exsltDateDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret;
if (nargs != 0) {
xmlXPathSetArityError(ctxt);
return;
}
ret = exsltDateDateTime();
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, ret);
}
/**
* exsltDateDateFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDate() for use by the XPath engine.
*/
static void
exsltDateDateFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret, *dt = NULL;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateDate(dt);
if (ret == NULL) {
xsltGenericDebug(xsltGenericDebugContext,
"{http://exslt.org/dates-and-times}date: "
"invalid date or format %s\n", dt);
xmlXPathReturnEmptyString(ctxt);
} else {
xmlXPathReturnString(ctxt, ret);
}
if (dt != NULL)
xmlFree(dt);
}
/**
* exsltDateTimeFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateTime() for use by the XPath engine.
*/
static void
exsltDateTimeFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret, *dt = NULL;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateTime(dt);
if (ret == NULL) {
xsltGenericDebug(xsltGenericDebugContext,
"{http://exslt.org/dates-and-times}time: "
"invalid date or format %s\n", dt);
xmlXPathReturnEmptyString(ctxt);
} else {
xmlXPathReturnString(ctxt, ret);
}
if (dt != NULL)
xmlFree(dt);
}
/**
* exsltDateYearFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateYear() for use by the XPath engine.
*/
static void
exsltDateYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *dt = NULL;
double ret;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateYear(dt);
if (dt != NULL)
xmlFree(dt);
xmlXPathReturnNumber(ctxt, ret);
}
/**
* exsltDateLeapYearFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateLeapYear() for use by the XPath engine.
*/
static void
exsltDateLeapYearFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *dt = NULL;
xmlXPathObjectPtr ret;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateLeapYear(dt);
if (dt != NULL)
xmlFree(dt);
valuePush(ctxt, ret);
}
#define X_IN_Y(x, y) \
static void \
exsltDate##x##In##y##Function (xmlXPathParserContextPtr ctxt, \
int nargs) { \
xmlChar *dt = NULL; \
double ret; \
\
if ((nargs < 0) || (nargs > 1)) { \
xmlXPathSetArityError(ctxt); \
return; \
} \
\
if (nargs == 1) { \
dt = xmlXPathPopString(ctxt); \
if (xmlXPathCheckError(ctxt)) { \
xmlXPathSetTypeError(ctxt); \
return; \
} \
} \
\
ret = exsltDate##x##In##y(dt); \
\
if (dt != NULL) \
xmlFree(dt); \
\
xmlXPathReturnNumber(ctxt, ret); \
}
/**
* exsltDateMonthInYearFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateMonthInYear() for use by the XPath engine.
*/
X_IN_Y(Month,Year)
/**
* exsltDateMonthNameFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateMonthName() for use by the XPath engine.
*/
static void
exsltDateMonthNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *dt = NULL;
const xmlChar *ret;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateMonthName(dt);
if (dt != NULL)
xmlFree(dt);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, xmlStrdup(ret));
}
/**
* exsltDateMonthAbbreviationFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateMonthAbbreviation() for use by the XPath engine.
*/
static void
exsltDateMonthAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *dt = NULL;
const xmlChar *ret;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateMonthAbbreviation(dt);
if (dt != NULL)
xmlFree(dt);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, xmlStrdup(ret));
}
/**
* exsltDateWeekInYearFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateWeekInYear() for use by the XPath engine.
*/
X_IN_Y(Week,Year)
/**
* exsltDateWeekInMonthFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateWeekInMonthYear() for use by the XPath engine.
*/
X_IN_Y(Week,Month)
/**
* exsltDateDayInYearFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDayInYear() for use by the XPath engine.
*/
X_IN_Y(Day,Year)
/**
* exsltDateDayInMonthFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDayInMonth() for use by the XPath engine.
*/
X_IN_Y(Day,Month)
/**
* exsltDateDayOfWeekInMonthFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDayOfWeekInMonth() for use by the XPath engine.
*/
X_IN_Y(DayOfWeek,Month)
/**
* exsltDateDayInWeekFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDayInWeek() for use by the XPath engine.
*/
X_IN_Y(Day,Week)
/**
* exsltDateDayNameFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDayName() for use by the XPath engine.
*/
static void
exsltDateDayNameFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *dt = NULL;
const xmlChar *ret;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateDayName(dt);
if (dt != NULL)
xmlFree(dt);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, xmlStrdup(ret));
}
/**
* exsltDateMonthDayFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDayAbbreviation() for use by the XPath engine.
*/
static void
exsltDateDayAbbreviationFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *dt = NULL;
const xmlChar *ret;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
dt = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateDayAbbreviation(dt);
if (dt != NULL)
xmlFree(dt);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, xmlStrdup(ret));
}
/**
* exsltDateHourInDayFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateHourInDay() for use by the XPath engine.
*/
X_IN_Y(Hour,Day)
/**
* exsltDateMinuteInHourFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateMinuteInHour() for use by the XPath engine.
*/
X_IN_Y(Minute,Hour)
/**
* exsltDateSecondInMinuteFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateSecondInMinute() for use by the XPath engine.
*/
X_IN_Y(Second,Minute)
/**
* exsltDateSecondsFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateSeconds() for use by the XPath engine.
*/
static void
exsltDateSecondsFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *str = NULL;
double ret;
if (nargs > 1) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
str = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateSeconds(str);
if (str != NULL)
xmlFree(str);
xmlXPathReturnNumber(ctxt, ret);
}
/**
* exsltDateAddFunction:
* @ctxt: an XPath parser context
* @nargs: the number of arguments
*
* Wraps exsltDateAdd() for use by the XPath processor.
*/
static void
exsltDateAddFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret, *xstr, *ystr;
if (nargs != 2) {
xmlXPathSetArityError(ctxt);
return;
}
ystr = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt))
return;
xstr = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlFree(ystr);
return;
}
ret = exsltDateAdd(xstr, ystr);
xmlFree(ystr);
xmlFree(xstr);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, ret);
}
/**
* exsltDateAddDurationFunction:
* @ctxt: an XPath parser context
* @nargs: the number of arguments
*
* Wraps exsltDateAddDuration() for use by the XPath processor.
*/
static void
exsltDateAddDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret, *xstr, *ystr;
if (nargs != 2) {
xmlXPathSetArityError(ctxt);
return;
}
ystr = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt))
return;
xstr = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlFree(ystr);
return;
}
ret = exsltDateAddDuration(xstr, ystr);
xmlFree(ystr);
xmlFree(xstr);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, ret);
}
/**
* exsltDateDifferenceFunction:
* @ctxt: an XPath parser context
* @nargs: the number of arguments
*
* Wraps exsltDateDifference() for use by the XPath processor.
*/
static void
exsltDateDifferenceFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret, *xstr, *ystr;
if (nargs != 2) {
xmlXPathSetArityError(ctxt);
return;
}
ystr = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt))
return;
xstr = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlFree(ystr);
return;
}
ret = exsltDateDifference(xstr, ystr);
xmlFree(ystr);
xmlFree(xstr);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, ret);
}
/**
* exsltDateDurationFunction:
* @ctxt: an XPath parser context
* @nargs : the number of arguments
*
* Wraps exsltDateDuration() for use by the XPath engine
*/
static void
exsltDateDurationFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
xmlChar *ret;
xmlChar *number = NULL;
if ((nargs < 0) || (nargs > 1)) {
xmlXPathSetArityError(ctxt);
return;
}
if (nargs == 1) {
number = xmlXPathPopString(ctxt);
if (xmlXPathCheckError(ctxt)) {
xmlXPathSetTypeError(ctxt);
return;
}
}
ret = exsltDateDuration(number);
if (number != NULL)
xmlFree(number);
if (ret == NULL)
xmlXPathReturnEmptyString(ctxt);
else
xmlXPathReturnString(ctxt, ret);
}
/**
* exsltDateRegister:
*
* Registers the EXSLT - Dates and Times module
*/
void
exsltDateRegister (void)
{
xsltRegisterExtModuleFunction ((const xmlChar *) "add",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateAddFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "add-duration",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateAddDurationFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "date",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDateFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "date-time",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDateTimeFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "day-abbreviation",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayAbbreviationFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-month",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayInMonthFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-week",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayInWeekFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "day-in-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayInYearFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "day-name",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayNameFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "day-of-week-in-month",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayOfWeekInMonthFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "difference",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDifferenceFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "duration",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDurationFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "hour-in-day",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateHourInDayFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "leap-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateLeapYearFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "minute-in-hour",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMinuteInHourFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "month-abbreviation",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMonthAbbreviationFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "month-in-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMonthInYearFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "month-name",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMonthNameFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "second-in-minute",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateSecondInMinuteFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "seconds",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateSecondsFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "sum",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateSumFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "time",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateTimeFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-month",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateWeekInMonthFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "week-in-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateWeekInYearFunction);
xsltRegisterExtModuleFunction ((const xmlChar *) "year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateYearFunction);
}
/**
* exsltDateXpathCtxtRegister:
*
* Registers the EXSLT - Dates and Times module for use outside XSLT
*/
int
exsltDateXpathCtxtRegister (xmlXPathContextPtr ctxt, const xmlChar *prefix)
{
if (ctxt
&& prefix
&& !xmlXPathRegisterNs(ctxt,
prefix,
(const xmlChar *) EXSLT_DATE_NAMESPACE)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "add",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateAddFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "add-duration",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateAddDurationFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "date",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDateFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "date-time",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDateTimeFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "day-abbreviation",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayAbbreviationFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "day-in-month",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayInMonthFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "day-in-week",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayInWeekFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "day-in-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayInYearFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "day-name",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayNameFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "day-of-week-in-month",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDayOfWeekInMonthFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "difference",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDifferenceFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "duration",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateDurationFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "hour-in-day",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateHourInDayFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "leap-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateLeapYearFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "minute-in-hour",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMinuteInHourFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "month-abbreviation",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMonthAbbreviationFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "month-in-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMonthInYearFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "month-name",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateMonthNameFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "second-in-minute",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateSecondInMinuteFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "seconds",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateSecondsFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "sum",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateSumFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "time",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateTimeFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "week-in-month",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateWeekInMonthFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "week-in-year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateWeekInYearFunction)
&& !xmlXPathRegisterFuncNS(ctxt,
(const xmlChar *) "year",
(const xmlChar *) EXSLT_DATE_NAMESPACE,
exsltDateYearFunction)) {
return 0;
}
return -1;
}