[libxslt] Change internal representation of years
- From: Nick Wellnhofer <nwellnhof src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libxslt] Change internal representation of years
- Date: Thu, 18 May 2017 16:24:55 +0000 (UTC)
commit 624cb21fc267fe227ef96638af0d8944e2cc4fc9
Author: Nick Wellnhofer <wellnhofer aevum de>
Date: Wed May 17 21:52:23 2017 +0200
Change internal representation of years
XML Schema Part 2 doesn't allow the year 0000 which seems to imply that
year 0001 is preceded by -0001. The old code followed this convention but
it represented the year -0001 as -1, requiring some adjustments when
crossing the beginning of year 0001.
Now the year -0001 is represented by 0 internally (astronomical year
numbering). This simplifies some calculations.
As a side effect, (XML Schema) years -0001, -0005, ... are now leap years.
Previously, years -0004, -0008, ... were leap years. The new behavior
seems more correct and better matches other implementations of the
proleptic Gregorian calendar.
Also fixes some bugs:
- Previously, date:day-in-week() returned wrong values for dates before
the year 3 BC. For example, it returned 6 (Friday) for both
'-0004-12-31' and '-0003-01-01'. Now it returns 4 (Wednesday) for
'-0004-12-31' and 5 (Thursday) for '-0003-01-01' (because of the leap
year change).
- date:add could return wrong results when crossing AD 1. For example,
date:add('-0001-01-01', 'P2Y') would return '0001-01-01' instead of
'0002-01-01'.
- Likewise, date:difference produced wrong results when working on
years or yearMonths.
libexslt/date.c | 91 +++++++++++++++++++++----------------
tests/exslt/date/add.1.out | 2 +
tests/exslt/date/add.1.xml | 1 +
tests/exslt/date/date.1.out | 10 ++--
tests/exslt/date/date.1.xml | 2 +-
tests/exslt/date/datetime.1.out | 14 +++---
tests/exslt/date/difference.1.out | 4 +-
tests/exslt/date/difference.1.xml | 1 +
tests/exslt/date/gyear.1.out | 4 +-
tests/exslt/date/gyear.1.xml | 2 +-
tests/exslt/date/gyearmonth.1.out | 4 +-
tests/exslt/date/gyearmonth.1.xml | 2 +-
tests/exslt/date/seconds.1.out | 2 +-
13 files changed, 78 insertions(+), 61 deletions(-)
---
diff --git a/libexslt/date.c b/libexslt/date.c
index 625dea6..81fddf9 100644
--- a/libexslt/date.c
+++ b/libexslt/date.c
@@ -139,7 +139,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))
@@ -164,7 +163,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
@@ -201,6 +200,10 @@ 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
@@ -231,17 +234,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 +260,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) { \
@@ -1348,7 +1352,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,11 +1361,11 @@ _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->value.date.year <= 0)
+ ret = ((dt->value.date.year-1) * 365) +
+ (((dt->value.date.year)/4)-((dt->value.date.year)/100)+
+ ((dt->value.date.year)/400)) +
+ DAY_IN_YEAR(0, dt->value.date.mon, dt->value.date.year) - 1;
else
ret = ((dt->value.date.year-1) * 365) +
(((dt->value.date.year-1)/4)-((dt->value.date.year-1)/100)+
@@ -1387,7 +1391,7 @@ _exsltDateCastYMToDays (const exsltDateValPtr dt)
* exsltDateCastDateToNumber:
* @dt: an #exsltDateValPtr
*
- * Calculates the number of seconds from year zero.
+ * Calculates the number of seconds from year 1 AD.
*
* Returns seconds from zero year.
*/
@@ -1461,7 +1465,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 +1474,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
@@ -1524,12 +1528,6 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
/* year (may be modified later) */
r->year = d->year + carry;
- if (r->year == 0) {
- if (d->year > 0)
- r->year--;
- else
- r->year++;
- }
/* time zone */
r->tzo = d->tzo;
@@ -1557,7 +1555,7 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
* Note we use tempdays because the temporary values may need more
* than 5 bits
*/
- if ((VALID_YEAR(r->year)) && (VALID_MONTH(r->mon)) &&
+ if ((VALID_MONTH(r->mon)) &&
(d->day > MAX_DAYINMONTH(r->year, r->mon)))
tempdays = MAX_DAYINMONTH(r->year, r->mon);
else if (d->day < 1)
@@ -1592,12 +1590,6 @@ _exsltDateAdd (exsltDateValPtr dt, exsltDateValPtr dur)
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;
@@ -1908,6 +1900,7 @@ static double
exsltDateYear (const xmlChar *dateTime)
{
exsltDateValPtr dt;
+ long year;
double ret;
if (dateTime == NULL) {
@@ -1927,7 +1920,9 @@ exsltDateYear (const xmlChar *dateTime)
}
}
- ret = (double) dt->value.date.year;
+ year = dt->value.date.year;
+ if (year <= 0) year -= 1; /* Adjust for missing year 0. */
+ ret = (double) year;
exsltDateFreeDate(dt);
return ret;
@@ -1956,16 +1951,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->value.date.year));
+ exsltDateFreeDate(dt);
+ }
- return xmlXPathNewBoolean(0);
+ return ret;
}
/**
diff --git a/tests/exslt/date/add.1.out b/tests/exslt/date/add.1.out
index 47394ae..76c1865 100644
--- a/tests/exslt/date/add.1.out
+++ b/tests/exslt/date/add.1.out
@@ -21,6 +21,8 @@ add : -0001 + -PT59S
result : -0002-12-31T23:59:01Z
add : -0001 + P1Y
result : 0001
+add : -0001-01-01 + P2Y
+result : 0002-01-01
add : 2000-01 + -PT86400S
result : 1999-12-31
add : 2000-01 + -P1D
diff --git a/tests/exslt/date/add.1.xml b/tests/exslt/date/add.1.xml
index 5555747..94cf1ad 100644
--- a/tests/exslt/date/add.1.xml
+++ b/tests/exslt/date/add.1.xml
@@ -11,6 +11,7 @@
<date date='2000-01-01T00:00:00Z' dur='-PT59S'/>
<date date='-0001' dur='-PT59S'/>
<date date='-0001' dur='P1Y'/>
+ <date date='-0001-01-01' dur='P2Y'/>
<date date='2000-01' dur='-PT86400S'/>
<date date='2000-01' dur='-P1D'/>
<date date='1970-01-01T00:00:00-00:30' dur='-PT30S'/>
diff --git a/tests/exslt/date/date.1.out b/tests/exslt/date/date.1.out
index f22546c..8296de5 100644
--- a/tests/exslt/date/date.1.out
+++ b/tests/exslt/date/date.1.out
@@ -73,8 +73,8 @@
minute-in-hour : NaN
second-in-minute : NaN
- Test Date : -0004-02-29
- year : -4
+ Test Date : -0005-02-29
+ year : -5
leap-year : true
month-in-year : 2
month-name : February
@@ -83,9 +83,9 @@
day-in-year : 60
day-in-month : 29
day-of-week-in-month : 5
- day-in-week : 1
- day-name : Sunday
- day-abbreviation : Sun
+ day-in-week : 5
+ day-name : Thursday
+ day-abbreviation : Thu
time :
hour-in-day : NaN
minute-in-hour : NaN
diff --git a/tests/exslt/date/date.1.xml b/tests/exslt/date/date.1.xml
index 562e08e..7f3e9f5 100644
--- a/tests/exslt/date/date.1.xml
+++ b/tests/exslt/date/date.1.xml
@@ -5,7 +5,7 @@
<date date="3000-01-31"/>
<date date="2000-02-29"/>
<date date="9990001-12-31Z"/>
- <date date="-0004-02-29"/>
+ <date date="-0005-02-29"/>
<date date="1999-01-02"/>
<date date="1999-01-03"/>
<date date="2004-01-01"/>
diff --git a/tests/exslt/date/datetime.1.out b/tests/exslt/date/datetime.1.out
index c0b4c53..273130d 100644
--- a/tests/exslt/date/datetime.1.out
+++ b/tests/exslt/date/datetime.1.out
@@ -39,12 +39,12 @@
Test Date : -0001-12-31T23:59:59-05:00
year : -1
- leap-year : false
+ leap-year : true
month-in-year : 12
month-name : December
month-abbreviation : Dec
week-in-year : 52
- day-in-year : 365
+ day-in-year : 366
day-in-month : 31
day-of-week-in-month : 5
day-in-week : 1
@@ -75,17 +75,17 @@
Test Date : -10000-12-31T23:59:59Z
year : -10000
- leap-year : true
+ leap-year : false
month-in-year : 12
month-name : December
month-abbreviation : Dec
week-in-year : 1
- day-in-year : 366
+ day-in-year : 365
day-in-month : 31
day-of-week-in-month : 5
- day-in-week : 4
- day-name : Wednesday
- day-abbreviation : Wed
+ day-in-week : 2
+ day-name : Monday
+ day-abbreviation : Mon
time : 23:59:59Z
hour-in-day : 23
minute-in-hour : 59
diff --git a/tests/exslt/date/difference.1.out b/tests/exslt/date/difference.1.out
index bcafa2b..aac2e14 100644
--- a/tests/exslt/date/difference.1.out
+++ b/tests/exslt/date/difference.1.out
@@ -14,7 +14,9 @@ result : -P366D
difference : 0002-05-05 - 0001-01
result : -P1Y4M
difference : -0002-01-05 - 0001-01-04
-result : P729D
+result : P730D
+difference : 0002 - -0001
+result : -P2Y
difference : 1970-01-01T05:04:03 - 1970-01-01T04:03:02
result : -PT1H1M1S
difference : 2000-01-01T05:00:03 - 2000-01-01T04:03:02
diff --git a/tests/exslt/date/difference.1.xml b/tests/exslt/date/difference.1.xml
index ca897c0..0e361f0 100644
--- a/tests/exslt/date/difference.1.xml
+++ b/tests/exslt/date/difference.1.xml
@@ -9,6 +9,7 @@
<date date1='0002-01-05' date2='0001-01-04'/>
<date date1='0002-05-05' date2='0001-01'/>
<date date1='-0002-01-05' date2='0001-01-04'/>
+ <date date1='0002' date2='-0001'/>
<date date1='1970-01-01T05:04:03' date2='1970-01-01T04:03:02'/>
<date date1='2000-01-01T05:00:03' date2='2000-01-01T04:03:02'/>
<date date1='2000-01-01T05:00:03' date2='1980-01-01T04:03:02'/>
diff --git a/tests/exslt/date/gyear.1.out b/tests/exslt/date/gyear.1.out
index 9a7b291..c5c517c 100644
--- a/tests/exslt/date/gyear.1.out
+++ b/tests/exslt/date/gyear.1.out
@@ -73,8 +73,8 @@
minute-in-hour : NaN
second-in-minute : NaN
- Test Date : -0004
- year : -4
+ Test Date : -0005
+ year : -5
leap-year : true
month-in-year : NaN
month-name :
diff --git a/tests/exslt/date/gyear.1.xml b/tests/exslt/date/gyear.1.xml
index 95d776b..fb1e577 100644
--- a/tests/exslt/date/gyear.1.xml
+++ b/tests/exslt/date/gyear.1.xml
@@ -5,6 +5,6 @@
<date date="3000"/>
<date date="2000"/>
<date date="9990001"/>
- <date date="-0004"/>
+ <date date="-0005"/>
</page>
diff --git a/tests/exslt/date/gyearmonth.1.out b/tests/exslt/date/gyearmonth.1.out
index 57580c7..6ffef03 100644
--- a/tests/exslt/date/gyearmonth.1.out
+++ b/tests/exslt/date/gyearmonth.1.out
@@ -73,8 +73,8 @@
minute-in-hour : NaN
second-in-minute : NaN
- Test Date : -0004-02
- year : -4
+ Test Date : -0005-02
+ year : -5
leap-year : true
month-in-year : 2
month-name : February
diff --git a/tests/exslt/date/gyearmonth.1.xml b/tests/exslt/date/gyearmonth.1.xml
index 08a7d29..3e3bdaf 100644
--- a/tests/exslt/date/gyearmonth.1.xml
+++ b/tests/exslt/date/gyearmonth.1.xml
@@ -5,6 +5,6 @@
<date date="3000-01"/>
<date date="2000-02"/>
<date date="9990001-12"/>
- <date date="-0004-02"/>
+ <date date="-0005-02"/>
</page>
diff --git a/tests/exslt/date/seconds.1.out b/tests/exslt/date/seconds.1.out
index c0eb72c..5d5e5da 100644
--- a/tests/exslt/date/seconds.1.out
+++ b/tests/exslt/date/seconds.1.out
@@ -34,7 +34,7 @@ result : 31536000
seconds : 0001-01-01T00:00:00
result : -6.21355968e+10
seconds : -0001-01-01T00:00:00
-result : -6.21671328e+10
+result : -6.21672192e+10
seconds : 1970-01-01
result : 0
seconds : 1970-01-01Z
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]