summaryrefslogtreecommitdiff
path: root/libexslt/date.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexslt/date.c')
-rw-r--r--libexslt/date.c1047
1 files changed, 546 insertions, 501 deletions
diff --git a/libexslt/date.c b/libexslt/date.c
index 3af6f7fd..4b74677f 100644
--- a/libexslt/date.c
+++ b/libexslt/date.c
@@ -22,7 +22,7 @@
#define IN_LIBEXSLT
#include "libexslt/libexslt.h"
-#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
+#if defined(_WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
#include <win32config.h>
#else
#include "config.h"
@@ -79,14 +79,14 @@ typedef enum {
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),
- XS_DURATION = (XS_GYEAR << 1)
+ XS_DATETIME = (XS_DATE | XS_TIME)
} exsltDateType;
/* Date value */
-typedef struct _exsltDateValDate exsltDateValDate;
-typedef exsltDateValDate *exsltDateValDatePtr;
-struct _exsltDateValDate {
+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 */
@@ -98,22 +98,13 @@ struct _exsltDateValDate {
};
/* Duration value */
-typedef struct _exsltDateValDuration exsltDateValDuration;
-typedef exsltDateValDuration *exsltDateValDurationPtr;
-struct _exsltDateValDuration {
- long mon; /* mon stores years also */
+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 */
-};
-
-typedef struct _exsltDateVal exsltDateVal;
-typedef exsltDateVal *exsltDateValPtr;
-struct _exsltDateVal {
- exsltDateType type;
- union {
- exsltDateValDate date;
- exsltDateValDuration dur;
- } value;
+ double sec; /* sec stores min and hour also
+ 0 <= sec < SECS_PER_DAY */
};
/****************************************************************
@@ -139,7 +130,6 @@ struct _exsltDateVal {
((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
#define VALID_ALWAYS(num) (num >= 0)
-#define VALID_YEAR(yr) (yr != 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))
@@ -148,7 +138,7 @@ struct _exsltDateVal {
#define VALID_SEC(sec) ((sec >= 0) && (sec < 60))
#define VALID_TZO(tzo) ((tzo > -1440) && (tzo < 1440))
#define IS_LEAP(y) \
- (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
+ (((y & 3) == 0) && ((y % 25 != 0) || ((y & 15) == 0)))
static const unsigned long daysInMonth[12] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
@@ -164,7 +154,7 @@ static const unsigned long daysInMonthLeap[12] =
(dt->day <= daysInMonth[dt->mon - 1]))
#define VALID_DATE(dt) \
- (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
+ (VALID_MONTH(dt->mon) && VALID_MDAY(dt))
/*
hour and min structure vals are unsigned, so normal macros give
@@ -177,9 +167,14 @@ static const unsigned long daysInMonthLeap[12] =
#define VALID_DATETIME(dt) \
(VALID_DATE(dt) && VALID_TIME(dt))
-#define SECS_PER_MIN (60)
-#define SECS_PER_HOUR (60 * SECS_PER_MIN)
-#define SECS_PER_DAY (24 * SECS_PER_HOUR)
+#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 unsigned long dayInYearByMonth[12] =
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
@@ -201,10 +196,14 @@ static const unsigned long dayInLeapYearByMonth[12] =
* 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 (exsltDateValDatePtr dt, const xmlChar **str)
+_exsltDateParseGYear (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str, *firstChar;
int isneg = 0, digcnt = 0;
@@ -221,6 +220,8 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
firstChar = cur;
while ((*cur >= '0') && (*cur <= '9')) {
+ if (dt->year >= LONG_MAX / 10)
+ return -1;
dt->year = dt->year * 10 + (*cur - '0');
cur++;
digcnt++;
@@ -231,17 +232,18 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
if ((digcnt < 4) || ((digcnt > 4) && (*firstChar == '0')))
return 1;
- if (isneg)
- dt->year = - dt->year;
-
- if (!VALID_YEAR(dt->year))
+ 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 %04i\n", dt->year);
+ "Parsed year %04ld\n", dt->year);
#endif
return 0;
@@ -256,12 +258,12 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
* @cur is updated to point after the xsl:gYear.
*/
#define FORMAT_GYEAR(yr, cur) \
- if (yr < 0) { \
+ if (yr <= 0) { \
*cur = '-'; \
cur++; \
} \
{ \
- long year = (yr < 0) ? - yr : yr; \
+ long year = (yr <= 0) ? -yr + 1 : yr; \
xmlChar tmp_buf[100], *tmp = tmp_buf; \
/* result is in reverse-order */ \
while (year > 0) { \
@@ -381,7 +383,7 @@ _exsltDateParseGYear (exsltDateValDatePtr dt, const xmlChar **str)
* Returns 0 or the error code
*/
static int
-_exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
+_exsltDateParseGMonth (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str;
int ret = 0;
@@ -423,7 +425,7 @@ _exsltDateParseGMonth (exsltDateValDatePtr dt, const xmlChar **str)
* Returns 0 or the error code
*/
static int
-_exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
+_exsltDateParseGDay (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str;
int ret = 0;
@@ -444,7 +446,7 @@ _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
/**
* FORMAT_GDAY:
- * @dt: the #exsltDateValDate to format
+ * @dt: the #exsltDateVal to format
* @cur: a pointer to an allocated buffer
*
* Formats @dt in xsl:gDay format. Result is appended to @cur and
@@ -455,7 +457,7 @@ _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
/**
* FORMAT_DATE:
- * @dt: the #exsltDateValDate to format
+ * @dt: the #exsltDateVal to format
* @cur: a pointer to an allocated buffer
*
* Formats @dt in xsl:date format. Result is appended to @cur and
@@ -483,7 +485,7 @@ _exsltDateParseGDay (exsltDateValDatePtr dt, const xmlChar **str)
* Returns 0 or the error code
*/
static int
-_exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
+_exsltDateParseTime (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur = *str;
unsigned int hour = 0; /* use temp var in case str is not xs:time */
@@ -528,7 +530,7 @@ _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
/**
* FORMAT_TIME:
- * @dt: the #exsltDateValDate to format
+ * @dt: the #exsltDateVal to format
* @cur: a pointer to an allocated buffer
*
* Formats @dt in xsl:time format. Result is appended to @cur and
@@ -555,7 +557,7 @@ _exsltDateParseTime (exsltDateValDatePtr dt, const xmlChar **str)
* Returns 0 or the error code
*/
static int
-_exsltDateParseTimeZone (exsltDateValDatePtr dt, const xmlChar **str)
+_exsltDateParseTimeZone (exsltDateValPtr dt, const xmlChar **str)
{
const xmlChar *cur;
int ret = 0;
@@ -670,10 +672,8 @@ exsltDateCreateDate (exsltDateType type)
}
memset (ret, 0, sizeof(exsltDateVal));
- if (type != XS_DURATION) {
- ret->value.date.mon = 1;
- ret->value.date.day = 1;
- }
+ ret->mon = 1;
+ ret->day = 1;
if (type != EXSLT_UNKNOWN)
ret->type = type;
@@ -696,54 +696,41 @@ exsltDateFreeDate (exsltDateValPtr date) {
}
/**
- * PARSE_DIGITS:
- * @num: the integer to fill in
- * @cur: an #xmlChar *
- * @num_type: an integer flag
+ * exsltDateCreateDuration:
*
- * Parses a digits integer and updates @num with the value. @cur is
- * updated to point just after the integer.
- * In case of error, @num_type is set to -1, values of @num and
- * @cur are undefined.
+ * Creates a new #exsltDateDurVal, uninitialized.
+ *
+ * Returns the #exsltDateDurValPtr
*/
-#define PARSE_DIGITS(num, cur, num_type) \
- if ((*cur < '0') || (*cur > '9')) \
- num_type = -1; \
- else \
- while ((*cur >= '0') && (*cur <= '9')) { \
- num = num * 10 + (*cur - '0'); \
- cur++; \
- }
+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;
+}
/**
- * PARSE_NUM:
- * @num: the double to fill in
- * @cur: an #xmlChar *
- * @num_type: an integer flag
+ * exsltDateFreeDuration:
+ * @date: an #exsltDateDurValPtr
*
- * Parses a float or integer and updates @num with the value. @cur is
- * updated to point just after the number. If the number is a float,
- * then it must have an integer part and a decimal part; @num_type will
- * be set to 1. If there is no decimal part, @num_type is set to zero.
- * In case of error, @num_type is set to -1, values of @num and
- * @cur are undefined.
+ * Frees up the @duration
*/
-#define PARSE_NUM(num, cur, num_type) \
- num = 0; \
- PARSE_DIGITS(num, cur, num_type); \
- if (!num_type && (*cur == '.')) { \
- double mult = 1; \
- cur++; \
- if ((*cur < '0') || (*cur > '9')) \
- num_type = -1; \
- else \
- num_type = 1; \
- while ((*cur >= '0') && (*cur <= '9')) { \
- mult /= 10; \
- num += (*cur - '0') * mult; \
- cur++; \
- } \
- }
+static void
+exsltDateFreeDuration (exsltDateDurValPtr duration) {
+ if (duration == NULL)
+ return;
+
+ xmlFree(duration);
+}
#ifdef WITH_TIME
/**
@@ -806,31 +793,32 @@ exsltDateCurrent (void)
}
/* get real year, not years since 1900 */
- ret->value.date.year = localTm.tm_year + 1900;
+ ret->year = localTm.tm_year + 1900;
- ret->value.date.mon = localTm.tm_mon + 1;
- ret->value.date.day = localTm.tm_mday;
- ret->value.date.hour = localTm.tm_hour;
- ret->value.date.min = localTm.tm_min;
+ 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->value.date.sec = (double) localTm.tm_sec;
+ ret->sec = (double) localTm.tm_sec;
/* determine the time zone offset from local to gm time */
#if HAVE_GMTIME_R
gmtime_r(&secs, &gmTm);
#else
tb = gmtime(&secs);
- if (tb != NULL)
- gmTm = *tb;
+ if (tb == NULL)
+ return NULL;
+ gmTm = *tb;
#endif
- ret->value.date.tz_flag = 0;
+ ret->tz_flag = 0;
#if 0
- ret->value.date.tzo = (((ret->value.date.day * 1440) +
- (ret->value.date.hour * 60) +
- ret->value.date.min) -
- ((gmTm.tm_mday * 1440) + (gmTm.tm_hour * 60) +
- gmTm.tm_min));
+ 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 +
@@ -841,19 +829,19 @@ exsltDateCurrent (void)
gmTm.tm_sec;
if (localTm.tm_year < gmTm.tm_year) {
- ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
+ ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
} else if (localTm.tm_year > gmTm.tm_year) {
- ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
+ ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
} else if (localTm.tm_mon < gmTm.tm_mon) {
- ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
+ ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
} else if (localTm.tm_mon > gmTm.tm_mon) {
- ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
+ ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
} else if (localTm.tm_mday < gmTm.tm_mday) {
- ret->value.date.tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
+ ret->tzo = -((SECS_PER_DAY - local_s) + gm_s)/60;
} else if (localTm.tm_mday > gmTm.tm_mday) {
- ret->value.date.tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
+ ret->tzo = ((SECS_PER_DAY - gm_s) + local_s)/60;
} else {
- ret->value.date.tzo = (local_s - gm_s)/60;
+ ret->tzo = (local_s - gm_s)/60;
}
return ret;
@@ -877,7 +865,7 @@ exsltDateParse (const xmlChar *dateTime)
#define RETURN_TYPE_IF_VALID(t) \
if (IS_TZO_CHAR(*cur)) { \
- ret = _exsltDateParseTimeZone(&(dt->value.date), &cur); \
+ ret = _exsltDateParseTimeZone(dt, &cur); \
if (ret == 0) { \
if (*cur != 0) \
goto error; \
@@ -906,7 +894,7 @@ exsltDateParse (const xmlChar *dateTime)
/* is it an xs:gDay? */
if (*cur == '-') {
++cur;
- ret = _exsltDateParseGDay(&(dt->value.date), &cur);
+ ret = _exsltDateParseGDay(dt, &cur);
if (ret != 0)
goto error;
@@ -918,7 +906,7 @@ exsltDateParse (const xmlChar *dateTime)
/*
* it should be an xs:gMonthDay or xs:gMonth
*/
- ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
+ ret = _exsltDateParseGMonth(dt, &cur);
if (ret != 0)
goto error;
@@ -934,7 +922,7 @@ exsltDateParse (const xmlChar *dateTime)
}
/* it should be an xs:gMonthDay */
- ret = _exsltDateParseGDay(&(dt->value.date), &cur);
+ ret = _exsltDateParseGDay(dt, &cur);
if (ret != 0)
goto error;
@@ -948,7 +936,7 @@ exsltDateParse (const xmlChar *dateTime)
* Try to parse an xs:time then fallback on right-truncated dates.
*/
if ((*cur >= '0') && (*cur <= '9')) {
- ret = _exsltDateParseTime(&(dt->value.date), &cur);
+ ret = _exsltDateParseTime(dt, &cur);
if (ret == 0) {
/* it's an xs:time */
RETURN_TYPE_IF_VALID(XS_TIME);
@@ -958,7 +946,7 @@ exsltDateParse (const xmlChar *dateTime)
/* fallback on date parsing */
cur = dateTime;
- ret = _exsltDateParseGYear(&(dt->value.date), &cur);
+ ret = _exsltDateParseGYear(dt, &cur);
if (ret != 0)
goto error;
@@ -969,7 +957,7 @@ exsltDateParse (const xmlChar *dateTime)
goto error;
cur++;
- ret = _exsltDateParseGMonth(&(dt->value.date), &cur);
+ ret = _exsltDateParseGMonth(dt, &cur);
if (ret != 0)
goto error;
@@ -980,8 +968,8 @@ exsltDateParse (const xmlChar *dateTime)
goto error;
cur++;
- ret = _exsltDateParseGDay(&(dt->value.date), &cur);
- if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
+ ret = _exsltDateParseGDay(dt, &cur);
+ if ((ret != 0) || !VALID_DATE(dt))
goto error;
/* is it an xs:date? */
@@ -992,12 +980,12 @@ exsltDateParse (const xmlChar *dateTime)
cur++;
/* it should be an xs:dateTime */
- ret = _exsltDateParseTime(&(dt->value.date), &cur);
+ ret = _exsltDateParseTime(dt, &cur);
if (ret != 0)
goto error;
- ret = _exsltDateParseTimeZone(&(dt->value.date), &cur);
- if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
+ ret = _exsltDateParseTimeZone(dt, &cur);
+ if ((ret != 0) || (*cur != 0) || !VALID_DATETIME(dt))
goto error;
dt->type = XS_DATETIME;
@@ -1016,15 +1004,17 @@ error:
*
* Parses a duration string
*
- * Returns a newly built #exsltDateValPtr of NULL in case of error
+ * Returns a newly built #exsltDateDurValPtr of NULL in case of error
*/
-static exsltDateValPtr
+static exsltDateDurValPtr
exsltDateParseDuration (const xmlChar *duration)
{
const xmlChar *cur = duration;
- exsltDateValPtr dur;
+ exsltDateDurValPtr dur;
int isneg = 0;
unsigned int seq = 0;
+ long days, secs = 0;
+ double sec_frac = 0.0;
if (duration == NULL)
return NULL;
@@ -1038,15 +1028,15 @@ exsltDateParseDuration (const xmlChar *duration)
if (*cur++ != 'P')
return NULL;
- dur = exsltDateCreateDate(XS_DURATION);
+ dur = exsltDateCreateDuration();
if (dur == NULL)
return NULL;
while (*cur != 0) {
- double num;
- int num_type = 0; /* -1 = invalid, 0 = int, 1 = floating */
+ long num = 0;
+ size_t has_digits = 0;
+ int has_frac = 0;
const xmlChar desig[] = {'Y', 'M', 'D', 'H', 'M', 'S'};
- const double multi[] = { 0.0, 0.0, 86400.0, 3600.0, 60.0, 1.0, 0.0};
/* input string should be empty or invalid date/time item */
if (seq >= sizeof(desig))
@@ -1054,67 +1044,123 @@ exsltDateParseDuration (const xmlChar *duration)
/* T designator must be present for time items */
if (*cur == 'T') {
- if (seq <= 3) {
- seq = 3;
- cur++;
- } else
- return NULL;
+ if (seq > 3)
+ goto error;
+ cur++;
+ seq = 3;
} else if (seq == 3)
goto error;
- /* parse the number portion of the item */
- PARSE_NUM(num, cur, num_type);
+ /* Parse integral part. */
+ while (*cur >= '0' && *cur <= '9') {
+ long digit = *cur - '0';
- if ((num_type == -1) || (*cur == 0))
- goto error;
-
- /* update duration based on item type */
- while (seq < sizeof(desig)) {
- if (*cur == desig[seq]) {
-
- /* verify numeric type; only seconds can be float */
- if ((num_type != 0) && (seq < (sizeof(desig)-1)))
- goto error;
+ if (num > LONG_MAX / 10)
+ goto error;
+ num *= 10;
+ if (num > LONG_MAX - digit)
+ goto error;
+ num += digit;
- switch (seq) {
- case 0:
- dur->value.dur.mon = (long)num * 12;
- break;
- case 1:
- dur->value.dur.mon += (long)num;
- break;
- default:
- /* convert to seconds using multiplier */
- dur->value.dur.sec += num * multi[seq];
- seq++;
- break;
- }
+ has_digits = 1;
+ cur++;
+ }
- break; /* exit loop */
+ 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++;
}
- /* no date designators found? */
- if (++seq == 3)
+ }
+
+ 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->value.dur.mon = -dur->value.dur.mon;
- dur->value.dur.day = -dur->value.dur.day;
- dur->value.dur.sec = -dur->value.dur.sec;
+ 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->value.dur.sec);
+ "Parsed duration %f\n", dur->sec);
#endif
return dur;
error:
if (dur != NULL)
- exsltDateFreeDate(dur);
+ exsltDateFreeDuration(dur);
return NULL;
}
@@ -1126,49 +1172,47 @@ error:
* @item: char designator
*
*/
-#define FORMAT_ITEM(num, cur, limit, item) \
- if (num != 0) { \
- long comp = (long)num / limit; \
- if (comp != 0) { \
- FORMAT_FLOAT((double)comp, cur, 0); \
- *cur++ = item; \
- num -= (double)(comp * limit); \
- } \
+#define FORMAT_ITEM(num, cur, limit, item) \
+ if (num >= limit) { \
+ double comp = floor(num / limit); \
+ FORMAT_FLOAT(comp, cur, 0); \
+ *cur++ = item; \
+ num -= comp * limit; \
}
/**
* exsltDateFormatDuration:
- * @dt: an #exsltDateValDurationPtr
+ * @dur: an #exsltDateDurValPtr
*
- * Formats @dt in xs:duration format.
+ * Formats the duration.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
-exsltDateFormatDuration (const exsltDateValDurationPtr dt)
+exsltDateFormatDuration (const exsltDateDurValPtr dur)
{
xmlChar buf[100], *cur = buf;
double secs, days;
double years, months;
- if (dt == NULL)
+ if (dur == NULL)
return NULL;
/* quick and dirty check */
- if ((dt->sec == 0.0) && (dt->day == 0) && (dt->mon == 0))
+ if ((dur->sec == 0.0) && (dur->day == 0) && (dur->mon == 0))
return xmlStrdup((xmlChar*)"P0D");
- secs = dt->sec;
- days = (double)dt->day;
- years = (double)(dt->mon / 12);
- months = (double)(dt->mon % 12);
+ secs = dur->sec;
+ days = (double)dur->day;
+ years = (double)(dur->mon / 12);
+ months = (double)(dur->mon % 12);
*cur = '\0';
- if (secs < 0.0) {
- secs = -secs;
- *cur = '-';
- }
if (days < 0) {
+ if (secs != 0.0) {
+ secs = SECS_PER_DAY - secs;
+ days += 1;
+ }
days = -days;
*cur = '-';
}
@@ -1193,12 +1237,6 @@ exsltDateFormatDuration (const exsltDateValDurationPtr dt)
FORMAT_ITEM(months, cur, 1, 'M');
}
- if (secs >= SECS_PER_DAY) {
- double tmp = floor(secs / SECS_PER_DAY);
- days += tmp;
- secs -= (tmp * SECS_PER_DAY);
- }
-
FORMAT_ITEM(days, cur, 1, 'D');
if (secs > 0.0) {
*cur++ = 'T';
@@ -1217,14 +1255,14 @@ exsltDateFormatDuration (const exsltDateValDurationPtr dt)
/**
* exsltDateFormatDateTime:
- * @dt: an #exsltDateValDatePtr
+ * @dt: an #exsltDateValPtr
*
* Formats @dt in xs:dateTime format.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
-exsltDateFormatDateTime (const exsltDateValDatePtr dt)
+exsltDateFormatDateTime (const exsltDateValPtr dt)
{
xmlChar buf[100], *cur = buf;
@@ -1243,14 +1281,14 @@ exsltDateFormatDateTime (const exsltDateValDatePtr dt)
/**
* exsltDateFormatDate:
- * @dt: an #exsltDateValDatePtr
+ * @dt: an #exsltDateValPtr
*
* Formats @dt in xs:date format.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
-exsltDateFormatDate (const exsltDateValDatePtr dt)
+exsltDateFormatDate (const exsltDateValPtr dt)
{
xmlChar buf[100], *cur = buf;
@@ -1268,14 +1306,14 @@ exsltDateFormatDate (const exsltDateValDatePtr dt)
/**
* exsltDateFormatTime:
- * @dt: an #exsltDateValDatePtr
+ * @dt: an #exsltDateValPtr
*
* Formats @dt in xs:time format.
*
* Returns a newly allocated string, or NULL in case of error
*/
static xmlChar *
-exsltDateFormatTime (const exsltDateValDatePtr dt)
+exsltDateFormatTime (const exsltDateValPtr dt)
{
xmlChar buf[100], *cur = buf;
@@ -1309,14 +1347,12 @@ exsltDateFormat (const exsltDateValPtr dt)
return NULL;
switch (dt->type) {
- case XS_DURATION:
- return exsltDateFormatDuration(&(dt->value.dur));
case XS_DATETIME:
- return exsltDateFormatDateTime(&(dt->value.date));
+ return exsltDateFormatDateTime(dt);
case XS_DATE:
- return exsltDateFormatDate(&(dt->value.date));
+ return exsltDateFormatDate(dt);
case XS_TIME:
- return exsltDateFormatTime(&(dt->value.date));
+ return exsltDateFormatTime(dt);
default:
break;
}
@@ -1324,15 +1360,15 @@ exsltDateFormat (const exsltDateValPtr dt)
if (dt->type & XS_GYEAR) {
xmlChar buf[100], *cur = buf;
- FORMAT_GYEAR(dt->value.date.year, cur);
+ FORMAT_GYEAR(dt->year, cur);
if (dt->type == XS_GYEARMONTH) {
*cur = '-';
cur++;
- FORMAT_GMONTH(dt->value.date.mon, cur);
+ FORMAT_GMONTH(dt->mon, cur);
}
- if (dt->value.date.tz_flag || (dt->value.date.tzo != 0)) {
- FORMAT_TZ(dt->value.date.tzo, cur);
+ if (dt->tz_flag || (dt->tzo != 0)) {
+ FORMAT_TZ(dt->tzo, cur);
}
*cur = 0;
return xmlStrdup(buf);
@@ -1348,7 +1384,7 @@ exsltDateFormat (const exsltDateValPtr dt)
* 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 and there is no zero year.
+ * years must be handled a little differently.
*
* Returns number of days.
*/
@@ -1357,16 +1393,16 @@ _exsltDateCastYMToDays (const exsltDateValPtr dt)
{
long ret;
- if (dt->value.date.year < 0)
- ret = (dt->value.date.year * 365) +
- (((dt->value.date.year+1)/4)-((dt->value.date.year+1)/100)+
- ((dt->value.date.year+1)/400)) +
- DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
+ 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->value.date.year-1) * 365) +
- (((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
- ((dt->value.date.year-1)/400)) +
- DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year);
+ 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;
}
@@ -1380,42 +1416,8 @@ _exsltDateCastYMToDays (const exsltDateValPtr dt)
* Returns seconds.
*/
#define TIME_TO_NUMBER(dt) \
- ((double)((dt->value.date.hour * SECS_PER_HOUR) + \
- (dt->value.date.min * SECS_PER_MIN)) + dt->value.date.sec)
-
-/**
- * exsltDateCastDateToNumber:
- * @dt: an #exsltDateValPtr
- *
- * Calculates the number of seconds from year zero.
- *
- * Returns seconds from zero year.
- */
-static double
-exsltDateCastDateToNumber (const exsltDateValPtr dt)
-{
- double ret = 0.0;
-
- if (dt == NULL)
- return 0.0;
-
- if ((dt->type & XS_GYEAR) == XS_GYEAR) {
- ret = (double)_exsltDateCastYMToDays(dt) * SECS_PER_DAY;
- }
-
- /* add in days */
- if (dt->type == XS_DURATION) {
- ret += (double)dt->value.dur.day * SECS_PER_DAY;
- ret += dt->value.dur.sec;
- } else {
- ret += (double)dt->value.date.day * SECS_PER_DAY;
- /* add in time */
- ret += TIME_TO_NUMBER(dt);
- }
-
-
- return ret;
-}
+ ((double)((dt->hour * SECS_PER_HOUR) + \
+ (dt->min * SECS_PER_MIN)) + dt->sec)
/**
* _exsltDateTruncateDate:
@@ -1433,19 +1435,19 @@ _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
return 1;
if ((type & XS_TIME) != XS_TIME) {
- dt->value.date.hour = 0;
- dt->value.date.min = 0;
- dt->value.date.sec = 0.0;
+ dt->hour = 0;
+ dt->min = 0;
+ dt->sec = 0.0;
}
if ((type & XS_GDAY) != XS_GDAY)
- dt->value.date.day = 1;
+ dt->day = 1;
if ((type & XS_GMONTH) != XS_GMONTH)
- dt->value.date.mon = 1;
+ dt->mon = 1;
if ((type & XS_GYEAR) != XS_GYEAR)
- dt->value.date.year = 0;
+ dt->year = 0;
dt->type = type;
@@ -1461,7 +1463,7 @@ _exsltDateTruncateDate (exsltDateValPtr dt, exsltDateType type)
* 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 and there is no zero year.
+ * years must be handled a little differently.
*
* Returns day in week (Sunday = 0).
*/
@@ -1470,8 +1472,8 @@ _exsltDateDayInWeek(long yday, long yr)
{
long ret;
- if (yr < 0) {
- ret = ((yr + (((yr+1)/4)-((yr+1)/100)+((yr+1)/400)) + yday) % 7);
+ if (yr <= 0) {
+ ret = ((yr-2 + ((yr/4)-(yr/100)+(yr/400)) + yday) % 7);
if (ret < 0)
ret += 7;
} else
@@ -1480,18 +1482,10 @@ _exsltDateDayInWeek(long yday, long yr)
return ret;
}
-/*
- * macros for adding date/times and durations
- */
-#define FQUOTIENT(a,b) ((floor(((double)a/(double)b))))
-#define MODULO(a,b) ((a - FQUOTIENT(a,b) * b))
-#define FQUOTIENT_RANGE(a,low,high) (FQUOTIENT((a-low),(high-low)))
-#define MODULO_RANGE(a,low,high) ((MODULO((a-low),(high-low)))+low)
-
/**
* _exsltDateAdd:
* @dt: an #exsltDateValPtr
- * @dur: an #exsltDateValPtr of type #XS_DURATION
+ * @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.
@@ -1499,12 +1493,11 @@ _exsltDateDayInWeek(long yday, long yr)
* Returns date/time pointer or NULL.
*/
static exsltDateValPtr
-_exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
+_exsltDateAdd (exsltDateValPtr dt, exsltDateDurValPtr dur)
{
exsltDateValPtr ret;
- long carry, tempdays, temp;
- exsltDateValDatePtr r, d;
- exsltDateValDurationPtr u;
+ long carry, temp;
+ double sum;
if ((dt == NULL) || (dur == NULL))
return NULL;
@@ -1513,105 +1506,119 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
if (ret == NULL)
return NULL;
- r = &(ret->value.date);
- d = &(dt->value.date);
- u = &(dur->value.dur);
+ /*
+ * Note that temporary values may need more bits than the values in
+ * bit field.
+ */
/* month */
- carry = d->mon + u->mon;
- r->mon = (unsigned int)MODULO_RANGE(carry, 1, 13);
- carry = (long)FQUOTIENT_RANGE(carry, 1, 13);
+ 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) */
- r->year = d->year + carry;
- if (r->year == 0) {
- if (d->year > 0)
- r->year--;
- else
- r->year++;
+ /*
+ * 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 > LONG_MAX - carry) ||
+ (carry < 0 && dt->year < LONG_MIN - carry)) {
+ /* Overflow */
+ exsltDateFreeDate(ret);
+ return NULL;
}
+ ret->year = dt->year + carry;
/* time zone */
- r->tzo = d->tzo;
- r->tz_flag = d->tz_flag;
+ ret->tzo = dt->tzo;
+ ret->tz_flag = dt->tz_flag;
/* seconds */
- r->sec = d->sec + u->sec;
- carry = (long)FQUOTIENT((long)r->sec, 60);
- if (r->sec != 0.0) {
- r->sec = MODULO(r->sec, 60.0);
- }
+ sum = dt->sec + dur->sec;
+ ret->sec = fmod(sum, 60.0);
+ carry = (long)(sum / 60.0);
/* minute */
- carry += d->min;
- r->min = (unsigned int)MODULO(carry, 60);
- carry = (long)FQUOTIENT(carry, 60);
+ temp = dt->min + carry % 60;
+ carry = carry / 60;
+ if (temp >= 60) {
+ temp -= 60;
+ carry += 1;
+ }
+ ret->min = temp;
/* hours */
- carry += d->hour;
- r->hour = (unsigned int)MODULO(carry, 24);
- carry = (long)FQUOTIENT(carry, 24);
+ temp = dt->hour + carry % 24;
+ carry = carry / 24;
+ if (temp >= 24) {
+ temp -= 24;
+ carry += 1;
+ }
+ ret->hour = temp;
- /*
- * days
- * Note we use tempdays because the temporary values may need more
- * than 5 bits
- */
- if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
- (d->day > MAX_DAYINMONTH(r->year, r->mon)))
- tempdays = MAX_DAYINMONTH(r->year, r->mon);
- else if (d->day < 1)
- tempdays = 1;
+ /* 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
- tempdays = d->day;
+ temp = dt->day;
- tempdays += u->day + carry;
+ temp += dur->day % DAYS_PER_EPOCH + carry;
while (1) {
- if (tempdays < 1) {
- long tmon = (long)MODULO_RANGE((int)r->mon-1, 1, 13);
- long tyr = r->year + (long)FQUOTIENT_RANGE((int)r->mon-1, 1, 13);
- if (tyr == 0)
- tyr--;
- /*
- * Coverity detected an overrun in daysInMonth
- * of size 12 at position 12 with index variable "((r)->mon - 1)"
- */
- if (tmon < 0)
- tmon = 0;
- if (tmon > 12)
- tmon = 12;
- tempdays += MAX_DAYINMONTH(tyr, tmon);
- carry = -1;
- } else if (tempdays > (long)MAX_DAYINMONTH(r->year, r->mon)) {
- tempdays = tempdays - MAX_DAYINMONTH(r->year, r->mon);
- carry = 1;
+ if (temp < 1) {
+ if (ret->mon > 1) {
+ ret->mon -= 1;
+ }
+ else {
+ if (ret->year == LONG_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 == LONG_MAX) {
+ exsltDateFreeDate(ret);
+ return NULL;
+ }
+ ret->mon = 1;
+ ret->year += 1;
+ }
} else
break;
-
- temp = r->mon + carry;
- r->mon = (unsigned int)MODULO_RANGE(temp, 1, 13);
- r->year = r->year + (long)FQUOTIENT_RANGE(temp, 1, 13);
- if (r->year == 0) {
- if (temp < 1)
- r->year--;
- else
- r->year++;
- }
}
- r->day = tempdays;
+ ret->day = temp;
/*
* adjust the date/time type to the date values
*/
if (ret->type != XS_DATETIME) {
- if ((r->hour) || (r->min) || (r->sec))
+ if ((ret->hour) || (ret->min) || (ret->sec))
ret->type = XS_DATETIME;
else if (ret->type != XS_DATE) {
- if (r->day != 1)
+ if (ret->day != 1)
ret->type = XS_DATE;
- else if ((ret->type != XS_GYEARMONTH) && (r->mon != 1))
+ else if ((ret->type != XS_GYEARMONTH) && (ret->mon != 1))
ret->type = XS_GYEARMONTH;
}
}
@@ -1629,12 +1636,12 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
* (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 date/time pointer or NULL.
+ * Returns a duration pointer or NULL.
*/
-static exsltDateValPtr
+static exsltDateDurValPtr
_exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
{
- exsltDateValPtr ret;
+ exsltDateDurValPtr ret;
if ((x == NULL) || (y == NULL))
return NULL;
@@ -1655,30 +1662,37 @@ _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
}
}
- ret = exsltDateCreateDate(XS_DURATION);
+ ret = exsltDateCreateDuration();
if (ret == NULL)
return NULL;
if (((x->type == XS_GYEAR) || (x->type == XS_GYEARMONTH)) && (!flag)) {
/* compute the difference in months */
- ret->value.dur.mon = ((y->value.date.year * 12) + y->value.date.mon) -
- ((x->value.date.year * 12) + x->value.date.mon);
- /* The above will give a wrong result if x and y are on different sides
- of the September 1752. Resolution is welcome :-) */
+ 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 {
- ret->value.dur.day = _exsltDateCastYMToDays(y) -
- _exsltDateCastYMToDays(x);
- ret->value.dur.day += y->value.date.day - x->value.date.day;
- ret->value.dur.sec = TIME_TO_NUMBER(y) - TIME_TO_NUMBER(x);
- ret->value.dur.sec += (x->value.date.tzo - y->value.date.tzo) *
- SECS_PER_MIN;
- if (ret->value.dur.day > 0.0 && ret->value.dur.sec < 0.0) {
- ret->value.dur.day -= 1;
- ret->value.dur.sec = ret->value.dur.sec + SECS_PER_DAY;
- } else if (ret->value.dur.day < 0.0 && ret->value.dur.sec > 0.0) {
- ret->value.dur.day += 1;
- ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
- }
+ 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;
@@ -1686,9 +1700,9 @@ _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
/**
* _exsltDateAddDurCalc
- * @ret: an exsltDateValPtr for the return value:
- * @x: an exsltDateValPtr for the first operand
- * @y: an exsltDateValPtr for the second operand
+ * @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.
@@ -1696,72 +1710,76 @@ _exsltDateDifference (exsltDateValPtr x, exsltDateValPtr y, int flag)
* Returns 1 for success, 0 if error detected.
*/
static int
-_exsltDateAddDurCalc (exsltDateValPtr ret, exsltDateValPtr x,
- exsltDateValPtr y)
+_exsltDateAddDurCalc (exsltDateDurValPtr ret, exsltDateDurValPtr x,
+ exsltDateDurValPtr y)
{
- long carry;
-
/* months */
- ret->value.dur.mon = x->value.dur.mon + y->value.dur.mon;
-
- /* seconds */
- ret->value.dur.sec = x->value.dur.sec + y->value.dur.sec;
- carry = (long)FQUOTIENT(ret->value.dur.sec, SECS_PER_DAY);
- if (ret->value.dur.sec != 0.0) {
- ret->value.dur.sec = MODULO(ret->value.dur.sec, SECS_PER_DAY);
- /*
- * Our function MODULO always gives us a positive value, so
- * if we end up with a "-ve" carry we need to adjust it
- * appropriately (bug 154021)
- */
- if ((carry < 0) && (ret->value.dur.sec != 0)) {
- /* change seconds to equiv negative modulus */
- ret->value.dur.sec = ret->value.dur.sec - SECS_PER_DAY;
- carry++;
- }
+ 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 */
- ret->value.dur.day = x->value.dur.day + y->value.dur.day + carry;
+ 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->value.dur.day > 0) || (ret->value.dur.sec > 0)) &&
- (ret->value.dur.mon < 0)) ||
- (((ret->value.dur.day < 0) || (ret->value.dur.sec < 0)) &&
- (ret->value.dur.mon > 0))) {
- return 0;
+ 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 #exsltDateValPtr of type #XS_DURATION
- * @y: an #exsltDateValPtr of type #XS_DURATION
+ * @x: an #exsltDateDurValPtr
+ * @y: an #exsltDateDurValPtr
*
* Compute a new duration from @x and @y.
*
- * Returns date/time pointer or NULL.
+ * Returns a duration pointer or NULL.
*/
-static exsltDateValPtr
-_exsltDateAddDuration (exsltDateValPtr x, exsltDateValPtr y)
+static exsltDateDurValPtr
+_exsltDateAddDuration (exsltDateDurValPtr x, exsltDateDurValPtr y)
{
- exsltDateValPtr ret;
+ exsltDateDurValPtr ret;
if ((x == NULL) || (y == NULL))
return NULL;
- ret = exsltDateCreateDate(XS_DURATION);
+ ret = exsltDateCreateDuration();
if (ret == NULL)
return NULL;
if (_exsltDateAddDurCalc(ret, x, y))
return ret;
- exsltDateFreeDate(ret);
+ exsltDateFreeDuration(ret);
return NULL;
}
@@ -1788,7 +1806,7 @@ exsltDateDateTime (void)
cur = exsltDateCurrent();
if (cur != NULL) {
- ret = exsltDateFormatDateTime(&(cur->value.date));
+ ret = exsltDateFormatDateTime(cur);
exsltDateFreeDate(cur);
}
#endif
@@ -1834,7 +1852,7 @@ exsltDateDate (const xmlChar *dateTime)
}
}
- ret = exsltDateFormatDate(&(dt->value.date));
+ ret = exsltDateFormatDate(dt);
exsltDateFreeDate(dt);
return ret;
@@ -1878,7 +1896,7 @@ exsltDateTime (const xmlChar *dateTime)
}
}
- ret = exsltDateFormatTime(&(dt->value.date));
+ ret = exsltDateFormatTime(dt);
exsltDateFreeDate(dt);
return ret;
@@ -1908,6 +1926,7 @@ static double
exsltDateYear (const xmlChar *dateTime)
{
exsltDateValPtr dt;
+ long year;
double ret;
if (dateTime == NULL) {
@@ -1927,7 +1946,9 @@ exsltDateYear (const xmlChar *dateTime)
}
}
- ret = (double) dt->value.date.year;
+ year = dt->year;
+ if (year <= 0) year -= 1; /* Adjust for missing year 0. */
+ ret = (double) year;
exsltDateFreeDate(dt);
return ret;
@@ -1956,16 +1977,32 @@ exsltDateYear (const xmlChar *dateTime)
static xmlXPathObjectPtr
exsltDateLeapYear (const xmlChar *dateTime)
{
- double year;
+ exsltDateValPtr dt = NULL;
+ xmlXPathObjectPtr ret;
- year = exsltDateYear(dateTime);
- if (xmlXPathIsNaN(year))
- return xmlXPathNewFloat(xmlXPathNAN);
+ if (dateTime == NULL) {
+#ifdef WITH_TIME
+ dt = exsltDateCurrent();
+#endif
+ } 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 (IS_LEAP((long)year))
- return xmlXPathNewBoolean(1);
+ if (dt == NULL) {
+ ret = xmlXPathNewFloat(xmlXPathNAN);
+ }
+ else {
+ ret = xmlXPathNewBoolean(IS_LEAP(dt->year));
+ exsltDateFreeDate(dt);
+ }
- return xmlXPathNewBoolean(0);
+ return ret;
}
/**
@@ -2013,7 +2050,7 @@ exsltDateMonthInYear (const xmlChar *dateTime)
}
}
- ret = (double) dt->value.date.mon;
+ ret = (double) dt->mon;
exsltDateFreeDate(dt);
return ret;
@@ -2060,11 +2097,12 @@ exsltDateMonthName (const xmlChar *dateTime)
{ 'N', 'o', 'v', 'e', 'm', 'b', 'e', 'r', 0 },
{ 'D', 'e', 'c', 'e', 'm', 'b', 'e', 'r', 0 }
};
- int month;
- month = (int) exsltDateMonthInYear(dateTime);
- if (!VALID_MONTH(month))
- month = 0;
- return monthNames[month];
+ double month;
+ int index = 0;
+ month = exsltDateMonthInYear(dateTime);
+ if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0))
+ index = (int) month;
+ return monthNames[index];
}
/**
@@ -2108,11 +2146,12 @@ exsltDateMonthAbbreviation (const xmlChar *dateTime)
{ 'N', 'o', 'v', 0 },
{ 'D', 'e', 'c', 0 }
};
- int month;
- month = (int) exsltDateMonthInYear(dateTime);
- if(!VALID_MONTH(month))
- month = 0;
- return monthAbbreviations[month];
+ double month;
+ int index = 0;
+ month = exsltDateMonthInYear(dateTime);
+ if (!xmlXPathIsNaN(month) && (month >= 1.0) && (month <= 12.0))
+ index = (int) month;
+ return monthAbbreviations[index];
}
/**
@@ -2158,23 +2197,22 @@ exsltDateWeekInYear (const xmlChar *dateTime)
}
}
- diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
- dt->value.date.year);
+ 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->value.date.year) + 6) % 7;
+ diw = (_exsltDateDayInWeek(diy, dt->year) + 6) % 7;
/* ISO 8601 adjustment, 3 is Thu */
diy += (3 - diw);
if(diy < 1) {
- year = dt->value.date.year - 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->value.date.year)) {
- diy -= DAY_IN_YEAR(31, 12, dt->value.date.year);
+ } else if (diy > (long)DAY_IN_YEAR(31, 12, dt->year)) {
+ diy -= DAY_IN_YEAR(31, 12, dt->year);
}
ret = ((diy - 1) / 7) + 1;
@@ -2227,14 +2265,14 @@ exsltDateWeekInMonth (const xmlChar *dateTime)
}
}
- fdiy = DAY_IN_YEAR(1, dt->value.date.mon, dt->value.date.year);
+ 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->value.date.year) + 6) % 7;
+ fdiw = (_exsltDateDayInWeek(fdiy, dt->year) + 6) % 7;
- ret = ((dt->value.date.day + fdiw - 1) / 7) + 1;
+ ret = ((dt->day + fdiw - 1) / 7) + 1;
exsltDateFreeDate(dt);
@@ -2281,8 +2319,7 @@ exsltDateDayInYear (const xmlChar *dateTime)
}
}
- ret = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
- dt->value.date.year);
+ ret = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
exsltDateFreeDate(dt);
@@ -2332,7 +2369,7 @@ exsltDateDayInMonth (const xmlChar *dateTime)
}
}
- ret = (double) dt->value.date.day;
+ ret = (double) dt->day;
exsltDateFreeDate(dt);
return ret;
@@ -2379,7 +2416,7 @@ exsltDateDayOfWeekInMonth (const xmlChar *dateTime)
}
}
- ret = ((dt->value.date.day -1) / 7) + 1;
+ ret = ((dt->day -1) / 7) + 1;
exsltDateFreeDate(dt);
@@ -2428,10 +2465,9 @@ exsltDateDayInWeek (const xmlChar *dateTime)
}
}
- diy = DAY_IN_YEAR(dt->value.date.day, dt->value.date.mon,
- dt->value.date.year);
+ diy = DAY_IN_YEAR(dt->day, dt->mon, dt->year);
- ret = _exsltDateDayInWeek(diy, dt->value.date.year) + 1;
+ ret = _exsltDateDayInWeek(diy, dt->year) + 1;
exsltDateFreeDate(dt);
@@ -2471,11 +2507,12 @@ exsltDateDayName (const xmlChar *dateTime)
{ 'F', 'r', 'i', 'd', 'a', 'y', 0 },
{ 'S', 'a', 't', 'u', 'r', 'd', 'a', 'y', 0 }
};
- int day;
- day = (int) exsltDateDayInWeek(dateTime);
- if((day < 1) || (day > 7))
- day = 0;
- return dayNames[day];
+ double day;
+ int index = 0;
+ day = exsltDateDayInWeek(dateTime);
+ if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0))
+ index = (int) day;
+ return dayNames[index];
}
/**
@@ -2511,11 +2548,12 @@ exsltDateDayAbbreviation (const xmlChar *dateTime)
{ 'F', 'r', 'i', 0 },
{ 'S', 'a', 't', 0 }
};
- int day;
- day = (int) exsltDateDayInWeek(dateTime);
- if((day < 1) || (day > 7))
- day = 0;
- return dayAbbreviations[day];
+ double day;
+ int index = 0;
+ day = exsltDateDayInWeek(dateTime);
+ if(!xmlXPathIsNaN(day) && (day >= 1.0) && (day <= 7.0))
+ index = (int) day;
+ return dayAbbreviations[index];
}
/**
@@ -2558,7 +2596,7 @@ exsltDateHourInDay (const xmlChar *dateTime)
}
}
- ret = (double) dt->value.date.hour;
+ ret = (double) dt->hour;
exsltDateFreeDate(dt);
return ret;
@@ -2604,7 +2642,7 @@ exsltDateMinuteInHour (const xmlChar *dateTime)
}
}
- ret = (double) dt->value.date.min;
+ ret = (double) dt->min;
exsltDateFreeDate(dt);
return ret;
@@ -2652,7 +2690,7 @@ exsltDateSecondInMinute (const xmlChar *dateTime)
}
}
- ret = dt->value.date.sec;
+ ret = dt->sec;
exsltDateFreeDate(dt);
return ret;
@@ -2685,7 +2723,8 @@ exsltDateSecondInMinute (const xmlChar *dateTime)
static xmlChar *
exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
{
- exsltDateValPtr dt, dur, res;
+ exsltDateValPtr dt, res;
+ exsltDateDurValPtr dur;
xmlChar *ret;
if ((xstr == NULL) || (ystr == NULL))
@@ -2708,7 +2747,7 @@ exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
res = _exsltDateAdd(dt, dur);
exsltDateFreeDate(dt);
- exsltDateFreeDate(dur);
+ exsltDateFreeDuration(dur);
if (res == NULL)
return NULL;
@@ -2743,7 +2782,7 @@ exsltDateAdd (const xmlChar *xstr, const xmlChar *ystr)
static xmlChar *
exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
{
- exsltDateValPtr x, y, res;
+ exsltDateDurValPtr x, y, res;
xmlChar *ret;
if ((xstr == NULL) || (ystr == NULL))
@@ -2755,20 +2794,20 @@ exsltDateAddDuration (const xmlChar *xstr, const xmlChar *ystr)
y = exsltDateParseDuration(ystr);
if (y == NULL) {
- exsltDateFreeDate(x);
+ exsltDateFreeDuration(x);
return NULL;
}
res = _exsltDateAddDuration(x, y);
- exsltDateFreeDate(x);
- exsltDateFreeDate(y);
+ exsltDateFreeDuration(x);
+ exsltDateFreeDuration(y);
if (res == NULL)
return NULL;
- ret = exsltDateFormatDuration(&(res->value.dur));
- exsltDateFreeDate(res);
+ ret = exsltDateFormatDuration(res);
+ exsltDateFreeDuration(res);
return ret;
}
@@ -2798,7 +2837,7 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
xmlNodeSetPtr ns;
void *user = NULL;
xmlChar *tmp;
- exsltDateValPtr x, total;
+ exsltDateDurValPtr x, total;
xmlChar *ret;
int i;
@@ -2825,7 +2864,7 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
return;
}
- total = exsltDateCreateDate (XS_DURATION);
+ total = exsltDateCreateDuration ();
if (total == NULL) {
xmlXPathFreeNodeSet (ns);
return;
@@ -2836,14 +2875,14 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
tmp = xmlXPathCastNodeToString (ns->nodeTab[i]);
if (tmp == NULL) {
xmlXPathFreeNodeSet (ns);
- exsltDateFreeDate (total);
+ exsltDateFreeDuration (total);
return;
}
x = exsltDateParseDuration (tmp);
if (x == NULL) {
xmlFree (tmp);
- exsltDateFreeDate (total);
+ exsltDateFreeDuration (total);
xmlXPathFreeNodeSet (ns);
xmlXPathReturnEmptyString (ctxt);
return;
@@ -2851,18 +2890,18 @@ exsltDateSumFunction (xmlXPathParserContextPtr ctxt, int nargs)
result = _exsltDateAddDurCalc(total, total, x);
- exsltDateFreeDate (x);
+ exsltDateFreeDuration (x);
xmlFree (tmp);
if (!result) {
- exsltDateFreeDate (total);
+ exsltDateFreeDuration (total);
xmlXPathFreeNodeSet (ns);
xmlXPathReturnEmptyString (ctxt);
return;
}
}
- ret = exsltDateFormatDuration (&(total->value.dur));
- exsltDateFreeDate (total);
+ ret = exsltDateFormatDuration (total);
+ exsltDateFreeDuration (total);
xmlXPathFreeNodeSet (ns);
if (user != NULL)
@@ -2906,6 +2945,7 @@ static double
exsltDateSeconds (const xmlChar *dateTime)
{
exsltDateValPtr dt;
+ exsltDateDurValPtr dur = NULL;
double ret = xmlXPathNAN;
if (dateTime == NULL) {
@@ -2915,16 +2955,14 @@ exsltDateSeconds (const xmlChar *dateTime)
#endif
return xmlXPathNAN;
} else {
- dt = exsltDateParseDuration(dateTime);
+ dt = exsltDateParse(dateTime);
if (dt == NULL)
- dt = exsltDateParse(dateTime);
+ dur = exsltDateParseDuration(dateTime);
}
- if (dt == NULL)
- return xmlXPathNAN;
-
- if ((dt->type <= XS_DATETIME) && (dt->type >= XS_GYEAR)) {
- exsltDateValPtr y, dur;
+ if ((dt != NULL) && (dt->type >= XS_GYEAR)) {
+ exsltDateValPtr y;
+ exsltDateDurValPtr diff;
/*
* compute the difference between the given (or current) date
@@ -2932,23 +2970,27 @@ exsltDateSeconds (const xmlChar *dateTime)
*/
y = exsltDateCreateDate(XS_DATETIME);
if (y != NULL) {
- y->value.date.year = 1970;
- y->value.date.mon = 1;
- y->value.date.day = 1;
- y->value.date.tz_flag = 1;
-
- dur = _exsltDateDifference(y, dt, 1);
- if (dur != NULL) {
- ret = exsltDateCastDateToNumber(dur);
- exsltDateFreeDate(dur);
+ 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 ((dt->type == XS_DURATION) && (dt->value.dur.mon == 0))
- ret = exsltDateCastDateToNumber(dt);
+ } else if ((dur != NULL) && (dur->mon == 0)) {
+ ret = (double)dur->day * SECS_PER_DAY + dur->sec;
+ }
- exsltDateFreeDate(dt);
+ if (dt != NULL)
+ exsltDateFreeDate(dt);
+ if (dur != NULL)
+ exsltDateFreeDuration(dur);
return ret;
}
@@ -2993,8 +3035,9 @@ exsltDateSeconds (const xmlChar *dateTime)
static xmlChar *
exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
{
- exsltDateValPtr x, y, dur;
- xmlChar *ret = NULL;
+ exsltDateValPtr x, y;
+ exsltDateDurValPtr dur;
+ xmlChar *ret = NULL;
if ((xstr == NULL) || (ystr == NULL))
return NULL;
@@ -3024,8 +3067,8 @@ exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
if (dur == NULL)
return NULL;
- ret = exsltDateFormatDuration(&(dur->value.dur));
- exsltDateFreeDate(dur);
+ ret = exsltDateFormatDuration(dur);
+ exsltDateFreeDuration(dur);
return ret;
}
@@ -3053,8 +3096,8 @@ exsltDateDifference (const xmlChar *xstr, const xmlChar *ystr)
static xmlChar *
exsltDateDuration (const xmlChar *number)
{
- exsltDateValPtr dur;
- double secs;
+ exsltDateDurValPtr dur;
+ double secs, days;
xmlChar *ret;
if (number == NULL)
@@ -3065,14 +3108,16 @@ exsltDateDuration (const xmlChar *number)
if ((xmlXPathIsNaN(secs)) || (xmlXPathIsInf(secs)))
return NULL;
- dur = exsltDateCreateDate(XS_DURATION);
+ dur = exsltDateCreateDuration();
if (dur == NULL)
return NULL;
- dur->value.dur.sec = secs;
+ days = floor(secs / SECS_PER_DAY);
+ dur->day = (long)days;
+ dur->sec = secs - days * SECS_PER_DAY;
- ret = exsltDateFormatDuration(&(dur->value.dur));
- exsltDateFreeDate(dur);
+ ret = exsltDateFormatDuration(dur);
+ exsltDateFreeDuration(dur);
return ret;
}