You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2012/06/06 17:10:04 UTC

svn commit: r1346931 - in /jena/Scratch/AFS/Jena-Dev/trunk/src/dev: JENA247_XSDDatimeTzBoundary.java xsddt/ xsddt/DT.java xsddt/XSDDateTime2.java

Author: andy
Date: Wed Jun  6 15:10:03 2012
New Revision: 1346931

URL: http://svn.apache.org/viewvc?rev=1346931&view=rev
Log: (empty)

Added:
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/DT.java
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/XSDDateTime2.java
Modified:
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/JENA247_XSDDatimeTzBoundary.java

Modified: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/JENA247_XSDDatimeTzBoundary.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/JENA247_XSDDatimeTzBoundary.java?rev=1346931&r1=1346930&r2=1346931&view=diff
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/JENA247_XSDDatimeTzBoundary.java (original)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/JENA247_XSDDatimeTzBoundary.java Wed Jun  6 15:10:03 2012
@@ -24,7 +24,11 @@ import java.util.Calendar ;
 import java.util.Date ;
 import java.util.TimeZone ;
 
-import com.hp.hpl.jena.datatypes.xsd.XSDDateTime ;
+import dev.xsddt.DT ;
+import dev.xsddt.XSDDateTime2 ;
+
+import org.junit.Assert ;
+import org.junit.Test ;
 
  
 /* from
@@ -47,10 +51,43 @@ with:
  */
 
 public class JENA247_XSDDatimeTzBoundary {
+    
     public static void main(String ... argv) throws Exception
     {
         new JENA247_XSDDatimeTzBoundary().test() ;
     }
+
+    
+    
+    @Test public void jtest()
+    {
+        // From TestTypeLiterals.testDateTime
+        
+//        // dateTime
+//        Literal l1 = ResourceFactory.createTypedLiteral("1999-05-31T02:09:32Z", XSDDatatype.XSDdateTime);
+//        System.out.println(l1.getValue()) ;
+//        XSDDateTime xdt = (XSDDateTime)l1.getValue() ;
+//        
+//        //Calendar x = XSDDateTime2.convertCalendar() ;
+//        
+//        int x[] = new int[] {1999, 5, 31, 2, 9, 32, 0, 90, 0} ;
+//        XSDDateTime2 xdt2 = new XSDDateTime2(x , 15) ;
+
+        XSDDateTime2 xdt = new DT("dateTime").parseValidated("1999-05-31T02:09:32Z") ;
+        
+//        Assert.assertEquals("dateTime data type", XSDDatatype.XSDdateTime, l1.getDatatype());
+//        Assert.assertEquals("dateTime java type", XSDDateTime.class, l1.getValue().getClass());
+        Assert.assertEquals("dateTime value", 1999, xdt.getYears());
+        Assert.assertEquals("dateTime value", 5, xdt.getMonths());
+        Assert.assertEquals("dateTime value", 31, xdt.getDays());
+        Assert.assertEquals("dateTime value", 2, xdt.getHours());
+        Assert.assertEquals("dateTime value", 9, xdt.getMinutes());
+        Assert.assertEquals("dateTime value", 32, xdt.getFullSeconds());
+        //Assert.assertEquals("serialization", "1999-05-31T02:09:32Z", l1.getValue().toString());
+        Assert.assertEquals("serialization", "1999-05-31T02:09:32Z", xdt.toString());
+    }
+    
+    /// -------------------------------
     
     
     public static Date getDateFromPattern(String ts, String format, String timezoneid) throws Exception {
@@ -74,12 +111,12 @@ public class JENA247_XSDDatimeTzBoundary
 
 
 
-    //@Test -- not a test (yet)
+    //@Test -- not a test (yet)XSDDateTime2
     public void test() throws Exception {
         String[] timezonelist = {
-//            "GMT",
+            //"GMT",
             "America/New_York",
-//            "America/Chicago",
+            //"America/Chicago",
         };
 
         for (String timezoneid : timezonelist) {
@@ -115,7 +152,10 @@ public class JENA247_XSDDatimeTzBoundary
                 Calendar cal = Calendar.getInstance();
                 cal.setTimeZone(tz);
                 cal.setTime(dt);
-                XSDDateTime xdt = new XSDDateTime(cal);
+                
+                // ****** XSDDateTime2
+                
+                XSDDateTime2 xdt = new XSDDateTime2(cal);
                 int offset = tz.getOffset(dt.getTime()) /( 60 * 60 * 1000);
                 int xhr = xdt.getHours();
                 int dhr = cal.get(Calendar.HOUR_OF_DAY);
@@ -131,6 +171,8 @@ public class JENA247_XSDDatimeTzBoundary
             System.out.println();
         }
     }
+
+
 }
 
 

Added: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/DT.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/DT.java?rev=1346931&view=auto
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/DT.java (added)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/DT.java Wed Jun  6 15:10:03 2012
@@ -0,0 +1,495 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.xsddt;
+
+import com.hp.hpl.jena.datatypes.RDFDatatype;
+import com.hp.hpl.jena.datatypes.xsd.AbstractDateTime ;
+import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
+import com.hp.hpl.jena.datatypes.xsd.XSDDateTime;
+import com.hp.hpl.jena.graph.impl.LiteralLabel;
+
+/**
+ * Base class for all date/time/duration type representations.
+ * Includes support functions for parsing and comparing dates.
+ * 
+ * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
+ * @version $Revision: 1.1 $ on $Date: 2009-06-29 08:55:58 $
+ */
+public class DT extends XSDDatatype {
+
+    
+    @Override
+    public XSDDateTime2 parseValidated(String str) {
+         int len = str.length();
+         int[] date = new int[TOTAL_SIZE];
+         int[] timeZone = new int[2];
+
+         int end = indexOf (str, 0, len, 'T');
+
+         // both time and date
+         getDate(str, 0, end, date);
+         getTime(str, end+1, len, date, timeZone);
+
+         if ( date[utc]!=0 && date[utc]!='Z') {
+             AbstractDateTime.normalize(date, timeZone);
+         }
+         // XSDDateTime2************
+         return new XSDDateTime2(date, XSDDateTime.FULL_MASK);
+    }
+    
+    /**
+     * Constructor
+     */
+    public DT(String typename) {
+        super(typename);
+    }
+     
+    /**
+     * Compares two instances of values of the given datatype.
+     * This ignores lang tags and just uses the java.lang.Number 
+     * equality.
+     */
+    @Override
+    public boolean isEqual(LiteralLabel value1, LiteralLabel value2) {
+       return value1.getValue().equals(value2.getValue());
+    }
+    
+    /** Mask to indicate whether year is present */
+    public static final short YEAR_MASK = 0x1;
+    
+    /** Mask to indicate whether month is present */
+    public static final short MONTH_MASK = 0x2;
+    
+    /** Mask to indicate whether day is present */
+    public static final short DAY_MASK = 0x4;
+    
+    /** Mask to indicate whether time is present */
+    public static final short TIME_MASK = 0x8;
+    
+    /** Mask to indicate all date/time are present */
+    public static final short FULL_MASK = 0xf;
+    
+    
+//  --------------------------------------------------------------------
+//  This code is adapated from Xerces 2.6.0 AbstractDateTimeDV.    
+//  Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
+//  reserved.
+//  --------------------------------------------------------------------
+
+     //define constants
+     protected final static int CY = 0,  M = 1, D = 2, h = 3,
+     m = 4, s = 5, ms = 6, msscale=8, utc=7, hh=0, mm=1;
+        
+     //size for all objects must have the same fields:
+     //CCYY, MM, DD, h, m, s, ms + timeZone
+     protected final static int TOTAL_SIZE = 9;
+
+     //define constants to be used in assigning default values for
+     //all date/time excluding duration
+     protected final static int YEAR=2000;
+     protected final static int MONTH=01;
+     protected final static int DAY = 15;
+
+
+     /**
+      * Parses time hh:mm:ss.sss and time zone if any
+      *
+      * @param start
+      * @param end
+      * @param data
+      * @exception RuntimeException
+      */
+     protected  void getTime (String buffer, int start, int end, int[] data, int[] timeZone) throws RuntimeException{
+
+         int stop = start+2;
+
+         //get hours (hh)
+         data[h]=parseInt(buffer, start,stop);
+
+         //get minutes (mm)
+
+         if (buffer.charAt(stop++)!=':') {
+                 throw new RuntimeException("Error in parsing time zone" );
+         }
+         start = stop;
+         stop = stop+2;
+         data[m]=parseInt(buffer, start,stop);
+
+         //get seconds (ss)
+         if (buffer.charAt(stop++)!=':') {
+                 throw new RuntimeException("Error in parsing time zone" );
+         }
+         start = stop;
+         stop = stop+2;
+         data[s]=parseInt(buffer, start,stop);
+
+         if (stop == end)
+             return;
+        
+         //get miliseconds (ms)
+         start = stop;
+         int milisec = buffer.charAt(start) == '.' ? start : -1;
+
+         //find UTC sign if any
+         int sign = findUTCSign(buffer, start, end);
+
+         //parse miliseconds
+         if ( milisec != -1 ) {
+             // The end of millisecond part is between . and
+             // either the end of the UTC sign
+             start = sign < 0 ? end : sign;
+             int msEnd = start;
+             while (buffer.charAt(msEnd-1) == '0') msEnd--;
+             data[ms]=parseInt(buffer, milisec+1, msEnd);
+             data[msscale] = msEnd - milisec - 1;
+         }
+
+         //parse UTC time zone (hh:mm)
+         if ( sign>0 ) {
+             if (start != sign)
+                 throw new RuntimeException("Error in parsing time zone" );
+             getTimeZone(buffer, data, sign, end, timeZone);
+         }
+         else if (start != end) {
+             throw new RuntimeException("Error in parsing time zone" );
+         }
+     }
+
+     /**
+      * Parses date CCYY-MM-DD
+      *
+      * @param start
+      * @param end
+      * @param data
+      * @exception RuntimeException
+      */
+     protected int getDate (String buffer, int start, int end, int[] date) throws RuntimeException{
+
+         start = getYearMonth(buffer, start, end, date);
+
+         if (buffer.charAt(start++) !='-') {
+             throw new RuntimeException("CCYY-MM must be followed by '-' sign");
+         }
+         int stop = start + 2;
+         date[D]=parseInt(buffer, start, stop);
+         return stop;
+     }
+
+     /**
+      * Parses date CCYY-MM
+      *
+      * @param start
+      * @param end
+      * @param data
+      * @exception RuntimeException
+      */
+     protected int getYearMonth (String buffer, int start, int end, int[] date) throws RuntimeException{
+
+         if ( buffer.charAt(0)=='-' ) {
+             // REVISIT: date starts with preceding '-' sign
+             //          do we have to do anything with it?
+             //
+             start++;
+         }
+         int i = indexOf(buffer, start, end, '-');
+         if ( i==-1 ) throw new RuntimeException("Year separator is missing or misplaced");
+         int length = i-start;
+         if (length<4) {
+             throw new RuntimeException("Year must have 'CCYY' format");
+         }
+         else if (length > 4 && buffer.charAt(start)=='0'){
+             throw new RuntimeException("Leading zeros are required if the year value would otherwise have fewer than four digits; otherwise they are forbidden");
+         }
+         date[CY]= parseIntYear(buffer, i);
+         if (buffer.charAt(i)!='-') {
+             throw new RuntimeException("CCYY must be followed by '-' sign");
+         }
+         start = ++i;
+         i = start +2;
+         date[M]=parseInt(buffer, start, i);
+         return i; //fStart points right after the MONTH
+     }
+
+     /**
+      * Shared code from Date and YearMonth datatypes.
+      * Finds if time zone sign is present
+      *
+      * @param end
+      * @param date
+      * @exception RuntimeException
+      */
+     protected void parseTimeZone (String buffer, int start, int end, int[] date, int[] timeZone) throws RuntimeException{
+
+         //fStart points right after the date
+
+         if ( start<end ) {
+             int sign = findUTCSign(buffer, start, end);
+             if ( sign<0 ) {
+                 throw new RuntimeException ("Error in month parsing");
+             }
+             else {
+                 getTimeZone(buffer, date, sign, end, timeZone);
+             }
+         }
+     }
+
+     /**
+      * Parses time zone: 'Z' or {+,-} followed by  hh:mm
+      *
+      * @param data
+      * @param sign
+      * @exception RuntimeException
+      */
+     protected void getTimeZone (String buffer, int[] data, int sign, int end, int[] timeZone) throws RuntimeException{
+         data[utc]=buffer.charAt(sign);
+
+         if ( buffer.charAt(sign) == 'Z' ) {
+             if (end>(++sign)) {
+                 throw new RuntimeException("Error in parsing time zone");
+             }
+             return;
+         }
+         if ( sign<=(end-6) ) {
+
+             //parse [hh]
+             int stop = ++sign+2;
+             timeZone[hh]=parseInt(buffer, sign, stop);
+             if (buffer.charAt(stop++)!=':') {
+                 throw new RuntimeException("Error in parsing time zone" );
+             }
+
+             //parse [ss]
+             timeZone[mm]=parseInt(buffer, stop, stop+2);
+
+             if ( stop+2!=end ) {
+                 throw new RuntimeException("Error in parsing time zone");
+             }
+
+         }
+         else {
+             throw new RuntimeException("Error in parsing time zone");
+         }
+     }
+
+     /**
+      * Computes index of given char within StringBuffer
+      *
+      * @param start
+      * @param end
+      * @param ch     character to look for in StringBuffer
+      * @return index of ch within StringBuffer
+      */
+     protected  int indexOf (String buffer, int start, int end, char ch) {
+         for ( int i=start;i<end;i++ ) {
+             if ( buffer.charAt(i) == ch ) {
+                 return i;
+             }
+         }
+         return -1;
+     }
+
+     // check whether the character is in the range 0x30 ~ 0x39
+     public static final boolean isDigit(char ch) {
+         return ch >= '0' && ch <= '9';
+     }
+    
+     // if the character is in the range 0x30 ~ 0x39, return its int value (0~9),
+     // otherwise, return -1
+     public static final int getDigit(char ch) {
+         return isDigit(ch) ? ch - '0' : -1;
+     }
+
+
+     /**
+      * Return index of UTC char: 'Z', '+', '-'
+      *
+      * @param start
+      * @param end
+      * @return index of the UTC character that was found
+      */
+     protected int findUTCSign (String buffer, int start, int end) {
+         int c;
+         for ( int i=start;i<end;i++ ) {
+             c=buffer.charAt(i);
+             if ( c == 'Z' || c=='+' || c=='-' ) {
+                 return i;
+             }
+
+         }
+         return -1;
+     }
+
+     /**
+      * Given start and end position, parses string value
+      *
+      * @param value  string to parse
+      * @param start  Start position
+      * @param end    end position
+      * @return  return integer representation of characters
+      */
+     protected  int parseInt (String buffer, int start, int end)
+     throws NumberFormatException{
+         //REVISIT: more testing on this parsing needs to be done.
+         int radix=10;
+         int result = 0;
+         int digit=0;
+         int limit = -Integer.MAX_VALUE;
+         int multmin = limit / radix;
+         int i = start;
+         do {
+             digit = getDigit(buffer.charAt(i));
+             if ( digit < 0 ) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+             if ( result < multmin ) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+             result *= radix;
+             if ( result < limit + digit ) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+             result -= digit;
+
+         }while ( ++i < end );
+         return -result;
+     }
+
+     // parse Year differently to support negative value.
+     protected int parseIntYear (String buffer, int end){
+         int radix=10;
+         int result = 0;
+         boolean negative = false;
+         int i=0;
+         int limit;
+         int multmin;
+         int digit=0;
+
+         if (buffer.charAt(0) == '-'){
+             negative = true;
+             limit = Integer.MIN_VALUE;
+             i++;
+
+         }
+         else{
+             limit = -Integer.MAX_VALUE;
+         }
+         multmin = limit / radix;
+         while (i < end)
+         {
+             digit = getDigit(buffer.charAt(i++));
+             if (digit < 0) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+             if (result < multmin) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+             result *= radix;
+             if (result < limit + digit) throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+             result -= digit;
+         }
+
+         if (negative)
+         {
+             if (i > 1) return result;
+             else throw new NumberFormatException("'"+buffer.toString()+"' has wrong format");
+         }
+         return -result;
+
+     }
+
+     public String dateToString(int[] date) {
+         StringBuffer message = new StringBuffer(25);
+         append(message, date[CY], 4);
+         message.append('-');
+         append(message, date[M], 2);
+         message.append('-');
+         append(message, date[D], 2);
+         message.append('T');
+         append(message, date[h], 2);
+         message.append(':');
+         append(message, date[m], 2);
+         message.append(':');
+         append(message, date[s], 2);
+         message.append('.');
+         appendFractionalTime(message, date[ms], date[msscale]);
+         append(message, (char)date[utc], 0);
+         return message.toString();
+     }
+    
+     /** Append the fraction time part of a date/time vector to
+      * a string buffer.
+      */
+     public static void appendFractionalTime(StringBuffer buff, int fsec, int scale) {
+         String msString = Integer.toString(fsec);
+         int pad = scale - msString.length();
+         while (pad > 0) {
+             buff.append('0');
+             pad--;
+         }
+         int trunc = msString.length();
+         while (trunc > 0 && msString.charAt(trunc-1) == '0') trunc --;
+         buff.append(msString.substring(0, trunc));
+     }
+     
+     protected void append(StringBuffer message, int value, int nch) {
+         if (value < 0) {
+             message.append('-');
+             value = -value;
+         }
+         if (nch == 4) {
+             if (value < 10)
+                 message.append("000");
+             else if (value < 100)
+                 message.append("00");
+             else if (value < 1000)
+                 message.append("0");
+             message.append(value);
+         }
+         else if (nch == 2) {
+             if (value < 10)
+                 message.append('0');
+             message.append(value);
+         }
+         else {
+             if (value != 0)
+                 message.append((char)value);
+         }
+     }
+
+    
+//  --------------------------------------------------------------------
+//  End of code is adapated from Xerces 2.6.0 AbstractDateTimeDV.    
+//  --------------------------------------------------------------------
+     
+     /**
+     * Normalization. If the value is narrower than the current data type
+     * (e.g. value is xsd:date but the time is xsd:datetime) returns
+     * the narrower type for the literal. 
+     * If the type is narrower than the value then it may normalize
+     * the value (e.g. set the mask of an XSDDateTime)
+     * Currently only used to narrow gener XSDDateTime objects
+     * to the minimal XSD date/time type.
+     * @param value the current object value
+     * @param dt the currently set data type
+     * @return a narrower version of the datatype based on the actual value range
+      */
+     @Override
+    public RDFDatatype normalizeSubType(Object value, RDFDatatype dt) {
+         if (value instanceof XSDDateTime) {
+             if (dt.equals(XSDDatatype.XSDdateTime)) {
+                 return ((XSDDateTime)value).getNarrowedDatatype();
+             } else if (dt instanceof XSDDatatype){
+                 // We've externally narrowed the type, push this down to the date time
+                 ((XSDDateTime)value).narrowType((XSDDatatype)dt);
+             }
+         }
+         return this;
+     }
+
+}

Added: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/XSDDateTime2.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/XSDDateTime2.java?rev=1346931&view=auto
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/XSDDateTime2.java (added)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/xsddt/XSDDateTime2.java Wed Jun  6 15:10:03 2012
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.xsddt;
+
+import java.util.Calendar ;
+import java.util.GregorianCalendar ;
+import java.util.TimeZone ;
+
+import com.hp.hpl.jena.datatypes.xsd.AbstractDateTime ;
+import com.hp.hpl.jena.datatypes.xsd.IllegalDateTimeFieldException ;
+import com.hp.hpl.jena.datatypes.xsd.XSDDatatype ;
+import com.hp.hpl.jena.datatypes.xsd.impl.XSDAbstractDateTimeType ;
+
+
+/**
+ * Represent an XSD date/time value. Rather than have a separate type for each
+ * legal date/time value combination this is a combination type than does runtime
+ * checks whether a given field is legal in the current circumstances.
+ * 
+ * @author <a href="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>
+ * @version $Revision: 1.2 $ on $Date: 2009-09-25 09:58:14 $
+ */
+public class XSDDateTime2 extends AbstractDateTime {
+    /** Mask to indicate whether year is present */
+    public static final short YEAR_MASK = 0x1;
+    
+    /** Mask to indicate whether month is present */
+    public static final short MONTH_MASK = 0x2;
+    
+    /** Mask to indicate whether day is present */
+    public static final short DAY_MASK = 0x4;
+    
+    /** Mask to indicate whether time is present */
+    public static final short TIME_MASK = 0x8;
+    
+    /** Mask to indicate all date/time are present */
+    public static final short FULL_MASK = 0xf;
+    
+    // Unused /** table mapping xs type name to mask of legal values */
+    //public static final HashMap maskMap = new HashMap();
+        
+    /** Set of legal fields for the particular date/time instance */
+    protected short mask;
+    
+    /**
+     * Constructor - should only be used by the internals but public scope because
+     * the internals spread across multiple packages.
+     * 
+     * @param value the date/time value returned by the parsing
+     * @param mask bitmask defining which components are valid in this instance
+     * (e.g. dates don't have valid time fields).
+     */
+    public XSDDateTime2(Object value, int mask) {
+        super(value);
+        this.mask = (short)mask;
+    }
+    
+    
+    /**
+     * Constructor - create a full DateTime object from a java calendar instance.
+     * 
+     * @param date java calendar instance
+     */
+    public XSDDateTime2(Calendar date) {
+        super(convertCalendar(date));
+        this.mask = FULL_MASK;
+    }
+    
+    /**
+     * Return the most specific xsd type which can represent
+     * this date/time
+     */
+    public XSDDatatype getNarrowedDatatype() {
+        switch (mask) {
+        case TIME_MASK:
+            return XSDDatatype.XSDtime;
+        case MONTH_MASK:
+            return XSDDatatype.XSDgMonth;
+        case DAY_MASK:
+            return XSDDatatype.XSDgDay;
+        case YEAR_MASK:
+            return XSDDatatype.XSDgYear;
+        case MONTH_MASK | DAY_MASK:
+            return XSDDatatype.XSDgMonthDay;
+        case MONTH_MASK | YEAR_MASK:
+            return XSDDatatype.XSDgYearMonth;
+        case MONTH_MASK | YEAR_MASK | DAY_MASK:
+            return XSDDatatype.XSDdate;
+        default:
+            return XSDDatatype.XSDdateTime;
+        }
+    }
+    
+    /**
+     * Set the mask for this date/time to be that appropriate
+     * for the given XSD subtype. If the type is a subtype of XSDdateTime the 
+     * mask will be narrowed appropriately, other types will be silently ignored.
+     */
+    public void narrowType(XSDDatatype dt) {
+        if (dt.equals(XSDDatatype.XSDtime)) {
+            mask = TIME_MASK;
+        } else if (dt.equals(XSDDatatype.XSDgMonth)) {
+            mask = MONTH_MASK;
+        } else if (dt.equals(XSDDatatype.XSDgDay)) {
+            mask = DAY_MASK;
+        } else if (dt.equals(XSDDatatype.XSDgYear)) {
+            mask = YEAR_MASK;
+        } else if (dt.equals(XSDDatatype.XSDgMonthDay)) {
+            mask = MONTH_MASK | DAY_MASK;
+        } else if (dt.equals(XSDDatatype.XSDgYearMonth)) {
+            mask = YEAR_MASK | MONTH_MASK;
+        } else if (dt.equals(XSDDatatype.XSDdate)) {
+            mask = MONTH_MASK | YEAR_MASK | DAY_MASK;
+        }  
+    }
+    
+    /**
+     * Convert a java calendar object to a new int[] in the format used by XSDAbstractDateTime
+     */
+    static int[] convertCalendar(Calendar date) {
+        int[] data = new int[TOTAL_SIZE];
+//
+//        int offset = date.get(Calendar.ZONE_OFFSET) + date.get(Calendar.DST_OFFSET);
+//                                        //  Thanks to Greg Shueler for pointing out need for DST offset
+//        Calendar cal = date;
+//        if (offset != 0) {
+//            cal = (Calendar)date.clone();
+//            cal.add(Calendar.MILLISECOND, -offset);
+//            //cal.setTimeZone(TimeZone.getTimeZone("GMT"));
+//        }
+        
+        
+        Calendar cal = (Calendar)date.clone();
+        cal.get(Calendar.ZONE_OFFSET) ;
+        cal.setTimeZone(TimeZone.getTimeZone("GMT"));   // Rebase to GMT.
+        
+        data[AbstractDateTime.CY] = cal.get(Calendar.YEAR);
+        data[AbstractDateTime.M] = cal.get(Calendar.MONTH) + 1;
+        data[AbstractDateTime.D] = cal.get(Calendar.DAY_OF_MONTH);
+        data[AbstractDateTime.h] = cal.get(Calendar.HOUR_OF_DAY);
+        data[AbstractDateTime.m] = cal.get(Calendar.MINUTE);
+        data[AbstractDateTime.s] = cal.get(Calendar.SECOND);
+        int ms = cal.get(Calendar.MILLISECOND);
+        data[AbstractDateTime.ms] = ms;
+        data[AbstractDateTime.msscale] = (ms == 0) ? 0 : 3;
+        data[AbstractDateTime.utc] = 'Z';
+        return data;
+    }
+
+    /**
+     * Return the date time as a java Calendar object. 
+     * If the timezone has been specified then the object is normalized to GMT.
+     * If the zone has not been specified then we use the default timezone.
+     * 
+     * @throws IllegalDateTimeFieldException if this is not a full date + time
+     */
+    public Calendar asCalendar () throws IllegalDateTimeFieldException {
+        TimeZone tz = data[utc] == 'Z' ? TimeZone.getTimeZone("GMT") : TimeZone.getDefault();
+        Calendar calendar = new GregorianCalendar(tz);
+        calendar.set(data[CY], data[M] - 1, data[D], data[h], data[m], data[s]);
+        calendar.set(Calendar.MILLISECOND, (int)Math.round(1000.0 * fractionalSeconds));
+        // was this to work around problems with some Linux JDKs
+        // calendar.set(Calendar.MILLISECOND, 0);
+        return calendar;
+    }
+    
+    /**
+     * Return the number of years in the dateTime.
+     * @throws IllegalDateTimeFieldException if there is no legal year component
+     */
+    public int getYears() throws IllegalDateTimeFieldException {
+        if ((mask & YEAR_MASK) == 0) throw new IllegalDateTimeFieldException("Year not available");
+        return data[CY];
+    }
+    
+    /**
+     * Return the month in the dateTime, this is in ISO8601 format so january = 1
+     * @throws IllegalDateTimeFieldException if there is no legal month component
+     */
+    public int getMonths() throws IllegalDateTimeFieldException {
+        if ((mask & MONTH_MASK) == 0) throw new IllegalDateTimeFieldException("Month not available");
+        return data[M];
+    }
+    
+    /**
+     * Return the number of years in the dateTime
+     * @throws IllegalDateTimeFieldException if there is no legal day component
+     */
+    public int getDays() throws IllegalDateTimeFieldException {
+        if ((mask & DAY_MASK) == 0) throw new IllegalDateTimeFieldException("Day not available");
+        return data[D];
+    }
+    
+    /**
+     * Return the number of hours in the dateTime
+     * @throws IllegalDateTimeFieldException if there is no legal time component
+     */
+    public int getHours() throws IllegalDateTimeFieldException {
+        if ((mask & TIME_MASK) == 0) throw new IllegalDateTimeFieldException("Time not available");
+        return data[h];
+    }
+    
+    /**
+     * Return the number of minutes in the dateTime
+     * @throws IllegalDateTimeFieldException if there is no legal time component
+     */
+    public int getMinutes() throws IllegalDateTimeFieldException {
+        if ((mask & TIME_MASK) == 0) throw new IllegalDateTimeFieldException("Time not available");
+        return data[m];
+    }
+    
+    /**
+     * Return the number of full seconds in the dateTime
+     * @throws IllegalDateTimeFieldException if there is no legal time component
+     */
+    public int getFullSeconds() throws IllegalDateTimeFieldException {
+        if ((mask & TIME_MASK) == 0) throw new IllegalDateTimeFieldException("Time not available");
+        return data[s];
+    }
+    
+    /**
+     * Return the number of seconds in the dateTime, including fractional part
+     * @throws IllegalDateTimeFieldException if there is no legal time component
+     */
+    public double getSeconds() throws IllegalDateTimeFieldException {
+        if ((mask & TIME_MASK) == 0) throw new IllegalDateTimeFieldException("Time not available");
+        return data[s] + fractionalSeconds;
+    }
+    
+    /**
+     * Return the time component of the dateTime - i.e. just the hours/mins/seconds,
+     * and returns the values in seconds.
+     * @throws IllegalDateTimeFieldException if there is no legal time component
+     */
+    public double getTimePart() throws IllegalDateTimeFieldException {
+        if ((mask & TIME_MASK) == 0) throw new IllegalDateTimeFieldException("Time not available");
+        return ((data[h]) * 60l + data[m]) * 60l + getSeconds();
+    }
+    
+    /**
+     * Return legal serialized form.
+     */
+    @Override
+    public String toString() {
+        StringBuffer buff = new StringBuffer();
+        if ((mask & YEAR_MASK) != 0) {
+            buff.append(data[CY]);
+        } else {
+            buff.append("-");
+        }
+        if ((mask & (MONTH_MASK | DAY_MASK)) != 0) {
+            buff.append("-");
+            if ((mask & MONTH_MASK) != 0) {
+                if (data[M] <= 9) buff.append("0");
+                buff.append(data[M]);
+            } else {
+                buff.append("-");
+            }
+            if ((mask & DAY_MASK) != 0) {
+                if (mask != DAY_MASK) buff.append("-");
+                if (data[D] <= 9) buff.append("0");
+                buff.append(data[D]);
+            }
+        }
+        if ((mask & TIME_MASK) != 0 ) {
+            buff.append("T");
+            buff.append(timeLexicalForm());
+        }
+
+        if ( data[utc] != 0 )
+            buff.append("Z");
+
+        return buff.toString();
+    }
+    
+    /**
+     * Return the lexical form of the time component.
+     */
+    public String timeLexicalForm() {
+        StringBuffer buff = new StringBuffer();
+        if(data[h]<10) buff.append("0");
+        buff.append(data[h]);
+
+        buff.append(":");
+        if(data[m]<10) buff.append("0");
+        buff.append(data[m]);
+
+        buff.append(":");
+        if(data[s]<10) buff.append("0");
+        buff.append(data[s]);
+
+        if (data[ms] != 0) {
+            buff.append(".");
+            XSDAbstractDateTimeType.appendFractionalTime(buff, data[ms], data[msscale]);
+        }
+        return buff.toString();
+    }
+    
+}