[xml] date time schema types



Daniel,

This patch adds validation of all the date, time, and duration types to
xmlschemastypes. I also added a compare function for duration
(xmlSchemaCompareDurations), however I wasn't sure how to handle
indeterminate values (i.e. P1M <> P30D). I coded the compare function to
treat indeterminate comparisons as equal (return value 0). I still need
a comparison function for all the date/time types, and that may take a
while.
I also included a crude test for duration restrictions; it could use
more test cases. I haven't tested the date/time type parsing (except for
xs:date which is currently in other tests) but since the code is mostly
from Thomas Broyer's code in libexslt, I am pretty confident it works.
Feel free to reorganize/optimize this code as you see fit.

Charlie Bozeman
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema";>
  <xsd:annotation>
    <xsd:documentation xml:lang="en">
      Testing duration data types
    </xsd:documentation>
  </xsd:annotation>
  <xsd:element name="duration">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:choice minOccurs="1" maxOccurs="unbounded">
          <xsd:element name="second1" type="xsd:duration">
            <xsd:simpleType>
              <xsd:restriction base="xsd:duration">
                <xsd:maxExclusive value="PT1S"/>
                <xsd:minExclusive value="PT0.1S"/>
              </xsd:restriction>
            </xsd:simpleType>
          </xsd:element>
          <xsd:element name="second2" type="xsd:duration">
            <xsd:simpleType>
              <xsd:restriction base="xsd:duration">
                <xsd:maxInclusive value="PT1S"/>
                <xsd:minInclusive value="PT0.1S"/>
              </xsd:restriction>
            </xsd:simpleType>
          </xsd:element>
          <xsd:element name="month1" type="xsd:duration">
            <xsd:simpleType>
              <xsd:restriction base="xsd:duration">
                <xsd:maxExclusive value="P1M"/>
                <xsd:minExclusive value="P0M"/>
              </xsd:restriction>
            </xsd:simpleType>
          </xsd:element>
          <xsd:element name="month2" type="xsd:duration">
            <xsd:simpleType>
              <xsd:restriction base="xsd:duration">
                <xsd:maxInclusive value="P1M"/>
                <xsd:minInclusive value="P0M"/>
              </xsd:restriction>
            </xsd:simpleType>
          </xsd:element>
          <xsd:element name="month3" type="MSD"/>
          <xsd:element name="year1" type="xsd:duration">
            <xsd:simpleType>
              <xsd:restriction base="xsd:duration">
                <xsd:maxExclusive value="P2Y"/>
                <xsd:minExclusive value="P1Y"/>
              </xsd:restriction>
            </xsd:simpleType>
          </xsd:element>
          <xsd:element name="year2" type="xsd:duration">
            <xsd:simpleType>
              <xsd:restriction base="xsd:duration">
                <xsd:maxInclusive value="P2Y"/>
                <xsd:minInclusive value="P1Y"/>
              </xsd:restriction>
            </xsd:simpleType>
          </xsd:element>
        </xsd:choice>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
  <xsd:simpleType name="MSD">
    <xsd:restriction base="xsd:duration">
      <xsd:minOccurs value="0"/>
      <xsd:maxExclusive value="PT24H"/>
      <xsd:minExclusive value="-PT24H"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:schema>

<?xml version="1.0"?>
<duration>
  <second1>PT0.9S</second1>
  <second2>PT0.1S</second2>
  <second2>PT0.999999S</second2>
  <month1>P0Y27D</month1>
  <month1>P27DT23H59M59S</month1>
  <month2>P0Y</month2>
  <year1>P366DT23H59M59S</year1>
  <year1>P12M</year1>
  <year2>P12M</year2>
  <month3>PT86400S</month3>
</duration>
Index: xmlschemastypes.c
===================================================================
RCS file: /cvs/gnome/gnome-xml/xmlschemastypes.c,v
retrieving revision 1.4
diff -c -r1.4 xmlschemastypes.c
*** xmlschemastypes.c   2002/04/23 07:12:16     1.4
--- xmlschemastypes.c   2002/05/03 02:19:42
***************
*** 23,28 ****
--- 23,32 ----
  #include <libxml/schemasInternals.h>
  #include <libxml/xmlschemastypes.h>
  
+ #ifdef HAVE_MATH_H
+ #include <math.h>
+ #endif
+ 
  #define DEBUG
  
  #define TODO                                                          \
***************
*** 38,43 ****
--- 42,56 ----
      XML_SCHEMAS_STRING,
      XML_SCHEMAS_NMTOKEN,
      XML_SCHEMAS_DECIMAL,
+     XML_SCHEMAS_TIME,
+     XML_SCHEMAS_GDAY,
+     XML_SCHEMAS_GMONTH,
+     XML_SCHEMAS_GMONTHDAY,
+     XML_SCHEMAS_GYEAR,
+     XML_SCHEMAS_GYEARMONTH,
+     XML_SCHEMAS_DATE,
+     XML_SCHEMAS_DATETIME,
+     XML_SCHEMAS_DURATION,
      XML_SCHEMAS_,
      XML_SCHEMAS_XXX
  } xmlSchemaValType;
***************
*** 47,52 ****
--- 60,88 ----
      100000000L, 1000000000L
  };
  
+ /* Date value */
+ typedef struct _xmlSchemaValDate xmlSchemaValDate;
+ typedef xmlSchemaValDate *xmlSchemaValDatePtr;
+ struct _xmlSchemaValDate {
+     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;
+     int                       tz_flag :1;     /* is tzo explicitely set? */
+     int                       tzo     :11;    /* -1440 <= tzo <= 1440 */
+ };
+ 
+ /* Duration value */
+ typedef struct _xmlSchemaValDuration xmlSchemaValDuration;
+ typedef xmlSchemaValDuration *xmlSchemaValDurationPtr;
+ struct _xmlSchemaValDuration {
+     long              mon;            /* mon stores years also */
+     long              day;
+     double            sec;            /* sec stores min and hour also */
+ };
+ 
  typedef struct _xmlSchemaValDecimal xmlSchemaValDecimal;
  typedef xmlSchemaValDecimal *xmlSchemaValDecimalPtr;
  struct _xmlSchemaValDecimal {
***************
*** 62,67 ****
--- 98,105 ----
      xmlSchemaValType type;
      union {
        xmlSchemaValDecimal decimal;
+         xmlSchemaValDate        date;
+         xmlSchemaValDuration    dur;
      } value;
  };
  
***************
*** 72,78 ****
--- 110,124 ----
  static xmlSchemaTypePtr xmlSchemaTypeAnyTypeDef = NULL;
  static xmlSchemaTypePtr xmlSchemaTypeAnySimpleTypeDef = NULL;
  static xmlSchemaTypePtr xmlSchemaTypeDecimalDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeDatetimeDef = NULL;
  static xmlSchemaTypePtr xmlSchemaTypeDateDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeTimeDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeGYearDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeGYearMonthDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeGDayDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeGMonthDayDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeGMonthDef = NULL;
+ static xmlSchemaTypePtr xmlSchemaTypeDurationDef = NULL;
  static xmlSchemaTypePtr xmlSchemaTypePositiveIntegerDef = NULL;
  static xmlSchemaTypePtr xmlSchemaTypeNonNegativeIntegerDef = NULL;
  static xmlSchemaTypePtr xmlSchemaTypeNmtoken = NULL;
***************
*** 118,123 ****
--- 164,177 ----
      xmlSchemaTypeAnySimpleTypeDef = xmlSchemaInitBasicType("anySimpleType");
      xmlSchemaTypeDecimalDef = xmlSchemaInitBasicType("decimal");
      xmlSchemaTypeDateDef = xmlSchemaInitBasicType("date");
+     xmlSchemaTypeDatetimeDef = xmlSchemaInitBasicType("dateTime");
+     xmlSchemaTypeTimeDef = xmlSchemaInitBasicType("time");
+     xmlSchemaTypeGYearDef = xmlSchemaInitBasicType("gYear");
+     xmlSchemaTypeGYearMonthDef = xmlSchemaInitBasicType("gYearMonth");
+     xmlSchemaTypeGMonthDef = xmlSchemaInitBasicType("gMonth");
+     xmlSchemaTypeGMonthDayDef = xmlSchemaInitBasicType("gMonthDay");
+     xmlSchemaTypeGDayDef = xmlSchemaInitBasicType("gDay");
+     xmlSchemaTypeDurationDef = xmlSchemaInitBasicType("duration");
      xmlSchemaTypePositiveIntegerDef = xmlSchemaInitBasicType("positiveInteger");
      xmlSchemaTypeNonNegativeIntegerDef =
        xmlSchemaInitBasicType("nonNegativeInteger");
***************
*** 190,196 ****
--- 244,889 ----
        return(NULL);
      return((xmlSchemaTypePtr) xmlHashLookup2(xmlSchemaTypesBank, name, ns));
  }
+ 
+ /****************************************************************
+  *                                                            *
+  *            Convenience macros and functions                *
+  *                                                            *
+  ****************************************************************/
+ 
+ #define IS_TZO_CHAR(c)                                                \
+       ((c == 0) || (c == 'Z') || (c == '+') || (c == '-'))
+ 
+ #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))
+ #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 % 4 == 0) && (y % 100 != 0)) || (y % 400 == 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 VALID_MDAY(dt)                                                \
+       (IS_LEAP(dt->year) ?                                    \
+           (dt->day <= daysInMonthLeap[dt->mon - 1]) :         \
+           (dt->day <= daysInMonth[dt->mon - 1]))
+ 
+ #define VALID_DATE(dt)                                                \
+       (VALID_YEAR(dt->year) && VALID_MONTH(dt->mon) && VALID_MDAY(dt))
+ 
+ #define VALID_TIME(dt)                                                \
+       (VALID_HOUR(dt->hour) && VALID_MIN(dt->min) &&          \
+        VALID_SEC(dt->sec) && VALID_TZO(dt->tzo))
+ 
+ #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)
+ 
+ /**
+  * _xmlSchemaParseGYear:
+  * @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.
+  *
+  * Returns 0 or the error code
+  */
+ static int
+ _xmlSchemaParseGYear (xmlSchemaValDatePtr 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')) {
+       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 (isneg)
+       dt->year = - dt->year;
+ 
+     if (!VALID_YEAR(dt->year))
+       return 2;
+ 
+     *str = cur;
+     return 0;
+ }
+ 
+ /**
+  * PARSE_2_DIGITS:
+  * @num:  the integer to fill in
+  * @cur:  an #xmlChar *
+  * @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, invalid)                     \
+       if ((cur[0] < '0') || (cur[0] > '9') ||                 \
+           (cur[1] < '0') || (cur[1] > '9'))                   \
+           invalid = 1;                                        \
+       else                                                    \
+           num = (cur[0] - '0') * 10 + (cur[1] - '0');         \
+       cur += 2;
+ 
  /**
+  * 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, 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++;                                          \
+           }                                                   \
+       }
+ 
+ /**
+  * _xmlSchemaParseGMonth:
+  * @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
+ _xmlSchemaParseGMonth (xmlSchemaValDatePtr dt, const xmlChar **str) {
+     const xmlChar *cur = *str;
+     int ret = 0;
+ 
+     PARSE_2_DIGITS(dt->mon, cur, ret);
+     if (ret != 0)
+       return ret;
+ 
+     if (!VALID_MONTH(dt->mon))
+       return 2;
+ 
+     *str = cur;
+     return 0;
+ }
+ 
+ /**
+  * _xmlSchemaParseGDay:
+  * @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
+ _xmlSchemaParseGDay (xmlSchemaValDatePtr dt, const xmlChar **str) {
+     const xmlChar *cur = *str;
+     int ret = 0;
+ 
+     PARSE_2_DIGITS(dt->day, cur, ret);
+     if (ret != 0)
+       return ret;
+ 
+     if (!VALID_DAY(dt->day))
+       return 2;
+ 
+     *str = cur;
+     return 0;
+ }
+ 
+ /**
+  * _xmlSchemaParseTime:
+  * @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
+ _xmlSchemaParseTime (xmlSchemaValDatePtr 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, 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, 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;
+     return 0;
+ }
+ 
+ /**
+  * _xmlSchemaParseTimeZone:
+  * @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
+ _xmlSchemaParseTimeZone (xmlSchemaValDatePtr dt, const xmlChar **str) {
+     const xmlChar *cur = *str;
+     int ret = 0;
+ 
+     if (str == NULL)
+       return -1;
+ 
+     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, ret);
+       if (ret != 0)
+           return ret;
+       if (!VALID_HOUR(tmp))
+           return 2;
+ 
+       if (*cur != ':')
+           return 1;
+       cur++;
+ 
+       dt->tzo = tmp * 60;
+ 
+       PARSE_2_DIGITS(tmp, cur, ret);
+       if (ret != 0)
+           return ret;
+       if (!VALID_MIN(tmp))
+           return 2;
+ 
+       dt->tzo += tmp;
+       if (isneg)
+           dt->tzo = - dt->tzo;
+ 
+       if (!VALID_TZO(dt->tzo))
+           return 2;
+ 
+       break;
+       }
+     default:
+       return 1;
+     }
+ 
+     *str = cur;
+     return 0;
+ }
+ 
+ /****************************************************************
+  *                                                            *
+  *    XML Schema Dates/Times Datatypes Handling               *
+  *                                                            *
+  ****************************************************************/
+ 
+ /**
+  * PARSE_DIGITS:
+  * @num:  the integer to fill in
+  * @cur:  an #xmlChar *
+  * @num_type: an integer flag
+  *
+  * 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.
+  */
+ #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++;                                          \
+             }
+ 
+ /**
+  * PARSE_NUM:
+  * @num:  the double to fill in
+  * @cur:  an #xmlChar *
+  * @num_type: an integer flag
+  *
+  * 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.
+  */
+ #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++;                                          \
+           }                                                   \
+       }
+ 
+ /**
+  * xmlSchemaParseDates:
+  * @type: the predefined type
+  * @dateTime:  string to analyze
+  * @val:  the return computed value
+  *
+  * Check that @dateTime conforms to the lexical space of one of the date types.
+  * if true a value is computed and returned in @val.
+  *
+  * Returns 0 if this validates, a positive error code number otherwise
+  *         and -1 in case of internal or API error.
+  */
+ static int
+ xmlSchemaParseDates (xmlSchemaTypePtr type, const xmlChar *dateTime,
+                         xmlSchemaValPtr *val) {
+     xmlSchemaValPtr dt;
+     int ret;
+     const xmlChar *cur = dateTime;
+ 
+ #define RETURN_TYPE_IF_VALID(t)                                       \
+     if (IS_TZO_CHAR(*cur)) {                                  \
+       ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur); \
+       if (ret == 0) {                                         \
+           if (*cur != 0)                                      \
+               goto error;                                     \
+           dt->type = t;                                       \
+             if (val != NULL)                                    \
+                 *val = dt;                                      \
+           return 0;                                           \
+       }                                                       \
+     }
+ 
+     if (dateTime == NULL)
+       return -1;
+ 
+     if ((*cur != '-') && (*cur < '0') && (*cur > '9'))
+       return 1;
+ 
+     dt = xmlSchemaNewValue(XML_SCHEMAS_UNKNOWN);
+     if (dt == NULL)
+       return -1;
+ 
+     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 = _xmlSchemaParseGDay(&(dt->value.date), &cur);
+           if (ret != 0)
+               goto error;
+ 
+           RETURN_TYPE_IF_VALID(XML_SCHEMAS_GDAY);
+ 
+           goto error;
+       }
+ 
+       /*
+        * it should be an xs:gMonthDay or xs:gMonth
+        */
+       ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
+       if (ret != 0)
+           goto error;
+ 
+       if (*cur != '-')
+           goto error;
+       cur++;
+ 
+       /* is it an xs:gMonth? */
+       if (*cur == '-') {
+           cur++;
+           RETURN_TYPE_IF_VALID(XML_SCHEMAS_GMONTH);
+           goto error;
+       }
+ 
+       /* it should be an xs:gMonthDay */
+       ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
+       if (ret != 0)
+           goto error;
+ 
+       RETURN_TYPE_IF_VALID(XML_SCHEMAS_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 = _xmlSchemaParseTime(&(dt->value.date), &cur);
+       if (ret == 0) {
+           /* it's an xs:time */
+           RETURN_TYPE_IF_VALID(XML_SCHEMAS_TIME);
+       }
+     }
+ 
+     /* fallback on date parsing */
+     cur = dateTime;
+ 
+     ret = _xmlSchemaParseGYear(&(dt->value.date), &cur);
+     if (ret != 0)
+       goto error;
+ 
+     /* is it an xs:gYear? */
+     RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEAR);
+ 
+     if (*cur != '-')
+       goto error;
+     cur++;
+ 
+     ret = _xmlSchemaParseGMonth(&(dt->value.date), &cur);
+     if (ret != 0)
+       goto error;
+ 
+     /* is it an xs:gYearMonth? */
+     RETURN_TYPE_IF_VALID(XML_SCHEMAS_GYEARMONTH);
+ 
+     if (*cur != '-')
+       goto error;
+     cur++;
+ 
+     ret = _xmlSchemaParseGDay(&(dt->value.date), &cur);
+     if ((ret != 0) || !VALID_DATE((&(dt->value.date))))
+       goto error;
+ 
+     /* is it an xs:date? */
+     RETURN_TYPE_IF_VALID(XML_SCHEMAS_DATE);
+ 
+     if (*cur != 'T')
+       goto error;
+     cur++;
+ 
+     /* it should be an xs:dateTime */
+     ret = _xmlSchemaParseTime(&(dt->value.date), &cur);
+     if (ret != 0)
+       goto error;
+ 
+     ret = _xmlSchemaParseTimeZone(&(dt->value.date), &cur);
+     if ((ret != 0) || (*cur != 0) || !VALID_DATETIME((&(dt->value.date))))
+       goto error;
+ 
+     dt->type = XML_SCHEMAS_DATETIME;
+ 
+     if (val != NULL)
+         *val = dt;
+ 
+     return 0;
+ 
+ error:
+     if (dt != NULL)
+       xmlSchemaFreeValue(dt);
+     return 1;
+ }
+ 
+ /**
+  * xmlSchemaParseDuration:
+  * @type: the predefined type
+  * @duration:  string to analyze
+  * @val:  the return computed value
+  *
+  * Check that @duration conforms to the lexical space of the duration type.
+  * if true a value is computed and returned in @val.
+  *
+  * Returns 0 if this validates, a positive error code number otherwise
+  *         and -1 in case of internal or API error.
+  */
+ static int
+ xmlSchemaParseDuration (xmlSchemaTypePtr type, const xmlChar *duration,
+                         xmlSchemaValPtr *val) {
+     const xmlChar  *cur = duration;
+     xmlSchemaValPtr dur;
+     int isneg = 0;
+     unsigned int seq = 0;
+ 
+     if (duration == NULL)
+       return -1;
+ 
+     if (*cur == '-') {
+         isneg = 1;
+         cur++;
+     }
+ 
+     /* duration must start with 'P' (after sign) */
+     if (*cur++ != 'P')
+       return 1;
+ 
+     dur = xmlSchemaNewValue(XML_SCHEMAS_DURATION);
+     if (dur == NULL)
+       return -1;
+ 
+     while (*cur != 0) {
+         double         num;
+         int            num_type = 0;  /* -1 = invalid, 0 = int, 1 = floating */
+         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))
+             goto error;
+ 
+         /* T designator must be present for time items */
+         if (*cur == 'T') {
+             if (seq <= 3) {
+                 seq = 3;
+                 cur++;
+             } else
+                 return 1;
+         } else if (seq == 3)
+             goto error;
+ 
+         /* parse the number portion of the item */
+         PARSE_NUM(num, cur, num_type);
+ 
+         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;
+ 
+                 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;
+                 }
+ 
+                 break;          /* exit loop */
+             }
+             /* no date designators found? */
+             if (++seq == 3)
+                 goto error;
+         }
+         cur++;
+     }
+ 
+     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;
+     }
+ 
+     if (val != NULL)
+         *val = dur;
+ 
+     return 0;
+ 
+ error:
+     if (dur != NULL)
+       xmlSchemaFreeValue(dur);
+     return 1;
+ }
+ 
+ /**
   * xmlSchemaValidatePredefinedType:
   * @type: the predefined type
   * @value: the value to check
***************
*** 262,307 ****
                *val = v;
            }
        }
-       return(0);
-     } else if (type == xmlSchemaTypeDateDef) {
-       const xmlChar *cur = value;
-       if (cur == NULL)
-           return(1);
-       if (*cur == '-')
-           cur++;
-       if ((*cur < '0') || (*cur > '9'))
-           return(1);
-       if ((*cur < '0') || (*cur > '9'))
-           return(1);
-       if ((*cur < '0') || (*cur > '9'))
-           return(1);
-       if ((*cur < '0') || (*cur > '9'))
-           return(1);
-       while ((*cur >= '0') && (*cur <= '9'))
-           cur++;
-       if (*cur != '-')
-           return(1);
-       cur++;
-       if ((*cur != '0') && (*cur != '1'))
-           return(1);
-       if ((*cur == '0') && (cur[1] == '0'))
-           return(1);
-       if ((*cur == '1') && ((cur[1] < '0') || (cur[1] > '2')))
-           return(1);
-       cur += 2;
-       if (*cur != '-')
-           return(1);
-       cur++;
-       if ((*cur < '0') || (*cur > '3'))
-           return(1);
-       if ((*cur == '0') && (cur[1] == '0'))
-           return(1);
-       if ((*cur == '3') && ((cur[1] < '0') || (cur[1] > '1')))
-           return(1);
-       cur += 2;
-       if (*cur != 0)
-           return(1);
        return(0);
      } else if (type == xmlSchemaTypePositiveIntegerDef) {
        const xmlChar *cur = value;
        unsigned long base = 0;
--- 955,972 ----
                *val = v;
            }
        }
        return(0);
+     } else if (type == xmlSchemaTypeDurationDef) {
+         return xmlSchemaParseDuration(type, value, val);
+     } else if ((type == xmlSchemaTypeDatetimeDef) ||
+                (type == xmlSchemaTypeTimeDef) ||
+                (type == xmlSchemaTypeDateDef) ||
+                (type == xmlSchemaTypeGYearDef) ||
+                (type == xmlSchemaTypeGYearMonthDef) ||
+                (type == xmlSchemaTypeGMonthDef) ||
+                (type == xmlSchemaTypeGMonthDayDef) ||
+                (type == xmlSchemaTypeGDayDef))         {
+         return xmlSchemaParseDates(type, value, val);
      } else if (type == xmlSchemaTypePositiveIntegerDef) {
        const xmlChar *cur = value;
        unsigned long base = 0;
***************
*** 417,422 ****
--- 1082,1168 ----
  }
  
  /**
+  * xmlSchemaCompareDurations:
+  * @x:  a first duration value
+  * @y:  a second duration value
+  *
+  * Compare 2 durations
+  *
+  * Returns -1 if x < y, 0 if x == y, 1 if x > y, 2 if x <> y, and -2 in
+  * case of error
+  */
+ static int
+ xmlSchemaCompareDurations(xmlSchemaValPtr x, xmlSchemaValPtr y)
+ {
+     long carry, mon, day;
+     double sec;
+     long xmon, xday, myear, lyear, minday, maxday;
+     static const long dayRange [2][12] = {
+         { 0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334, },
+         { 0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337} };
+ 
+     if ((x == NULL) || (y == NULL))
+         return NULL;
+ 
+     /* months */
+     mon = x->value.dur.mon - y->value.dur.mon;
+ 
+     /* seconds */
+     sec = x->value.dur.sec - y->value.dur.sec;
+     carry = (long)sec / SECS_PER_DAY;
+     sec -= (double)(carry * SECS_PER_DAY);
+ 
+     /* days */
+     day = x->value.dur.day - y->value.dur.day + carry;
+ 
+     /* easy test */
+     if (mon == 0) {
+         if (day == 0)
+             if (sec == 0.0)
+                 return 0;
+             else if (sec < 0.0)
+                 return -1;
+             else
+                 return 1;
+         else if (day < 0)
+             return -1;
+         else
+             return 1;
+     }
+ 
+     if (mon > 0) {
+         if ((day >= 0) && (sec >= 0.0))
+             return 1;
+         else {
+             xmon = mon;
+             xday = -day;
+         }
+     } else if ((day <= 0) && (sec <= 0.0)) {
+         return -1;
+     } else {
+         xmon = -mon;
+         xday = day;
+     }
+ 
+     myear = xmon / 12;
+     lyear = myear / 4;
+     minday = (myear * 365) + (lyear != 0 ? lyear - 1 : 0);
+     maxday = (myear * 365) + (lyear != 0 ? lyear + 1 : 0);
+         
+     xmon = xmon % 12;
+     minday += dayRange[0][xmon];
+     maxday += dayRange[1][xmon];
+ 
+     if (maxday < xday)
+         return 1;
+     else if (minday > xday)
+         return -1;
+ 
+     /* indeterminate */
+     return 0;
+ }
+ 
+ /**
   * xmlSchemaCompareValues:
   * @x:  a first value
   * @y:  a second value
***************
*** 438,443 ****
--- 1184,1194 ----
                return(xmlSchemaCompareDecimals(x, y));
            else
                return(-2);
+         case XML_SCHEMAS_DURATION:
+           if (y->type == XML_SCHEMAS_DURATION)
+                 return(xmlSchemaCompareDurations(x, y));
+             else
+                 return(-2);
        default:
            TODO
      }
***************
*** 478,483 ****
--- 1229,1264 ----
                return(-1);
            }
            if (ret == -1)
+               return(0);
+           TODO /* error code */
+           return(1);
+       case XML_SCHEMA_FACET_MAXINCLUSIVE:
+           ret = xmlSchemaCompareValues(val, facet->val);
+           if (ret == -2) {
+               TODO /* error code */
+               return(-1);
+           }
+           if ((ret == -1) || (ret == 0))
+               return(0);
+           TODO /* error code */
+           return(1);
+       case XML_SCHEMA_FACET_MINEXCLUSIVE:
+           ret = xmlSchemaCompareValues(val, facet->val);
+           if (ret == -2) {
+               TODO /* error code */
+               return(-1);
+           }
+           if (ret == 1)
+               return(0);
+           TODO /* error code */
+           return(1);
+       case XML_SCHEMA_FACET_MININCLUSIVE:
+           ret = xmlSchemaCompareValues(val, facet->val);
+           if (ret == -2) {
+               TODO /* error code */
+               return(-1);
+           }
+           if ((ret == 1) || (ret == 0))
                return(0);
            TODO /* error code */
            return(1);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]