stevedlawrence closed pull request #157: Add support for type aware checking in 
the TDML runner
URL: https://github.com/apache/incubator-daffodil/pull/157
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
 
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
index bef2ea3ce..fee4c517e 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDateTime.scala
@@ -17,29 +17,32 @@
 
 package org.apache.daffodil.grammar.primitives
 
-import java.text.ParsePosition
 import com.ibm.icu.text.SimpleDateFormat
 import com.ibm.icu.util.Calendar
+import com.ibm.icu.util.Calendar
+import com.ibm.icu.util.TimeZone
 import com.ibm.icu.util.TimeZone
 import com.ibm.icu.util.ULocale
+
+import java.text.ParsePosition
+
+import scala.Boolean
+
+import org.apache.daffodil.calendar.TextCalendarConstants
 import org.apache.daffodil.dsom.ElementBase
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.schema.annotation.props.gen.CalendarCheckPolicy
-import org.apache.daffodil.schema.annotation.props.gen.CalendarFirstDayOfWeek
-import org.apache.daffodil.schema.annotation.props.gen.CalendarPatternKind
-import 
org.apache.daffodil.processors.unparsers.ConvertBinaryCalendarSecMilliUnparser
-import org.apache.daffodil.processors.unparsers.ConvertTextCalendarUnparser
-import com.ibm.icu.util.Calendar
-import com.ibm.icu.util.TimeZone
 import org.apache.daffodil.processors.CalendarEv
 import org.apache.daffodil.processors.CalendarLanguageEv
 import 
org.apache.daffodil.processors.parsers.ConvertBinaryCalendarSecMilliParser
 import org.apache.daffodil.processors.parsers.ConvertTextCalendarParser
-import org.apache.daffodil.processors.parsers.TextCalendarConstants
+import 
org.apache.daffodil.processors.unparsers.ConvertBinaryCalendarSecMilliUnparser
+import org.apache.daffodil.processors.unparsers.ConvertTextCalendarUnparser
 import org.apache.daffodil.schema.annotation.props.gen.BinaryCalendarRep
+import org.apache.daffodil.schema.annotation.props.gen.CalendarCheckPolicy
+import org.apache.daffodil.schema.annotation.props.gen.CalendarFirstDayOfWeek
+import org.apache.daffodil.schema.annotation.props.gen.CalendarPatternKind
 import org.apache.daffodil.schema.annotation.props.gen.Representation
-import scala.Boolean
 
 abstract class ConvertCalendarPrimBase(e: ElementBase, guard: Boolean)
   extends Terminal(e, guard) {
diff --git 
a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala 
b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
index 11c47c32a..c7994d40d 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
@@ -50,15 +50,9 @@ object TestUtils {
 
   /**
    * Compares two XML Elements, after having (optionally) stripped off all 
attributes.
-   *
-   * TODO: we might start using xsi:type attributes at some point. If so fix 
this to
-   * save that attribute.
-   *
-   * NOTE: Has Side Effects: strips off attributes
    */
-  def assertEqualsXMLElements(expected: Node, actual: Node, stripAttributes: 
Boolean = true) {
-    val (exp, act) = if (!stripAttributes) (expected, actual) else 
(XMLUtils.removeAttributes(expected), XMLUtils.removeAttributes(actual))
-    XMLUtils.compareAndReport(Utility.trim(exp), Utility.trim(act))
+  def assertEqualsXMLElements(expected: Node, actual: Node) {
+    XMLUtils.compareAndReport(expected, actual)
   }
 
   /**
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
 b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
similarity index 66%
rename from 
daffodil-runtime1/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
rename to 
daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
index 6c5743eed..9af33158c 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
+++ 
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendar.scala
@@ -18,13 +18,12 @@
 package org.apache.daffodil.calendar
 
 import com.ibm.icu.text.SimpleDateFormat
-import com.ibm.icu.util.Calendar;
-import com.ibm.icu.util.TimeZone;
+import com.ibm.icu.util.Calendar
+import com.ibm.icu.util.TimeZone
 
 import java.math.{ BigDecimal => JBigDecimal }
 
 import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.processors.parsers.TextCalendarConstants
 
 object DFDLCalendarOrder extends Enumeration {
   type DFDLCalendarOrder = Value
@@ -96,20 +95,26 @@ trait OrderedCalendar { self: DFDLCalendar =>
       else if (pHasTZ && !qHasTZ) {
         val qPlus = qPrime.getDateTimePlusFourteenHours
 
-        if (orderCompareFields(pPrime, qPlus) == 
DFDLCalendarOrder.P_LESS_THAN_Q) { DFDLCalendarOrder.P_LESS_THAN_Q }
+        if (orderCompareFields(pPrime, qPlus) == 
DFDLCalendarOrder.P_LESS_THAN_Q)
+          DFDLCalendarOrder.P_LESS_THAN_Q
         else {
           val qMinus = q.getDateTimeMinusFourteenHours
-          if (orderCompareFields(pPrime, qMinus) == 
DFDLCalendarOrder.P_GREATER_THAN_Q) { DFDLCalendarOrder.P_GREATER_THAN_Q }
-          else { DFDLCalendarOrder.P_NOT_EQUAL_Q }
+          if (orderCompareFields(pPrime, qMinus) == 
DFDLCalendarOrder.P_GREATER_THAN_Q)
+            DFDLCalendarOrder.P_GREATER_THAN_Q
+          else
+            DFDLCalendarOrder.P_NOT_EQUAL_Q
         }
       } else if (!pHasTZ && qHasTZ) {
         val pMinus = pPrime.getDateTimeMinusFourteenHours
 
-        if (orderCompareFields(pMinus, qPrime) == 
DFDLCalendarOrder.P_LESS_THAN_Q) { DFDLCalendarOrder.P_LESS_THAN_Q }
+        if (orderCompareFields(pMinus, qPrime) == 
DFDLCalendarOrder.P_LESS_THAN_Q)
+          DFDLCalendarOrder.P_LESS_THAN_Q
         else {
           val pPlus = pPrime.getDateTimePlusFourteenHours
-          if (orderCompareFields(pPlus, qPrime) == 
DFDLCalendarOrder.P_GREATER_THAN_Q) { DFDLCalendarOrder.P_GREATER_THAN_Q }
-          else { DFDLCalendarOrder.P_NOT_EQUAL_Q }
+          if (orderCompareFields(pPlus, qPrime) == 
DFDLCalendarOrder.P_GREATER_THAN_Q)
+            DFDLCalendarOrder.P_GREATER_THAN_Q
+          else
+            DFDLCalendarOrder.P_NOT_EQUAL_Q
         }
       } else { Assert.impossibleCase }
     }
@@ -133,14 +138,14 @@ trait OrderedCalendar { self: DFDLCalendar =>
     for (i <- 0 until length) {
       val field = fieldsForComparison(i)
 
-      val hasPSubI = p.isFieldSet(field)
-      val hasQSubI = q.isFieldSet(field)
+      val hasPSubI = p.calendar.isSet(field)
+      val hasQSubI = q.calendar.isSet(field)
 
       if (!hasPSubI && !hasQSubI) { /* continue */ }
       else if (hasPSubI ^ hasQSubI) return DFDLCalendarOrder.P_NOT_EQUAL_Q
       else {
-        val pSubI = p.getField(field)
-        val qSubI = q.getField(field)
+        val pSubI = p.calendar.get(field)
+        val qSubI = q.calendar.get(field)
 
         if (pSubI < qSubI) { return DFDLCalendarOrder.P_LESS_THAN_Q }
         else if (pSubI > qSubI) { return DFDLCalendarOrder.P_GREATER_THAN_Q }
@@ -152,31 +157,23 @@ trait OrderedCalendar { self: DFDLCalendar =>
 }
 
 trait ToDateTimeMixin { self: DFDLCalendar =>
-  def toDateTime(): DFDLDateTime = new 
DFDLDateTime(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
+  def toDateTime(): DFDLDateTime = 
DFDLDateTime(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
 }
 
 trait ToTimeMixin { self: DFDLCalendar =>
-  def toTime(): DFDLTime = new 
DFDLTime(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
+  def toTime(): DFDLTime = DFDLTime(calendar.clone().asInstanceOf[Calendar], 
self.hasTimeZone)
 }
 
 trait ToDateMixin { self: DFDLCalendar =>
-  def toDate(): DFDLDate = new 
DFDLDate(calendar.clone().asInstanceOf[Calendar], self.hasTimeZone)
+  def toDate(): DFDLDate = DFDLDate(calendar.clone().asInstanceOf[Calendar], 
self.hasTimeZone)
 }
 
-case class DFDLDate(calendar: Calendar, parsedTZ: Boolean)
-  extends DFDLCalendar(parsedTZ: Boolean)
-  with ToDateTimeMixin with ToDateMixin {
+case class DFDLDate(calendar: Calendar, override val hasTimeZone: Boolean)
+  extends DFDLCalendar
+  with ToDateTimeMixin
+  with ToDateMixin {
 
-  /**
-   * This apply method supplies a way to just specify 'no time zone' when
-   * one was not expected.  This just so happens to be the same as GMT.
-   */
-  def apply(cal: Calendar, expectsTZ: Boolean) = {
-    if (!expectsTZ) cal.setTimeZone(TimeZone.UNKNOWN_ZONE)
-    new DFDLDate(cal, expectsTZ)
-  }
-
-  @transient override lazy val tlFormatter = if (this.hasTimeZone) 
TextCalendarConstants.tlDateInfosetFormatter else 
TextCalendarConstants.tlDateNoTZInfosetFormatter
+  override def toString(): String = DFDLDateConversion.toXMLString(this)
 
   override def equals(other: Any): Boolean = other match {
     case that: DFDLDate => this.toDateTimeWithReference equals 
that.toDateTimeWithReference
@@ -205,31 +202,16 @@ case class DFDLDate(calendar: Calendar, parsedTZ: Boolean)
     dateCal.clear(Calendar.HOUR_OF_DAY)
     dateCal.clear(Calendar.MINUTE)
     dateCal.clear(Calendar.SECOND)
-    new DFDLDateTime(dateCal, parsedTZ)
+    DFDLDateTime(dateCal, hasTimeZone)
   }
 
 }
 
-case class DFDLTime(calendar: Calendar, parsedTZ: Boolean)
-  extends DFDLCalendar(parsedTZ)
+case class DFDLTime(calendar: Calendar, override val hasTimeZone: Boolean)
+  extends DFDLCalendar
   with ToTimeMixin {
 
-  /**
-   * This apply method supplies a way to just specify 'no time zone' when
-   * one was not expected.  This just so happens to be the same as GMT.
-   */
-  def apply(cal: Calendar, expectsTZ: Boolean) = {
-    if (!expectsTZ) cal.setTimeZone(TimeZone.UNKNOWN_ZONE)
-    new DFDLTime(cal, expectsTZ)
-  }
-
-  @transient override lazy val tlFormatter = (this.hasTimeZone, 
this.hasFractionalSeconds) match {
-    case (true, true) => TextCalendarConstants.tlTimeInfosetFormatter
-    case (false, true) => TextCalendarConstants.tlTimeNoTZInfosetFormatter
-    case (true, false) => 
TextCalendarConstants.tlTimeNoFractSecInfosetFormatter
-    case (false, false) => 
TextCalendarConstants.tlTimeNoTZNoFractSecInfosetFormatter
-  }
-
+  override def toString(): String = DFDLTimeConversion.toXMLString(this)
 
   override def equals(other: Any): Boolean = other match {
     case that: DFDLTime => this.toDateTimeWithReference equals 
that.toDateTimeWithReference
@@ -261,30 +243,18 @@ case class DFDLTime(calendar: Calendar, parsedTZ: Boolean)
     timeCal.clear(Calendar.EXTENDED_YEAR)
     timeCal.clear(Calendar.MONTH)
     timeCal.clear(Calendar.DAY_OF_MONTH)
-    new DFDLDateTime(timeCal, parsedTZ)
+    DFDLDateTime(timeCal, hasTimeZone)
   }
 
 }
 
-case class DFDLDateTime(calendar: Calendar, parsedTZ: Boolean)
-  extends DFDLCalendar(parsedTZ)
-  with ToDateTimeMixin with ToDateMixin with ToTimeMixin {
+case class DFDLDateTime(calendar: Calendar, override val hasTimeZone: Boolean)
+  extends DFDLCalendar
+  with ToDateTimeMixin
+  with ToDateMixin
+  with ToTimeMixin {
 
-  /**
-   * This apply method supplies a way to just specify 'no time zone' when
-   * one was not expected.  This just so happens to be the same as GMT.
-   */
-  def apply(cal: Calendar, expectsTZ: Boolean) = {
-    if (!expectsTZ) cal.setTimeZone(TimeZone.UNKNOWN_ZONE)
-    new DFDLDateTime(cal, expectsTZ)
-  }
-
-  @transient override lazy val tlFormatter = (this.hasTimeZone, 
this.hasFractionalSeconds) match {
-    case (true, true) => TextCalendarConstants.tlDateTimeInfosetFormatter
-    case (false, true) => TextCalendarConstants.tlDateTimeNoTZInfosetFormatter
-    case (true, false) => 
TextCalendarConstants.tlDateTimeNoFractSecInfosetFormatter
-    case (false, false) => 
TextCalendarConstants.tlDateTimeNoTZNoFractSecInfosetFormatter
-  }
+  override def toString(): String = DFDLDateTimeConversion.toXMLString(this)
 
   override def equals(other: Any) = other match {
     case that: DFDLDateTime => dateTimeEqual(this, that)
@@ -303,14 +273,14 @@ case class DFDLDateTime(calendar: Calendar, parsedTZ: 
Boolean)
   def getDateTimePlusFourteenHours: DFDLDateTime = {
     Assert.invariant(!hasTimeZone)
     val adjustedCal = adjustTimeZone(normalizedCalendar, 14, 0)
-    val dt = new DFDLDateTime(adjustedCal, parsedTZ)
+    val dt = DFDLDateTime(adjustedCal, hasTimeZone)
     dt
   }
 
   def getDateTimeMinusFourteenHours: DFDLDateTime = {
     Assert.invariant(!hasTimeZone)
     val adjustedCal = adjustTimeZone(normalizedCalendar, -14, 0)
-    val dt = new DFDLDateTime(adjustedCal, parsedTZ)
+    val dt = DFDLDateTime(adjustedCal, hasTimeZone)
     dt
   }
 
@@ -349,7 +319,7 @@ case class DFDLDateTime(calendar: Calendar, parsedTZ: 
Boolean)
   }
 
   def getNormalizedCalendar(): DFDLDateTime = {
-    new DFDLDateTime(normalizedCalendar.clone().asInstanceOf[Calendar], 
parsedTZ)
+    DFDLDateTime(normalizedCalendar.clone().asInstanceOf[Calendar], 
hasTimeZone)
   }
 
   /**
@@ -400,77 +370,33 @@ case class DFDLDateTime(calendar: Calendar, parsedTZ: 
Boolean)
   }
 }
 
-abstract class DFDLCalendar(containsTZ: Boolean)
+/**
+ * Abstract class for DFDL Date/Time/DateTime. Wrapper around ICU calendar.
+ *
+ * The main reason this is neeed  is there does not appear to really be a
+ * concept of 'no time zone', so a hasTimeZone function must be implemented by
+ * implementors specifying whether or not the calendar has a timezone.
+ *
+ * On a related note, it does appear that we can specify an 'unknown' time zone
+ * using TimeZone.UNKNOWN_ZONE, since this behaves like the GMT/UTC time zone.
+ *
+ * To be clear, when no time zone is present (or expected) we set the calendar
+ * time zone to TimeZone.UNKNOWN_ZONE and carry around the hasTimeZOne flag.
+ * This avoids accessing calendar.getZone and comparing against
+ * TimeZone.UNKNOWN_ZONE, but allows the calendar object to somewhat represent
+ * 'no time zone'.
+ */
+abstract class DFDLCalendar
   extends OrderedCalendar with Serializable {
 
-  /* Print formats */
-  final val dateTimeFormatNoTZ: String = "uuuu-MM-dd'T'HH:mm:ss.SSSSSS"
-  final val dateFormatNoTZ: String = "uuuu-MM-dd"
-  final val timeFormatNoTZ: String = "HH:mm:ss.SSSSSS"
-  final val dateTimeFormat: String = "uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx"
-  final val dateFormat: String = "uuuu-MM-ddxxxxx"
-  final val timeFormat: String = "HH:mm:ss.SSSSSSxxxxx"
-  final val tzFormat: String = "xxx" // -08:00 The ISO8601 extended format 
with hours and minutes fields.
+  // Ensure the calendar does not have a timezone set if the hasTimeZone flag
+  // has not been set
+  if (!hasTimeZone) calendar.setTimeZone(TimeZone.UNKNOWN_ZONE)
 
   def calendar: Calendar
-  def tlFormatter: ThreadLocal[SimpleDateFormat]
-
-  def getField(fieldIndex: Int): Int = calendar.get(fieldIndex)
-  def isFieldSet(fieldIndex: Int): Boolean = calendar.isSet(fieldIndex)
-  def hasTimeZone: Boolean = {
-    // There does not appear to really be a concept of 'no time zone'
-    // so we use a regex or other means of elimination to determine
-    // if a time zone was expected or parsed.  This is passed as a flag
-    // to the DFDLCalendar object.
-    //
-    // On a side note, it does appear that we can specify an 'unknown'
-    // time zone using TimeZone.UNKNOWN_ZONE.  However, this behaves like
-    // the GMT/UTC time zone.
-    //
-    // To be clear, when no time zone is present (or expected) we set
-    // the calendar time zone to TimeZone.UNKNOWN_ZONE and carry around
-    // this flag.  This avoids accessing calendar.getZone and comparing
-    // against TimeZone.UNKNOWN_ZONE, but allows the calendar object
-    // to somewhat represent 'no time zone'.
-    //
-    containsTZ
-  }
 
-  def hasFractionalSeconds: Boolean = (calendar.isSet(Calendar.MILLISECOND) && 
(calendar.get(Calendar.MILLISECOND) != 0))
+  def hasTimeZone: Boolean
 
   final def toJBigDecimal: JBigDecimal = new 
JBigDecimal(calendar.getTimeInMillis())
-
-  /**
-   * Returns the TimeZone in the format of "+00:00"
-   */
-  def getTimeZoneString: String = {
-    val formatter = TextCalendarConstants.tlTzInfosetFormatter.get
-    val formattedString = try {
-      formatter.format(calendar)
-    } catch {
-      case ex: java.lang.IllegalArgumentException =>
-        throw new java.lang.IllegalArgumentException("Calendar content failed 
to match the format '%s' due to %s".format(formatter.toPattern, 
ex.getMessage()))
-    }
-    formattedString
-  }
-
-  /**
-   * Returns the ICU Calendar object.
-   *
-   * We represent 'No time zone' by TimeZone.UNKNOWN_ZONE
-   */
-  def getCalendar() = calendar
-  override def toString(): String = {
-    val formatter: SimpleDateFormat = tlFormatter.get
-    formatter.setCalendar(calendar)
-
-    val formattedString = try {
-      formatter.format(calendar)
-    } catch {
-      case ex: java.lang.IllegalArgumentException =>
-        throw new java.lang.IllegalArgumentException("Calendar content failed 
to match the format '%s' due to %s".format(formatter.toPattern, 
ex.getMessage()))
-    }
-    formattedString
-  }
-
 }
+
diff --git 
a/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendarConversion.scala
 
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendarConversion.scala
new file mode 100644
index 000000000..0c4ecda5b
--- /dev/null
+++ 
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/DFDLCalendarConversion.scala
@@ -0,0 +1,247 @@
+/*
+ * 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 org.apache.daffodil.calendar
+
+import com.ibm.icu.util.Calendar
+import com.ibm.icu.text.SimpleDateFormat
+import com.ibm.icu.util.TimeZone
+
+import java.text.ParseException
+import java.text.ParsePosition
+
+/**
+ * Wrapper arounda SimpleDateFormat and a flag that determines if it expects to
+ * parse a timezone or not
+ */
+case class DFDLCalendarFormat private (format: SimpleDateFormat, 
expectsTimezone: Boolean)
+
+object DFDLCalendarFormat {
+  def apply(pattern: String, expectsTimezone: Boolean): DFDLCalendarFormat = {
+    new DFDLCalendarFormat(InfosetSimpleDateFormat(pattern), expectsTimezone)
+  }
+}
+
+object InfosetSimpleDateFormat {
+  def apply(pattern: String): SimpleDateFormat = {
+    val sdf = new SimpleDateFormat(pattern)
+    sdf.setLenient(false)
+    sdf
+  }
+}
+
+object DFDLCalendarConversion {
+
+  @inline
+  private def pad2(i: Int) = {
+    val istr = i.toString
+    if (i >= 10) istr
+    else "0" + istr
+  }
+
+  @inline
+  private def pad3(i: Int) = {
+    val istr = i.toString
+    if (i >= 100) istr
+    else if (i >= 10) "0" + istr
+    else "00" + istr
+  }
+
+  @inline
+  private def pad4(i: Int) = {
+    val istr = i.toString
+    if (i >= 1000) istr
+    else if (i >= 100) "0" + istr
+    else if (i >= 10) "00" + istr
+    else "000" + istr
+  }
+
+  def datePartToXMLString(dfdlcal: DFDLCalendar): String = {
+    val calendar = dfdlcal.calendar
+    val y = calendar.get(Calendar.EXTENDED_YEAR)
+    val m = calendar.get(Calendar.MONTH) + 1
+    val d = calendar.get(Calendar.DAY_OF_MONTH)
+
+    val ysign = if (y >= 0) "" else "-"
+
+    ysign + pad4(Math.abs(y)) + "-" + pad2(m) + "-" + pad2(d)
+  }
+
+  def timePartToXMLString(dfdlcal: DFDLCalendar): String = {
+    val calendar = dfdlcal.calendar
+    val h = calendar.get(Calendar.HOUR_OF_DAY)
+    val m = calendar.get(Calendar.MINUTE)
+    val s = calendar.get(Calendar.SECOND)
+    val u = calendar.get(Calendar.MILLISECOND)
+
+    pad2(h) + ":" + pad2(m) + ":" + pad2(s) + (if (u != 0) "." + pad3(u) + 
"000" else "")
+  }
+
+  def timeZonePartToXMLString(dfdlcal: DFDLCalendar): String = {
+    if (!dfdlcal.hasTimeZone) {
+      ""
+    } else {
+      // This will return +00:00 for GMT. We may want to consider returning 'Z'
+      // instead when offsetInMils is zero. The DFDL specification is unclear
+      // on whether +00:00 or Z is expected. Note that the specification is
+      // clear that the dfdl:timeZoneFrom* functions (which are implemented
+      // using this function) should return +00:00 and not Z So if this
+      // function is changed to return a 'Z', it must be made configurable.
+      val tz = dfdlcal.calendar.getTimeZone
+      val offsetInMils = tz.getRawOffset
+      val offsetInMins = Math.abs(offsetInMils / (1000 * 60))
+      val h = offsetInMins / 60
+      val m = offsetInMins % 60
+      val s = if (offsetInMils >= 0) "+" else "-"
+
+      s + pad2(h) + ":" + pad2(m)
+    }
+  }
+}
+
+trait DFDLCalendarConversion {
+
+  val calendarType: String
+
+  protected def fromXMLFormats = new ThreadLocal[Seq[DFDLCalendarFormat]]
+
+  /**
+   * Attempts to parse a given date/time string with multiple allowable
+   * formats. If the string is completely parsed with one of the formats, it
+   * returns the resulting Calendar of the parse and the DFDLCalendarFormat
+   * that led to the successful match.
+   */
+  protected def parseFromXMLString(string: String): (Calendar, 
DFDLCalendarFormat) = {
+    // Create strict calendar, initialize with no time zone to ensure timezone
+    // information only comes from the data if it exists
+    val calendar = Calendar.getInstance(TimeZone.UNKNOWN_ZONE)
+    calendar.setLenient(false)
+
+    val successfulFormat = fromXMLFormats.get.find { calendarFormat =>
+
+      val pos = new ParsePosition(0)
+      calendar.clear()
+      calendarFormat.format.parse(string, calendar, pos)
+
+      try {
+        calendar.getTime()
+       
+        if (pos.getIndex() == 0 || pos.getErrorIndex() != -1 || pos.getIndex() 
!= string.length()) {
+          false
+        } else {
+          true
+        }
+      } catch {
+        case _: IllegalArgumentException => {
+          // thrown by getTime() when parsed data is not strictly correct
+          false
+        }
+      }
+    }
+
+    if (successfulFormat.isEmpty) {
+      throw new IllegalArgumentException(
+        """Failed to parse "%s" to an %s""".format(string, calendarType))
+    }
+
+    (calendar, successfulFormat.get)
+  }
+}
+
+object DFDLDateTimeConversion extends DFDLCalendarConversion {
+
+  val calendarType = "xs:dateTime"
+
+  @transient
+  override protected lazy val fromXMLFormats = new 
ThreadLocal[Seq[DFDLCalendarFormat]] {
+    override def initialValue = {
+      Seq(
+        DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx", true),
+        DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSS", false),
+        DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ssxxxxx", true),
+        DFDLCalendarFormat("uuuu-MM-dd'T'HH:mm:ss", false),
+        DFDLCalendarFormat("uuuu-MM-ddxxxxx", true),
+        DFDLCalendarFormat("uuuu-MM-dd", false)
+      )
+    }
+  }
+
+  def fromXMLString(string: String): DFDLDateTime = {
+    val (calendar, format) = parseFromXMLString(string)
+    DFDLDateTime(calendar, format.expectsTimezone)
+  }
+
+  def toXMLString(dt: DFDLDateTime): String = {
+    DFDLCalendarConversion.datePartToXMLString(dt) +
+      "T" +
+      DFDLCalendarConversion.timePartToXMLString(dt) +
+      DFDLCalendarConversion.timeZonePartToXMLString(dt)
+  }
+}
+
+object DFDLDateConversion extends DFDLCalendarConversion {
+
+  val calendarType = "xs:date"
+
+  @transient
+  override protected lazy val fromXMLFormats = new 
ThreadLocal[Seq[DFDLCalendarFormat]] {
+    override def initialValue = {
+      Seq(
+        DFDLCalendarFormat("uuuu-MM-ddxxxxx", true),
+        DFDLCalendarFormat("uuuu-MM-dd", false)
+      )
+    }
+  }
+
+  def fromXMLString(string: String): DFDLDate = {
+    val (calendar, format) = parseFromXMLString(string)
+    DFDLDate(calendar, format.expectsTimezone)
+  }
+
+  def toXMLString(d: DFDLDate): String = {
+    DFDLCalendarConversion.datePartToXMLString(d) +
+      DFDLCalendarConversion.timeZonePartToXMLString(d)
+  }
+}
+
+object DFDLTimeConversion extends DFDLCalendarConversion {
+
+  val calendarType = "xs:time"
+
+  @transient
+  override protected lazy val fromXMLFormats = new 
ThreadLocal[Seq[DFDLCalendarFormat]] {
+    override def initialValue = {
+      Seq(
+        DFDLCalendarFormat("HH:mm:ss.SSSSSSxxxxx", true),
+        DFDLCalendarFormat("HH:mm:ss.SSSSSS", false),
+        DFDLCalendarFormat("HH:mm:ssxxxxx", true),
+        DFDLCalendarFormat("HH:mm:ss", false)
+      )
+    }
+  }
+
+  def fromXMLString(string: String): DFDLTime = {
+    val (calendar, format) = parseFromXMLString(string)
+    DFDLTime(calendar, format.expectsTimezone)
+  }
+
+
+  def toXMLString(t: DFDLTime): String = {
+    DFDLCalendarConversion.timePartToXMLString(t) +
+      DFDLCalendarConversion.timeZonePartToXMLString(t)
+  }
+}
diff --git 
a/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/TextCalendarConstants.scala
 
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/TextCalendarConstants.scala
new file mode 100644
index 000000000..bdd88fd4f
--- /dev/null
+++ 
b/daffodil-lib/src/main/scala/org/apache/daffodil/calendar/TextCalendarConstants.scala
@@ -0,0 +1,23 @@
+/*
+ * 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 org.apache.daffodil.calendar
+
+object TextCalendarConstants {
+  final val maxFractionalSeconds = 9
+}
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala 
b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
index 2f6df383e..1c43cd65a 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/xml/XMLUtils.scala
@@ -18,11 +18,18 @@
 package org.apache.daffodil.xml
 
 import java.io.File
-import scala.xml._
-import org.apache.daffodil.exceptions._
+
 import scala.collection.mutable.ArrayBuilder
-import org.apache.commons.io.IOUtils
 import scala.xml.NamespaceBinding
+import scala.xml._
+
+import org.apache.commons.io.IOUtils
+
+import org.apache.daffodil.exceptions._
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.calendar.DFDLDateConversion
+import org.apache.daffodil.calendar.DFDLDateTimeConversion
+import org.apache.daffodil.calendar.DFDLTimeConversion
 import org.apache.daffodil.schema.annotation.props.LookupLocation
 
 /**
@@ -209,6 +216,17 @@ object XMLUtils {
     remapXMLCharacters(dfdlString, remapPUAToXMLIllegalChar(false))
   }
 
+  def coalesceAllAdjacentTextNodes(node: Node): Node = {
+    node match {
+      case Elem(prefix, label, attribs, scope, child @ _*) => {
+        val coalescedChildren = child.map(coalesceAllAdjacentTextNodes(_))
+        val newChildren = coalesceAdjacentTextNodes(coalescedChildren)
+        Elem(prefix, label, attribs, scope, true, newChildren: _*)
+      }
+      case x => x
+    }
+  }
+
   /*
    * This is needed for equality comparison of XML.
    *
@@ -224,15 +242,7 @@ object XMLUtils {
     if (seq.length == 0) return seq
     if (seq.length == 1) {
       seq(0) match {
-
-        case p: PCData => return seq
-
-        case Text(data) =>
-          if (data.matches("""\s*""")) return Nil
-          else return seq
-
-        case u: Unparsed => return seq // TODO: are these needed or possible?
-
+        case a: Atom[_] => return seq
         case _ => // fall through to code below. (We need to process children)
       }
     }
@@ -616,10 +626,8 @@ object XMLUtils {
 
   def convertPCDataToText(n: Node): Node = {
     val res = n match {
-      case PCData(data) => {
-        val t = Text(n.text)
-        t
-      }
+      case t: Text => t
+      case a: Atom[_] => Text(a.text)
       case Elem(prefix, label, attributes, scope, children @ _*) => {
         val newChildren = children.map { convertPCDataToText(_) }
         Elem(prefix, label, attributes, scope, true, newChildren: _*)
@@ -716,14 +724,25 @@ object XMLUtils {
     res
   }
 
-  def compareAndReport(trimmedExpected: Node, actualNoAttrs: Node, 
ignoreProcInstr: Boolean = true) = {
-    if (trimmedExpected != actualNoAttrs) {
-      val expString = trimmedExpected.toString
-      val actString = actualNoAttrs.toString
-      if (expString != actString) {
-        val diffs = XMLUtils.computeDiff(trimmedExpected, actualNoAttrs, 
ignoreProcInstr)
-        if (diffs.length > 0) {
-          throw new Exception("""
+  /**
+   * Prepares an XML node for diff comparison
+   **/
+  private def prepareForDiffComparison(n: Node): Node = {
+    val noComments = removeComments(n)
+    val noPCData = convertPCDataToText(noComments)
+    val combinedText = coalesceAllAdjacentTextNodes(noPCData)
+    val noMixedWS = removeMixedWhitespace(combinedText)
+    noMixedWS
+  }
+
+  class XMLDifferenceException(message: String) extends Exception(message)
+
+  def compareAndReport(expected: Node, actual: Node, ignoreProcInstr: Boolean 
= true) = {
+    val expectedMinimized = prepareForDiffComparison(expected)
+    val actualMinimized = prepareForDiffComparison(actual)
+    val diffs = XMLUtils.computeDiff(expectedMinimized, actualMinimized, 
ignoreProcInstr)
+    if (diffs.length > 0) {
+      throw new XMLDifferenceException("""
 Comparison failed.
 Expected
           %s
@@ -731,9 +750,9 @@ Actual
           %s
 Differences were (path, expected, actual):
  %s""".format(
-            trimmedExpected.toString, actualNoAttrs.toString, diffs.map { 
_.toString }.mkString("\n")))
-        }
-      }
+     removeAttributes(expected).toString,
+     removeAttributes(actual).toString,
+     diffs.map { _.toString }.mkString("", "\n", "\n")))
     }
   }
 
@@ -742,7 +761,7 @@ Differences were (path, expected, actual):
    * Each triple is the path (an x-path-like string), followed by expected, 
and actual values.
    */
   def computeDiff(a: Node, b: Node, ignoreProcInstr: Boolean = true) = {
-    computeDiffOne(Seq(a), Seq(b), Map.empty, Nil, ignoreProcInstr)
+    computeDiffOne(Seq(a), Seq(b), Map.empty, Nil, ignoreProcInstr, None)
   }
 
   def childArrayCounters(e: Elem) = {
@@ -755,27 +774,38 @@ Differences were (path, expected, actual):
     arrayCounters
   }
 
-  def computeDiffOne(as: Seq[Node], bs: Seq[Node],
+  def computeDiffOne(
+    as: Seq[Node],
+    bs: Seq[Node],
     aCounters: Map[String, Long],
     path: Seq[String],
-    ignoreProcInstr: Boolean = true): Seq[(String, String, String)] = {
+    ignoreProcInstr: Boolean,
+    maybeType: Option[String]): Seq[(String, String, String)] = {
     lazy val zPath = path.reverse.mkString("/")
     (as, bs) match {
       case (a1 :: ars, b1 :: brs) if (a1.isInstanceOf[Elem] && 
b1.isInstanceOf[Elem]) => {
         val (a: Elem, b: Elem) = (a1, b1)
         val Elem(_, labelA, attribsA, _, childrenA @ _*) = a
         val Elem(_, labelB, attribsB, _, childrenB @ _*) = b
-        if (labelA != labelB)
-          List((zPath, a.toString, b.toString))
-        else if (attribsA != attribsB
-          && !((attribsA == null && (attribsB == null || attribsB.length == 0))
-            || (attribsB == null) && attribsA.length == 0)) {
-
-          // println("attributes are different")
-
-          val aA = if (attribsA == null || attribsA == "") "null" else 
attribsA.toString
-          val aB = if (attribsB == null || attribsB == "") "null" else 
attribsB.toString
-          List((zPath, aA, aB))
+        val typeA: Option[String] = a.attribute(XSI_NAMESPACE.toString, 
"type").map(_.head.text)
+        val typeB: Option[String] = b.attribute(XSI_NAMESPACE.toString, 
"type").map(_.head.text)
+        val maybeType: Option[String] = 
Option(typeA.getOrElse(typeB.getOrElse(null)))
+        val nilledA = a.attribute(XSI_NAMESPACE.toString, "nil")
+        val nilledB = b.attribute(XSI_NAMESPACE.toString, "nil")
+
+        if (labelA != labelB) {
+          // different label
+          List((zPath, labelA, labelB))
+        } else if (nilledA != nilledB) {
+          // different xsi:nil
+          List((zPath + "/" + labelA + "@xsi:nil",
+            nilledA.map(_.toString).getOrElse(""),
+            nilledB.map(_.toString).getOrElse("")))
+        } else if (typeA != typeB && typeA.isDefined && typeB.isDefined) {
+          // different xsi:type (if both suppplied)
+          List((zPath + "/" + labelA + "@xsi:type",
+            typeA.map(_.toString).getOrElse(""),
+            typeA.map(_.toString).getOrElse("")))
         } else {
           val aIndex = aCounters.get(labelA)
           val aIndexExpr = aIndex.map { n => labelA + "[" + n + "]" }
@@ -790,27 +820,28 @@ Differences were (path, expected, actual):
           val newPath = pathStep +: path
           val childrenAList = childrenA.toList
           val childrenBList = childrenB.toList
+
           val childrenDiffs =
-            computeDiffOne(childrenAList, childrenBList, aChildArrayCounters, 
newPath, ignoreProcInstr)
-          val subsequentPeerDiffs = computeDiffOne(ars, brs, newACounters, 
path, ignoreProcInstr)
+            computeDiffOne(childrenAList, childrenBList, aChildArrayCounters, 
newPath, ignoreProcInstr, maybeType)
+          val subsequentPeerDiffs = computeDiffOne(ars, brs, newACounters, 
path, ignoreProcInstr, maybeType)
           val res = childrenDiffs ++ subsequentPeerDiffs
           res
         }
       }
       case (tA1 :: ars, tB1 :: brs) if (tA1.isInstanceOf[Text] && 
tB1.isInstanceOf[Text]) => {
         val (tA: Text, tB: Text) = (tA1, tB1)
-        val thisDiff = computeTextDiff(zPath, tA, tB)
-        val restDiffs = computeDiffOne(ars, brs, aCounters, path, 
ignoreProcInstr)
+        val thisDiff = computeTextDiff(zPath, tA, tB, maybeType)
+        val restDiffs = computeDiffOne(ars, brs, aCounters, path, 
ignoreProcInstr, maybeType)
         val res = thisDiff ++ restDiffs
         res
       }
       case (tA1 :: ars, brs) if (ignoreProcInstr && 
tA1.isInstanceOf[scala.xml.ProcInstr]) =>
-        computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr)
+        computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr, maybeType)
       case (ars, tB1 :: brs) if (ignoreProcInstr && 
tB1.isInstanceOf[scala.xml.ProcInstr]) =>
-        computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr)
+        computeDiffOne(ars, brs, aCounters, path, ignoreProcInstr, maybeType)
       case (scala.xml.ProcInstr(tA1label, tA1content) :: ars,
         scala.xml.ProcInstr(tB1label, tB1content) :: brs) => {
-        val labelDiff = computeTextDiff(zPath, tA1label, tB1label)
+        val labelDiff = computeTextDiff(zPath, tA1label, tB1label, None)
         //
         // The content of a ProcInstr is technically a big string
         // But our usage of them the content is XML-like so could be loaded 
and then compared
@@ -822,8 +853,8 @@ Differences were (path, expected, actual):
         //
         // TODO: implement XML-comparison for our data format info PIs.
         //
-        val contentDiff = computeTextDiff(zPath, tA1content, tB1content)
-        val restDiffs = computeDiffOne(ars, brs, aCounters, path, 
ignoreProcInstr)
+        val contentDiff = computeTextDiff(zPath, tA1content, tB1content, 
maybeType)
+        val restDiffs = computeDiffOne(ars, brs, aCounters, path, 
ignoreProcInstr, maybeType)
         val res = labelDiff ++ contentDiff ++ restDiffs
         res
       }
@@ -845,15 +876,26 @@ Differences were (path, expected, actual):
     }
   }
 
-  def computeTextDiff(zPath: String, tA: Text, tB: Text): Seq[(String, String, 
String)] = {
+  def computeTextDiff(
+    zPath: String,
+    tA: Text,
+    tB:Text,
+    maybeType: Option[String]): Seq[(String, String, String)] = {
+
     val dataA = tA.toString
     val dataB = tB.toString
-    computeTextDiff(zPath, dataA, dataB)
+    computeTextDiff(zPath, dataA, dataB, maybeType)
   }
 
-  def computeTextDiff(zPath: String, dataA: String, dataB: String): 
Seq[(String, String, String)] = {
+  def computeTextDiff(
+    zPath: String,
+    dataA: String,
+    dataB: String,
+    maybeType: Option[String]): Seq[(String, String, String)] = {
+
     def quoteIt(str: String) = "'" + str + "'"
-    if (dataA == dataB) Nil
+
+    if (textIsSame(dataA, dataB, maybeType)) Nil
     else if (dataA.length != dataB.length) {
       List((zPath, quoteIt(dataA), quoteIt(dataB)))
     } else {
@@ -871,6 +913,28 @@ Differences were (path, expected, actual):
     }
   }
 
+  def textIsSame(dataA: String, dataB: String, maybeType: Option[String]): 
Boolean = {
+    maybeType match {
+      case Some("xs:hexBinary") => dataA.equalsIgnoreCase(dataB)
+      case Some("xs:date") => {
+        val a = DFDLDateConversion.fromXMLString(dataA)
+        val b = DFDLDateConversion.fromXMLString(dataB)
+        a == b
+      }
+      case Some("xs:time") => {
+        val a = DFDLTimeConversion.fromXMLString(dataA)
+        val b = DFDLTimeConversion.fromXMLString(dataB)
+        a == b
+      }
+      case Some("xs:dateTime") => {
+        val a = DFDLDateTimeConversion.fromXMLString(dataA)
+        val b = DFDLDateTimeConversion.fromXMLString(dataB)
+        a == b
+      }
+      case _ => dataA == dataB
+    }
+  }
+
   /**
    * for quick tests, we use literal scala nodes. However, the underlying
    * infrastructure wants to be all file centric for diagnostic-message
diff --git 
a/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
 
b/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
index dc9f04e80..a7568c33b 100644
--- 
a/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
+++ 
b/daffodil-lib/src/test/scala/org/apache/daffodil/xml/test/unit/TestXMLUtils.scala
@@ -30,7 +30,7 @@ class TestXMLUtils {
   @Test def testDiff0() {
     val d1 = new Text("a")
     val d2 = new Text("b")
-    val diffs = XMLUtils.computeTextDiff("", d1, d2)
+    val diffs = XMLUtils.computeTextDiff("", d1, d2, None)
     val Seq((p, a, b)) = diffs
     assertEquals(".charAt(1)", p)
     assertEquals("a(%#x0061;)", a)
@@ -74,14 +74,12 @@ class TestXMLUtils {
   @Test def testNilDiff1() {
     val d1 = <a xmlns:xsi={ XMLUtils.XSI_NAMESPACE } xsi:nil="true" foo="bar"/>
     val d2 = <a dafint:col="30" xmlns:dafint={ XMLUtils.INT_NS }/>
-    val d1NoA = XMLUtils.removeAttributes(d1)
-    val d2NoA = XMLUtils.removeAttributes(d2)
-    val diffs = XMLUtils.computeDiff(d1NoA, d2NoA)
+    val diffs = XMLUtils.computeDiff(d1, d2)
     val Seq((path, d1attribs, d2attribs)) = diffs
-    assertEquals("", path)
+    assertEquals("/a@xsi:nil", path)
     // for whatever reason, an attribute prints with a leading space or 
something.
     // so we make the comparison be a 'contains' style comparison.
-    assertTrue(d1attribs.contains("xsi:nil=\"true\""))
+    assertTrue(d1attribs.contains("true"))
     assertEquals("", d2attribs)
   }
 
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
index 85c75dfcb..0e981368f 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/Conversions.scala
@@ -18,13 +18,6 @@
 package org.apache.daffodil.dpath
 
 import org.apache.daffodil.exceptions.ThrowsSDE
-import java.text.ParsePosition
-import com.ibm.icu.util.Calendar
-import com.ibm.icu.text.SimpleDateFormat
-import java.text.ParseException
-import org.apache.daffodil.calendar.DFDLDateTime
-import org.apache.daffodil.calendar.DFDLTime
-import org.apache.daffodil.calendar.DFDLDate
 
 /*
  * Casting chart taken from http://www.w3.org/TR/xpath-functions/#casting, with
@@ -294,61 +287,4 @@ object Conversion {
     ops
   }
 
-  protected def constructCalendar(value: String, inFormat: SimpleDateFormat, 
fncName: String, toType: String): Calendar = {
-    val isLenient = inFormat.isLenient()
-
-    try {
-      inFormat.setLenient(false)
-      inFormat.parse(value)
-      true
-    } catch {
-      case e: ParseException => {
-        inFormat.setLenient(isLenient)
-        throw new java.lang.IllegalArgumentException(String.format("Conversion 
Error: %s failed to convert \"%s\" to %s. Due to %s",
-          fncName, value.toString, toType, "Failed to match format."))
-      }
-    }
-
-    val calendar = inFormat.getCalendar()
-    val pos = new ParsePosition(0)
-
-    inFormat.parse(value.toString, calendar, pos)
-
-    try {
-      calendar.getTime()
-    } catch {
-      case ex: java.lang.IllegalArgumentException => {
-        inFormat.setLenient(isLenient)
-        throw new java.lang.IllegalArgumentException(String.format("Conversion 
Error: %s failed to convert \"%s\" to %s. Due to %s",
-          fncName, value.toString, toType, ex.getMessage()))
-      }
-    }
-
-    inFormat.setLenient(isLenient)
-
-    if (pos.getIndex() == 0 || pos.getErrorIndex() != -1) {
-      throw new java.lang.IllegalArgumentException(String.format("Conversion 
Error: %s failed to convert \"%s\" to %s due to a parse error.",
-        fncName, value.toString, toType))
-    }
-    if (pos.getIndex() != (value.length())) {
-      throw new java.lang.IllegalArgumentException(String.format("Conversion 
Error: %s failed to convert \"%s\" to %s. Failed to use up all characters in 
the parse.  Stopped at %s.",
-        fncName, value.toString, toType, pos.getIndex().toString))
-    }
-    calendar
-  }
-
-  def stringToDFDLDateTime(value: String, inFormat: SimpleDateFormat, 
expectsTZ: Boolean, fncName: String, toType: String): DFDLDateTime = {
-    val calendar = constructCalendar(value, inFormat, fncName, toType)
-    DFDLDateTime(calendar, expectsTZ)
-  }
-
-  def stringToDFDLDate(value: String, inFormat: SimpleDateFormat, expectsTZ: 
Boolean, fncName: String, toType: String): DFDLDate = {
-    val calendar = constructCalendar(value, inFormat, fncName, toType)
-    DFDLDate(calendar, expectsTZ)
-  }
-
-  def stringToDFDLTime(value: String, inFormat: SimpleDateFormat, expectsTZ: 
Boolean, fncName: String, toType: String): DFDLTime = {
-    val calendar = constructCalendar(value, inFormat, fncName, toType)
-    DFDLTime(calendar, expectsTZ)
-  }
 }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
index f38ce73cf..1b43e1eb6 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/ConverterOps2.scala
@@ -19,10 +19,14 @@ package org.apache.daffodil.dpath
 
 import com.ibm.icu.text.SimpleDateFormat
 import com.ibm.icu.util.Calendar
-import org.apache.daffodil.calendar.DFDLDateTime
+
 import org.apache.daffodil.calendar.DFDLCalendar
-import org.apache.daffodil.calendar.DFDLTime
 import org.apache.daffodil.calendar.DFDLDate
+import org.apache.daffodil.calendar.DFDLDateConversion
+import org.apache.daffodil.calendar.DFDLDateTime
+import org.apache.daffodil.calendar.DFDLDateTimeConversion
+import org.apache.daffodil.calendar.DFDLTime
+import org.apache.daffodil.calendar.DFDLTimeConversion
 
 case object AnyAtomicToString extends Converter {
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
@@ -33,128 +37,44 @@ case object AnyAtomicToString extends Converter {
   }
 }
 
-trait XSDateTimeKind extends Serializable {
-  val timeZoneID = "UTC"
-  @transient lazy val calendar = new ThreadLocal[Calendar] {
-    override def initialValue = {
-      val cal = Calendar.getInstance()
-      cal.clear()
-      cal.setLenient(false)
-      //
-      // We don't want to set a TimeZone here.  It should be 'parsed' if it's
-      // there.  If it's not, the TimeZone will be set to TimeZone.UNKNOWN_ZONE
-      // which will operate just like GMT/UTC
-      //
-      cal
-    }
-  }
-
-  def defaultFormatter: ThreadLocal[DFDLDateFormat]
-  def acceptableFormats: Seq[DFDLDateFormat]
-
-  def getNewCalendar: Calendar = calendar.get.clone().asInstanceOf[Calendar]
-
-  protected def createCalendar(str: String, inFormat: SimpleDateFormat, 
expectsTZ: Boolean,
-    fncName: String, toType: String): DFDLCalendar
-
-  def matchFormat(str: String, fncName: String, toType: String): DFDLCalendar 
= {
-
-    acceptableFormats.foreach(f => {
-      val inFormat = f.getFormat
-      inFormat.setCalendar(getNewCalendar)
-      try {
-        val cal = createCalendar(str, inFormat, f.expectsTimeZone, fncName, 
toType)
-        // Here we've successfully created a calendar using the expected format
-        // denoted by 'inFormat'. Return the calendar.
-        return cal
-      } catch {
-        case e: IllegalArgumentException => /* Format failed, continue trying 
to match other formats */
-      }
-
-    })
-    // All acceptable formats failed
-    throw new NumberFormatException("Failed to convert \"%s\" to 
%s.".format(str, toType))
-  }
-}
-
-case object StringToDate extends Converter with XSDateTimeKind with 
DateFormatters {
+case object StringToDate extends Converter {
   val name = "StringToDate"
 
-  /**
-   * A list of acceptable formats as specified by: 
http://www.w3.org/TR/NOTE-datetime
-   *
-   * Order matters here as we are also trying to determine if a time zone was 
parsed
-   */
-  def acceptableFormats = Seq(defaultFormatter.get, 
withoutTimezoneFormatter.get)
-
-  protected def createCalendar(str: String, inFormat: SimpleDateFormat, 
expectsTZ: Boolean, fncName: String, toType: String): DFDLCalendar = {
-    Conversion.stringToDFDLDate(str, inFormat, expectsTZ, fncName, toType)
-  }
-
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
     val result = a match {
       case cal: DFDLDateTime => cal.toDate
       case cal: DFDLDate => cal
-      case str: String => matchFormat(str, name, "xs:date")
+      case str: String => DFDLDateConversion.fromXMLString(str)
       case _ => throw new NumberFormatException("xs:date only accepts String, 
Date or DateTime objects.")
     }
     result
   }
 }
-case object StringToDateTime extends Converter
-  with XSDateTimeKind with DateTimeFormatters {
-  val name = "StringToDateTime"
-
-  /**
-   * A list of acceptable formats as specified by: 
http://www.w3.org/TR/NOTE-datetime
-   *
-   * Order matters here as we are also trying to determine if a time zone was 
parsed
-   */
-  def acceptableFormats = Seq(defaultFormatter.get, 
withFractNoTimeZoneFormatter.get,
-    withTimeZoneNoFractFormatter.get, noTimeZoneNoFractFormatter.get,
-    withTimeZoneFormatter.get, dateOnlyFormatter.get)
 
-  protected def createCalendar(str: String, inFormat: SimpleDateFormat,
-    expectsTZ: Boolean, fncName: String, toType: String): DFDLCalendar = {
-    Conversion.stringToDFDLDateTime(str, inFormat, expectsTZ, fncName, toType)
-  }
+case object StringToDateTime extends Converter {
+  val name = "StringToDateTime"
 
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
     val result = a match {
       case cal: DFDLDateTime => cal
       case cal: DFDLDate => cal.toDateTime
-      case str: String => matchFormat(str, name, "xs:dateTime")
+      case str: String => DFDLDateTimeConversion.fromXMLString(str)
       case _ => throw new NumberFormatException("xs:dateTime only accepts 
String, Date or DateTime objects.")
     }
     result
   }
-
 }
 
-case object StringToTime extends Converter with XSDateTimeKind with 
TimeFormatters {
+case object StringToTime extends Converter {
   val name = "StringToTime"
 
-  /**
-   * A list of acceptable formats as specified by: 
http://www.w3.org/TR/NOTE-datetime
-   *
-   * Order matters here as we are also trying to determine if a time zone was 
parsed
-   */
-  def acceptableFormats = Seq(defaultFormatter.get, noTimeZoneFormatter.get,
-    withTimeZoneNoFractFormatter.get, noTimeZoneNoFractFormatter.get)
-
-  protected def createCalendar(str: String, inFormat: SimpleDateFormat,
-    expectsTZ: Boolean, fncName: String, toType: String): DFDLCalendar = {
-    Conversion.stringToDFDLTime(str, inFormat, expectsTZ, fncName, toType)
-  }
-
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
     val result = a match {
       case cal: DFDLDateTime => cal.toTime
       case cal: DFDLTime => cal
-      case str: String => matchFormat(str, name, "xs:time")
+      case str: String => DFDLTimeConversion.fromXMLString(str)
       case _ => throw new NumberFormatException("xs:time only accepts String, 
DateTime or Time objects.")
     }
     result
   }
-
 }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
index f08dd34f1..11133cc0f 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DFDLFunctions.scala
@@ -17,11 +17,12 @@
 
 package org.apache.daffodil.dpath
 
-import org.apache.daffodil.exceptions._
-import org.apache.daffodil.dsom._
 import org.apache.daffodil.calendar.DFDLCalendar
-import org.apache.daffodil.util.Numbers._
+import org.apache.daffodil.calendar.DFDLCalendarConversion
 import org.apache.daffodil.cookers.EntityReplacer
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.exceptions._
+import org.apache.daffodil.util.Numbers._
 
 case class DFDLCheckConstraints(recipe: CompiledDPath) extends 
RecipeOpWithSubRecipes(recipe) {
   override def run(dstate: DState) {
@@ -114,10 +115,8 @@ case class DFDLTimeZoneFromDFDLCalendar(recipe: 
CompiledDPath, argType: NodeInfo
   extends FNOneArg(recipe, argType) {
 
   override def computeValue(value: AnyRef, dstate: DState) = {
-    val calendar = value.asInstanceOf[DFDLCalendar]
-
-    val res = if (calendar.hasTimeZone) { calendar.getTimeZoneString } else { 
"" }
-    res
+    val dfdlcal = value.asInstanceOf[DFDLCalendar]
+    DFDLCalendarConversion.timeZonePartToXMLString(dfdlcal)
   }
 }
 
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DateTimeConversions.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DateTimeConversions.scala
deleted file mode 100644
index 2f5bd4ea0..000000000
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DateTimeConversions.scala
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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 org.apache.daffodil.dpath
-
-import com.ibm.icu.text.SimpleDateFormat
-
-class DFDLDateFormat(format: SimpleDateFormat, expectsTZ: Boolean = false) {
-  def getFormat = format
-  def expectsTimeZone = expectsTZ
-}
-
-trait DateFormatters {
-  @transient lazy val defaultFormatter = new ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-ddxxxxx"), 
true)
-      format
-    }
-  }
-  @transient lazy val withoutTimezoneFormatter = new 
ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-dd"))
-      format
-    }
-  }
-}
-
-trait TimeFormatters {
-  @transient lazy val defaultFormatter = new ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new 
SimpleDateFormat("HH:mm:ss.SSSSSSxxxxx"), true)
-      format
-    }
-  }
-
-  @transient lazy val noTimeZoneFormatter = new ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("HH:mm:ss.SSSSSS"))
-      format
-    }
-  }
-
-  @transient lazy val noTimeZoneNoFractFormatter = new 
ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("HH:mm:ss"))
-      format
-    }
-  }
-
-  @transient lazy val withTimeZoneNoFractFormatter = new 
ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("HH:mm:ssxxxxx"), 
true)
-      format
-    }
-  }
-}
-
-trait DateTimeFormatters {
-  @transient lazy val defaultFormatter = new ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new 
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx"), true)
-      format
-    }
-  }
-
-  @transient lazy val withFractNoTimeZoneFormatter = new 
ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new 
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ss.SSSSSS"))
-      format
-    }
-  }
-
-  @transient lazy val withTimeZoneNoFractFormatter = new 
ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new 
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ssxxxxx"), true)
-      format
-    }
-  }
-
-  @transient lazy val noTimeZoneNoFractFormatter = new 
ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new 
SimpleDateFormat("uuuu-MM-dd'T'HH:mm:ss"))
-      format
-    }
-  }
-
-  @transient lazy val withTimeZoneFormatter = new ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-ddxxxxx"), 
true)
-      format
-    }
-  }
-
-  @transient lazy val dateOnlyFormatter = new ThreadLocal[DFDLDateFormat] {
-    override def initialValue = {
-      val format = new DFDLDateFormat(new SimpleDateFormat("uuuu-MM-dd"))
-      format
-    }
-  }
-}
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
index cdedb87ee..20d195376 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/FNFunctions.scala
@@ -242,7 +242,7 @@ case class FNDateTime(recipes: List[CompiledDPath]) extends 
FNTwoArgs(recipes) {
 
   private def calendarToDFDLDateTime(calendar: Calendar, hasTZ: Boolean, 
dstate: DState, fncName: String, toType: String): DFDLCalendar = {
     try {
-      val cal = new DFDLDateTime(calendar, hasTZ)
+      val cal = DFDLDateTime(calendar, hasTZ)
       return cal
     } catch {
       case ex: java.lang.IllegalArgumentException =>
@@ -253,8 +253,8 @@ case class FNDateTime(recipes: List[CompiledDPath]) extends 
FNTwoArgs(recipes) {
     val dateCalendar = arg1.asInstanceOf[DFDLCalendar]
     val timeCalendar = arg2.asInstanceOf[DFDLCalendar]
 
-    val dateCal = dateCalendar.getCalendar
-    val timeCal = timeCalendar.getCalendar
+    val dateCal = dateCalendar.calendar
+    val timeCal = timeCalendar.calendar
 
     val year = dateCal.get(Calendar.YEAR)
     val day = dateCal.get(Calendar.DAY_OF_MONTH)
@@ -748,7 +748,7 @@ abstract class FNFromDateTime(recipe: CompiledDPath, 
argType: NodeInfo.Kind)
   with FNFromDateTimeKind {
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
     a match {
-      case dt: DFDLDateTime => asAnyRef(dt.getField(field))
+      case dt: DFDLDateTime => asAnyRef(dt.calendar.get(field))
       case _ => throw new NumberFormatException("fn:" + fieldName + 
"-from-dateTime only accepts xs:dateTime.")
     }
   }
@@ -759,7 +759,7 @@ abstract class FNFromDate(recipe: CompiledDPath, argType: 
NodeInfo.Kind)
   with FNFromDateTimeKind {
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
     a match {
-      case d: DFDLDate => asAnyRef(d.getField(field))
+      case d: DFDLDate => asAnyRef(d.calendar.get(field))
       case _ => throw new NumberFormatException("fn:" + fieldName + 
"-from-date only accepts xs:date.")
     }
   }
@@ -770,7 +770,7 @@ abstract class FNFromTime(recipe: CompiledDPath, argType: 
NodeInfo.Kind)
   with FNFromDateTimeKind {
   override def computeValue(a: AnyRef, dstate: DState): AnyRef = {
     a match {
-      case t: DFDLTime => asAnyRef(t.getField(field))
+      case t: DFDLTime => asAnyRef(t.calendar.get(field))
       case _ => throw new NumberFormatException("fn:" + fieldName + 
"-from-time only accepts xs:time.")
     }
   }
@@ -826,8 +826,8 @@ case class FNSecondsFromDateTime(recipe: CompiledDPath, 
argType: NodeInfo.Kind)
     //
     val res: JNumber = a match {
       case dt: DFDLDateTime => {
-        val seconds = dt.getField(Calendar.SECOND)
-        val frac = dt.getField(Calendar.MILLISECOND)
+        val seconds = dt.calendar.get(Calendar.SECOND)
+        val frac = dt.calendar.get(Calendar.MILLISECOND)
         if (frac == 0) { seconds }
         else {
           val d = seconds + (frac / 1000.0)
@@ -878,8 +878,8 @@ case class FNSecondsFromTime(recipe: CompiledDPath, 
argType: NodeInfo.Kind)
     //
     val res: JNumber = a match {
       case dt: DFDLTime => {
-        val seconds = dt.getField(Calendar.SECOND)
-        val frac = dt.getField(Calendar.MILLISECOND)
+        val seconds = dt.calendar.get(Calendar.SECOND)
+        val frac = dt.calendar.get(Calendar.MILLISECOND)
         if (frac == 0) { seconds }
         else {
           val d = seconds + (frac / 1000.0)
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
index 047f9bae2..64d7f7efd 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/NodeInfo.scala
@@ -527,27 +527,15 @@ object NodeInfo extends Enum {
     case object Date extends PrimTypeNode(AnyDateTime, Nil) with DateKind {
       type Kind = DateKind
       override def fromXMLString(s: String): AnyRef = {
-        StringToDate.computeValue(s, null)
-        //val juDate = 
TextCalendarConstants.tlDateNoTZInfosetFormatter.get.parse(s)
-        //val icuCal = dateToCalendar(juDate)
-        //DFDLDate(icuCal, false)
+        DFDLDateConversion.fromXMLString(s)
       }
-
-      /*
-      private def dateToCalendar(date: java.util.Date): Calendar = {
-        val cal = Calendar.getInstance()
-        cal.setTime(date)
-        cal
-      }
-*/
-
     }
 
     protected sealed trait DateTimeKind extends AnyDateTimeKind
     case object DateTime extends PrimTypeNode(AnyDateTime, Nil) with 
DateTimeKind {
       type Kind = DateTimeKind
       override def fromXMLString(s: String): AnyRef = {
-        StringToDateTime.computeValue(s, null)
+        DFDLDateTimeConversion.fromXMLString(s)
       }
     }
 
@@ -555,7 +543,7 @@ object NodeInfo extends Enum {
     case object Time extends PrimTypeNode(AnyDateTime, Nil) with TimeKind {
       type Kind = TimeKind
       override def fromXMLString(s: String): AnyRef = {
-        StringToTime.computeValue(s, null)
+        DFDLTimeConversion.fromXMLString(s)
       }
     }
   }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
index 19e860059..5825ca845 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesDateTime1.scala
@@ -144,9 +144,9 @@ case class ConvertTextCalendarParser(
     }
 
     val newCal = xsdType.toLowerCase() match {
-      case "time" => new DFDLTime(cal, hasTZ)
-      case "date" => new DFDLDate(cal, hasTZ)
-      case "datetime" => new DFDLDateTime(cal, hasTZ)
+      case "time" => DFDLTime(cal, hasTZ)
+      case "date" => DFDLDate(cal, hasTZ)
+      case "datetime" => DFDLDateTime(cal, hasTZ)
       case _ => Assert.impossibleCase
     }
 
@@ -155,37 +155,6 @@ case class ConvertTextCalendarParser(
   }
 }
 
-object TextCalendarConstants {
-  final val maxFractionalSeconds = 9
-
-  // before being used, setCalendar must be called on the SimpleDateFormat
-  //
-  // The reason for the various NoTZ and NoFractSec variations are because
-  // we need to keep track of whether the data had timezone or millisecond
-  // information initially, otherwise we end up assuming a timezone, such
-  // as UTC, and .000000 milliseconds when the data did not specify this
-  // information.
-  final val tlDateTimeNoTZInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ss.SSSSSS")
-  final val tlDateTimeInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx")
-  final val tlDateTimeNoTZNoFractSecInfosetFormatter: 
ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ss")
-  final val tlDateTimeNoFractSecInfosetFormatter: 
ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("uuuu-MM-dd'T'HH:mm:ssxxxxx")
-  final val tlDateNoTZInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("uuuu-MM-dd")
-  final val tlDateInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("uuuu-MM-ddxxxxx")
-  final val tlTimeNoTZInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("HH:mm:ss.SSSSSS")
-  final val tlTimeInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("HH:mm:ss.SSSSSSxxxxx")
-  final val tlTimeNoTZNoFractSecInfosetFormatter: 
ThreadLocal[SimpleDateFormat] = createTLInfosetFormatter("HH:mm:ss")
-  final val tlTimeNoFractSecInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("HH:mm:ssxxxxx")
-  final val tlTzInfosetFormatter: ThreadLocal[SimpleDateFormat] = 
createTLInfosetFormatter("xxx") // -08:00 The ISO8601 extended format with 
hours and minutes fields.
-
-  private def createTLInfosetFormatter(pattern: String) = new 
ThreadLocal[SimpleDateFormat] {
-    override def initialValue = {
-      val formatter = new SimpleDateFormat(pattern)
-      formatter.setLenient(true)
-      formatter
-    }
-  }
-}
-
 case class ConvertBinaryCalendarSecMilliParser(
   override val context: ElementRuntimeData,
   hasTZ: Boolean,
@@ -229,7 +198,7 @@ case class ConvertBinaryCalendarSecMilliParser(
       }
     }
 
-    val newCal = new DFDLDateTime(cal, hasTZ)
+    val newCal = DFDLDateTime(cal, hasTZ)
     start.simpleElement.overwriteDataValue(newCal)
   }
 }
diff --git 
a/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
 
b/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
index da372e7bb..01be886c9 100644
--- 
a/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
+++ 
b/daffodil-runtime1/src/test/scala/org/apache/daffodil/processors/input/TestICU.scala
@@ -25,7 +25,7 @@ import com.ibm.icu.util.Calendar
 import com.ibm.icu.text.DecimalFormat
 import com.ibm.icu.text.DecimalFormatSymbols
 import org.junit.Test
-import org.apache.daffodil.processors.parsers.TextCalendarConstants
+import org.apache.daffodil.calendar.TextCalendarConstants
 
 class TestICU {
 
diff --git 
a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala 
b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
index 5c3f9cdc9..0ccbdef52 100644
--- a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
+++ b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
@@ -1267,40 +1267,10 @@ case class UnparserTestCase(ptc: NodeSeq, parentArg: 
DFDLTestSuite)
 object VerifyTestCase {
   def verifyParserTestData(actual: Node, infoset: Infoset, implString: 
Option[String]) {
 
-    val actualForCompare = XMLUtils.removeAttributes(actual)
-
-    // For debug
-    // scala.xml.XML.save("/tmp/actual.out.xml", actual, "utf-8")
-
-    //
-    // Would be great to validate the actuals against the DFDL schema, used as
-    // an XML schema on the returned infoset XML.
-    // Getting this to work is a bigger issue. What with stripping of 
attributes
-    // and that our internal Daffodil XML Catalog has a special treatment of 
the
-    // mapping of the XML Schema URI.
-    // etc.
-    //
-    // TODO: Fix so we can validate here.
-    //
-
-    // Something about the way XML is constructed is different between our 
infoset
-    // results and the ones created by scala directly parsing the TDML test 
files.
-    //
-    // This has something to do with values being lists of text nodes and 
entities
-    // and not just simple strings. I.e., if you write: <foo>a&#x5E74;</foo>, 
that's not
-    // an element with a string as its value. It's an element with several 
text nodes as
-    // its values.
-    //
-    // so we run the expected stuff through the same conditioners as the actual
-    // data so that they are properly comparable.
-    //
-
     val expected = infoset.contents
 
-    val expectedForCompare = 
XMLUtils.removeAttributes(XMLUtils.convertPCDataToText(expected))
-
     try {
-      XMLUtils.compareAndReport(expectedForCompare, actualForCompare)
+      XMLUtils.compareAndReport(expected, actual)
     } catch {
       case e: Exception =>
         throw TDMLException(e, implString)
diff --git 
a/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
 
b/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
index ab8952093..9c0b18a64 100644
--- 
a/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
+++ 
b/daffodil-tdml-processor/src/test/scala/org/apache/daffodil/tdml/TestTDMLRunner.scala
@@ -521,4 +521,172 @@ f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
     lazy val r = new DFDLTestSuite(Misc.getRequiredResource(t0))
     r.runOneTest("tdmlNamespaces1")
   }
+
+
+  val testHexBinarySchema = SchemaUtils.dfdlTestSchema(
+    <xs:include 
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+    <dfdl:format ref="tns:GeneralFormat"/>,
+    <xs:element name="data" type="xs:hexBinary" dfdl:lengthKind="explicit" 
dfdl:length="4"/>)
+
+  @Test def testTDMLHexBinaryTypeAwareSuccess_01() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLHexBinaryTypeAwareSuccess"
+                        root="data">
+                        <ts:document>
+                          <ts:documentPart 
type="byte">A1B2C3D4</ts:documentPart>
+                        </ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example } xmlns:xsi={ xsi } 
xmlns:xs={ xsd }
+                              xsi:type="xs:hexBinary">a1b2c3d4</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    ts.runOneTest("testTDMLHexBinaryTypeAwareSuccess", 
Some(testHexBinarySchema))
+  }
+
+  @Test def testTDMLHexBinaryTypeAwareSuccess_02() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLHexBinaryTypeAwareSuccess"
+                        root="data">
+                        <ts:document>
+                          <ts:documentPart 
type="byte">A1B2C3D4</ts:documentPart>
+                        </ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example } xmlns:xsi={ xsi } 
xmlns:xs={ xsd }
+                              xsi:type="xs:hexBinary">A1B2C3D4</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    ts.runOneTest("testTDMLHexBinaryTypeAwareSuccess", 
Some(testHexBinarySchema))
+  }
+
+  @Test def testTDMLHexBinaryTypeAwareFailure() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLHexBinaryTypeAwareFailure"
+                        root="data">
+                        <ts:document>
+                          <ts:documentPart 
type="byte">A1B2C3D4</ts:documentPart>
+                        </ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example }>a1b2c3d4</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    val e = intercept[Exception] {
+      ts.runOneTest("testTDMLHexBinaryTypeAwareFailure", 
Some(testHexBinarySchema))
+    }
+    val msg = e.getMessage()
+    assertTrue(msg.contains("Comparison failed"))
+    assertTrue(msg.contains("a1b2c3d4"))
+    assertTrue(msg.contains("A1B2C3D4"))
+  }
+
+
+
+  val testDateTimeSchema = SchemaUtils.dfdlTestSchema(
+    <xs:include 
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+    <dfdl:format ref="tns:GeneralFormat"/>,
+    <xs:element name="data" type="xs:dateTime" dfdl:lengthKind="explicit" 
dfdl:length="32"
+      dfdl:calendarPatternKind="explicit"
+      dfdl:calendarPattern="uuuu-MM-dd'T'HH:mm:ss.SSSSSSxxxxx" />)
+
+  @Test def testTDMLDateTimeTypeAwareSuccess_01() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLDateTimeTypeAwareSuccess"
+                        root="data">
+                        
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example } xmlns:xsi={ xsi } 
xmlns:xs={ xsd }
+                              
xsi:type="xs:dateTime">1995-03-24T01:30:00Z</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+  }
+
+  @Test def testTDMLDateTimeTypeAwareSuccess_02() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLDateTimeTypeAwareSuccess"
+                        root="data">
+                        
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example } xmlns:xsi={ xsi } 
xmlns:xs={ xsd }
+                              
xsi:type="xs:dateTime">1995-03-24T01:30:00+00:00</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+  }
+
+  @Test def testTDMLDateTimeTypeAwareSuccess_03() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLDateTimeTypeAwareSuccess"
+                        root="data">
+                        
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example } xmlns:xsi={ xsi } 
xmlns:xs={ xsd }
+                              
xsi:type="xs:dateTime">1995-03-24T01:30:00.000+00:00</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+  }
+
+  @Test def testTDMLDateTimeTypeAwareSuccess_04() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLDateTimeTypeAwareSuccess"
+                        root="data">
+                        
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example } xmlns:xsi={ xsi } 
xmlns:xs={ xsd }
+                              
xsi:type="xs:dateTime">1995-03-24T01:30:00.000000Z</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    ts.runOneTest("testTDMLDateTimeTypeAwareSuccess", Some(testDateTimeSchema))
+  }
+
+  @Test def testTDMLDateTimeTypeAwareFailure() {
+    val testSuite = <ts:testSuite xmlns:ts={ tdml } suiteName="theSuiteName">
+                      <ts:parserTestCase ID="some identifier" 
name="testTDMLDateTimeTypeAwareFailure"
+                        root="data">
+                        
<ts:document>1995-03-24T01:30:00.000000+00:00</ts:document>
+                        <ts:infoset>
+                          <ts:dfdlInfoset>
+                            <data xmlns={ example }>1995-03-24T01:30:00Z</data>
+                          </ts:dfdlInfoset>
+                        </ts:infoset>
+                      </ts:parserTestCase>
+                    </ts:testSuite>
+    val ts = new DFDLTestSuite(testSuite)
+    val e = intercept[Exception] {
+      ts.runOneTest("testTDMLDateTimeTypeAwareFailure", 
Some(testDateTimeSchema))
+    }
+    val msg = e.getMessage()
+    assertTrue(msg.contains("Comparison failed"))
+    assertTrue(msg.contains("1995-03-24T01:30:00Z"))
+    assertTrue(msg.contains("1995-03-24T01:30:00+00:00"))
+  }
+
 }
diff --git 
a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml 
b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
index eb72ec775..dc6ecb7b4 100644
--- 
a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
+++ 
b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaext2.tdml
@@ -276,8 +276,7 @@
        <tdml:parserTestCase name="simple_type_properties_text_calendar_13_02" 
root="calendar_group"
         roundTrip="twoPass"
                model="./fvt/ext/dpa/dpacaltxt_43.dfdl.xsd" 
-                description="Section 13.10 Properties for calendar text  - 
implicit patterns for date, time, and dateTime "
-     implementations="daffodil">
+                description="Section 13.10 Properties for calendar text  - 
implicit patterns for date, time, and dateTime ">
                
<tdml:document>2010-12-30*04:05:06+01:00*2010-12-30T04:05:06@@</tdml:document>
                <tdml:infoset>
                        <tdml:dfdlInfoset>
@@ -289,27 +288,6 @@
                        </tdml:dfdlInfoset>
                </tdml:infoset>
        </tdml:parserTestCase>
-  
-  <!-- Until DAFFODIL-2026 (type-dependent comparison for TDML tests) is 
implemented, we need
-  some tests to be split into IBM tests, and Daffodil tests since different 
equivalent infosets are 
-  allowed. -->
-    <tdml:parserTestCase name="simple_type_properties_text_calendar_13_02_ibm" 
root="calendar_group"
-        roundTrip="twoPass"
-    model="./fvt/ext/dpa/dpacaltxt_43.dfdl.xsd" 
-     description="Section 13.10 Properties for calendar text  - implicit 
patterns for date, time, and dateTime "
-     implementations="ibm">
-    
<tdml:document>2010-12-30*04:05:06+01:00*2010-12-30T04:05:06@@</tdml:document>
-    <tdml:infoset>
-      <tdml:dfdlInfoset>
-        <calendar_group>
-          <date1 xsi:type="xs:date">2010-12-30Z</date1>
-          <time1 xsi:type="xs:time">04:05:06.000+01:00</time1>
-          <datetime1 
xsi:type="xs:dateTime">2010-12-30T04:05:06.000Z</datetime1>
-        </calendar_group>
-      </tdml:dfdlInfoset>
-    </tdml:infoset>
-  </tdml:parserTestCase>
-  
                
        <tdml:parserTestCase name="simple_type_properties_text_calendar_13_03" 
root="myDateTime"
         roundTrip="twoPass"
diff --git 
a/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala 
b/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
index 09263c8f5..4e398602b 100644
--- a/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
+++ b/daffodil-test-ibm1/src/test/scala/org/apache/daffodil/IBMTests.scala
@@ -47,8 +47,6 @@ class IBMTestsThatPass {
   // Doesn't work for a user, possible locale issue (DAFFODIL-1945)
   // @Test def test_simple_type_properties_text_calendar_13_01() { 
runner2.runOneTest("simple_type_properties_text_calendar_13_01") }
   @Test def test_simple_type_properties_text_calendar_13_02() { 
runner2.runOneTest("simple_type_properties_text_calendar_13_02") }
-  // Equivalent IBM test needed until DAFFODIL-2026 is implemented
-  @Test def test_simple_type_properties_text_calendar_13_02_ibm() { 
runner2.runOneTest("simple_type_properties_text_calendar_13_02_ibm") }
   @Test def test_simple_type_properties_text_calendar_13_03() { 
runner2.runOneTest("simple_type_properties_text_calendar_13_03") }
   @Test def test_simple_type_properties_text_calendar_13_04() { 
runner2.runOneTest("simple_type_properties_text_calendar_13_04") }
 
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
index f5568f05e..f2b9dedda 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section12/lengthKind/PatternTests.tdml
@@ -329,8 +329,7 @@
           <first>bat</first>
           <second>cz</second>
           <third>3 4</third>
-          <fourth>
-          </fourth>
+          <fourth><![CDATA[  ]]></fourth>
           <fifth>AAAAA</fifth>
         </list_01>
       </tdml:dfdlInfoset>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to