You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by sl...@apache.org on 2018/12/17 17:48:11 UTC

[incubator-daffodil] branch master updated: Add support for type aware checking in the TDML runner

This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new a3e15e6  Add support for type aware checking in the TDML runner
a3e15e6 is described below

commit a3e15e665f92b0ea64a7651eff080489cff82e22
Author: Steve Lawrence <sl...@apache.org>
AuthorDate: Thu Dec 13 16:20:47 2018 -0500

    Add support for type aware checking in the TDML runner
    
    - One can now add xsi:type information to the expected infoset or to the
      generated infoset to have the TDML runner perform type aware
      comparisons. If both the actual and expected infosets provide type
      information, the xsi:type must be the same. If no type information is
      provided in either, string comparison is performed.
    - Move much of the Date/Time conversion code out of the runtime and
      into daffoddil-lib where the XMLUtils that perform comparisons live.
      Before is was tied too closely to DPath and made it difficult to use
      outside of that. DFDLDate/Time is no longer coupled to DPath and can
      be used for generic date time tasks.
    - Refactored the date/time code logic to be simpler and remove
      duplication and complexity that has grown over the years.
    - Add custom logic to convert Date/Time calendars to XML strings. This
      avoids SimpleDateTime and the thread safety issues and complexity it
      brings with it. Instead, we access individual calendar fields and
      pad/combine them as necessary to build an XML compatible string.
    - Modified XMLUtils.compareAreReport make all the necessary
      modifications to compare two infosets. This used to be scattered among
      different callers and wasn't consistent about what was actually done.
      This should make future comparisons, such as namespace checking,
      easier to add as well. Also, comparison no longer do a toString
      comparison first, it always does our custom comparison, allowing
      things to be more strict about whitespace expectations.
    - This does not yet determine which tests could make use of this
      function and change them so to show cross compatibility with IBM. It
      only adds the capability and adds some tests to validate it.
    
    DAFFODIL-2026
---
 .../grammar/primitives/PrimitivesDateTime.scala    |  23 +-
 .../scala/org/apache/daffodil/util/TestUtils.scala |  10 +-
 .../apache/daffodil/calendar/DFDLCalendar.scala    | 198 ++++++-----------
 .../daffodil/calendar/DFDLCalendarConversion.scala | 247 +++++++++++++++++++++
 .../daffodil/calendar/TextCalendarConstants.scala  |  23 ++
 .../scala/org/apache/daffodil/xml/XMLUtils.scala   | 172 +++++++++-----
 .../daffodil/xml/test/unit/TestXMLUtils.scala      |  10 +-
 .../org/apache/daffodil/dpath/Conversions.scala    |  64 ------
 .../org/apache/daffodil/dpath/ConverterOps2.scala  | 106 ++-------
 .../org/apache/daffodil/dpath/DFDLFunctions.scala  |  13 +-
 .../daffodil/dpath/DateTimeConversions.scala       | 114 ----------
 .../org/apache/daffodil/dpath/FNFunctions.scala    |  20 +-
 .../scala/org/apache/daffodil/dpath/NodeInfo.scala |  18 +-
 .../processors/parsers/PrimitivesDateTime1.scala   |  39 +---
 .../apache/daffodil/processors/input/TestICU.scala |   2 +-
 .../org/apache/daffodil/tdml/TDMLRunner.scala      |  32 +--
 .../org/apache/daffodil/tdml/TestTDMLRunner.scala  | 168 ++++++++++++++
 .../test-suite/ibm-contributed/dpaext2.tdml        |  24 +-
 .../test/scala/org/apache/daffodil/IBMTests.scala  |   2 -
 .../section12/lengthKind/PatternTests.tdml         |   3 +-
 20 files changed, 677 insertions(+), 611 deletions(-)

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 bef2ea3..fee4c51 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 11c47c3..c7994d4 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 6c5743e..9af3315 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 0000000..0c4ecda
--- /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 0000000..bdd88fd
--- /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 2f6df38..1c43cd6 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 dc9f04e..a7568c3 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 85c75df..0e98136 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 f38ce73..1b43e1e 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 f08dd34..11133cc 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 2f5bd4e..0000000
--- 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 cdedb87..20d1953 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 047f9ba..64d7f7e 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 19e8600..5825ca8 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 da372e7..01be886 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 5c3f9cd..0ccbdef 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 ab89520..9c0b18a 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 eb72ec7..dc6ecb7 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 09263c8..4e39860 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 f5568f0..f2b9ded 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>