You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ja...@apache.org on 2018/07/09 16:04:10 UTC

[incubator-daffodil] 01/01: Implemented Zoned Decimal formats

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

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

commit 098b3d54e6bc6d60ffbb612e912ee2daec83dcca
Author: Josh Adams <ja...@tresys.com>
AuthorDate: Thu Apr 5 08:36:50 2018 -0400

    Implemented Zoned Decimal formats
    
    This commit implements parsers and unparsers for the following Zoned
    Decimal formats:
    
     - asciiStandard
     - asciiTranslatedEBCDIC
     - asciiCARealiaModified
     - asciiTandemModified
    
    The one caveat to this support is that due to the DecimalFormat library
    we use P/V symbols, which are used to indicate decimal point position in
    dfdl:textNumberPattern, are not supported and therefore we currently
    cannot support Zoned Decimal numbers that have a decimal point.
    
    DAFFODIL-1738
---
 .../daffodil/grammar/ElementBaseGrammarMixin.scala |  56 ++-
 .../grammar/primitives/PrimitivesTextNumber.scala  |   4 +-
 .../grammar/primitives/PrimitivesZoned.scala       | 197 +++++++-
 .../org/apache/daffodil/util/DecimalUtils.scala    | 154 +++++-
 .../apache/daffodil/util/TestDecimalUtils.scala    | 515 ++++++++++++++++++++-
 .../apache/daffodil/xsd/DFDL_part1_simpletypes.xsd |   1 +
 .../unparsers/ConvertZonedNumberUnparser.scala     |  72 +++
 .../processors/parsers/PrimitivesTextNumber1.scala |  34 +-
 .../processors/parsers/ZonedTextParsers.scala      |  92 +++-
 .../org/apache/daffodil/section13/zoned/zoned.tdml | 427 +++++++++++++++++
 .../daffodil/section13/zoned/TestZoned.scala       |  69 +++
 11 files changed, 1578 insertions(+), 43 deletions(-)

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
index 76c4be2..e83e40a 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
@@ -67,6 +67,18 @@ import org.apache.daffodil.grammar.primitives.ConvertTextUnsignedBytePrim
 import org.apache.daffodil.grammar.primitives.ConvertTextUnsignedIntPrim
 import org.apache.daffodil.grammar.primitives.ConvertTextUnsignedLongPrim
 import org.apache.daffodil.grammar.primitives.ConvertTextUnsignedShortPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedBytePrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedCombinator
+import org.apache.daffodil.grammar.primitives.ConvertZonedDecimalPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedIntPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedIntegerPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedLongPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedNonNegativeIntegerPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedShortPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedUnsignedBytePrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedUnsignedIntPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedUnsignedLongPrim
+import org.apache.daffodil.grammar.primitives.ConvertZonedUnsignedShortPrim
 import org.apache.daffodil.grammar.primitives.DelimiterStackCombinatorElement
 import org.apache.daffodil.grammar.primitives.DynamicEscapeSchemeCombinatorElement
 import org.apache.daffodil.grammar.primitives.ElementCombinator
@@ -112,7 +124,6 @@ import org.apache.daffodil.grammar.primitives.StringDelimitedEndOfData
 import org.apache.daffodil.grammar.primitives.StringOfSpecifiedLength
 import org.apache.daffodil.grammar.primitives.Terminator
 import org.apache.daffodil.grammar.primitives.TrailingSkipRegion
-import org.apache.daffodil.grammar.primitives.ZonedTextIntPrim
 import org.apache.daffodil.processors.TextJustificationType
 import org.apache.daffodil.schema.annotation.props.Found
 import org.apache.daffodil.schema.annotation.props.NotFound
@@ -552,43 +563,43 @@ trait ElementBaseGrammarMixin
   }
 
   private lazy val textByte = prod("textByte", impliedRepresentation == Representation.Text) {
-    standardTextByte || zonedTextInt
+    standardTextByte || zonedTextByte
   }
 
   private lazy val textShort = prod("textShort", impliedRepresentation == Representation.Text) {
-    standardTextShort || zonedTextInt
+    standardTextShort || zonedTextShort
   }
 
   private lazy val textLong = prod("textLong", impliedRepresentation == Representation.Text) {
-    standardTextLong || zonedTextInt
+    standardTextLong || zonedTextLong
   }
 
   private lazy val textInteger = prod("textInteger", impliedRepresentation == Representation.Text) {
-    standardTextInteger || zonedTextInt
+    standardTextInteger || zonedTextInteger
   }
 
   private lazy val textDecimal = prod("textDecimal", impliedRepresentation == Representation.Text) {
-    standardTextDecimal || zonedTextInt
+    standardTextDecimal || zonedTextDecimal
   }
 
   private lazy val textNonNegativeInteger = prod("textNonNegativeInteger", impliedRepresentation == Representation.Text) {
-    standardTextNonNegativeInteger || zonedTextInt
+    standardTextNonNegativeInteger || zonedTextNonNegativeInteger
   }
 
   private lazy val textUnsignedInt = prod("textUnsignedInt", impliedRepresentation == Representation.Text) {
-    standardTextUnsignedInt || zonedTextInt
+    standardTextUnsignedInt || zonedTextUnsignedInt
   }
 
   private lazy val textUnsignedByte = prod("textUnsignedByte", impliedRepresentation == Representation.Text) {
-    standardTextUnsignedByte || zonedTextInt
+    standardTextUnsignedByte || zonedTextUnsignedByte
   }
 
   private lazy val textUnsignedShort = prod("textUnsignedShort", impliedRepresentation == Representation.Text) {
-    standardTextUnsignedShort || zonedTextInt
+    standardTextUnsignedShort || zonedTextUnsignedShort
   }
 
   private lazy val textUnsignedLong = prod("textUnsignedLong", impliedRepresentation == Representation.Text) {
-    standardTextUnsignedLong || zonedTextInt
+    standardTextUnsignedLong || zonedTextUnsignedLong
   }
 
   //
@@ -617,8 +628,29 @@ trait ElementBaseGrammarMixin
     textNumberRep == TextNumberRep.Standard) { ConvertTextCombinator(this, stringValue, ConvertTextUnsignedShortPrim(this)) }
   private lazy val standardTextUnsignedByte = prod("standardTextUnsignedByte",
     textNumberRep == TextNumberRep.Standard) { ConvertTextCombinator(this, stringValue, ConvertTextUnsignedBytePrim(this)) }
+
+  private lazy val zonedTextInteger = prod("zonedTextInteger",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedIntegerPrim(this)) }
+  private lazy val zonedTextDecimal = prod("zonedTextDecimal",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedDecimalPrim(this)) }
+  private lazy val zonedTextNonNegativeInteger = prod("zonedTextNonNegativeInteger",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedNonNegativeIntegerPrim(this)) }
+  private lazy val zonedTextLong = prod("zonedTextLong",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedLongPrim(this)) }
   private lazy val zonedTextInt = prod("zonedTextInt",
-    textNumberRep == TextNumberRep.Zoned) { ZonedTextIntPrim(this) }
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedIntPrim(this)) }
+  private lazy val zonedTextShort = prod("zonedTextShort",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedShortPrim(this)) }
+  private lazy val zonedTextByte = prod("zonedTextByte",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedBytePrim(this)) }
+  private lazy val zonedTextUnsignedLong = prod("zonedTextUnsignedLong",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedUnsignedLongPrim(this)) }
+  private lazy val zonedTextUnsignedInt = prod("zonedTextUnsignedInt",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedUnsignedIntPrim(this)) }
+  private lazy val zonedTextUnsignedShort = prod("zonedTextUnsignedShort",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedUnsignedShortPrim(this)) }
+  private lazy val zonedTextUnsignedByte = prod("zonedTextUnsignedByte",
+    textNumberRep == TextNumberRep.Zoned) { ConvertZonedCombinator(this, stringValue, ConvertZonedUnsignedBytePrim(this)) }
 
   private lazy val textDouble = prod("textDouble", impliedRepresentation == Representation.Text) {
     standardTextDouble || zonedTextDouble
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesTextNumber.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesTextNumber.scala
index 7907797..84a9f1c 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesTextNumber.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesTextNumber.scala
@@ -132,7 +132,7 @@ abstract class ConvertTextNumberPrim[S](e: ElementBase)
       new NumberFormatFactoryStatic[S](e.termRuntimeData, h,
         decSep,
         groupSep,
-        e.textStandardExponentRepEv,
+        One(e.textStandardExponentRepEv),
         infRep,
         nanRep,
         e.textNumberCheckPolicy,
@@ -144,7 +144,7 @@ abstract class ConvertTextNumberPrim[S](e: ElementBase)
       new NumberFormatFactoryDynamic[S](e.termRuntimeData, h,
         decSep,
         groupSep,
-        e.textStandardExponentRepEv,
+        One(e.textStandardExponentRepEv),
         infRep,
         nanRep,
         e.textNumberCheckPolicy,
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala
index ecad042..6a8e684 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesZoned.scala
@@ -17,13 +17,196 @@
 
 package org.apache.daffodil.grammar.primitives
 
-import org.apache.daffodil.dsom.ElementBase
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.dpath.NodeInfo.PrimType
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.grammar.Terminal
+import com.ibm.icu.text.DecimalFormat
+import org.apache.daffodil.processors.unparsers.Unparser
+import org.apache.daffodil.processors.unparsers.ConvertZonedNumberUnparser
+import org.apache.daffodil.processors.unparsers.ConvertZonedCombinatorUnparser
+import org.apache.daffodil.processors.parsers.Parser
+import org.apache.daffodil.processors.parsers.ConvertZonedNumberParser
+import org.apache.daffodil.processors.parsers.ConvertZonedCombinatorParser
+import org.apache.daffodil.processors.parsers.ConvertTextByteParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextDecimalParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextIntParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextIntegerParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextLongParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextNonNegativeIntegerParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextNumberParserUnparserHelperBase
+import org.apache.daffodil.processors.parsers.ConvertTextShortParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextUnsignedByteParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextUnsignedLongParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextUnsignedShortParserUnparserHelper
+import org.apache.daffodil.processors.parsers.ConvertTextUnsignedIntParserUnparserHelper
+import org.apache.daffodil.processors.parsers.NumberFormatFactoryBase
+import org.apache.daffodil.processors.parsers.NumberFormatFactoryStatic
+import org.apache.daffodil.schema.annotation.props.gen.TextNumberCheckPolicy
+import org.apache.daffodil.schema.annotation.props.gen.TextNumberRounding
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.Maybe._
+import org.apache.daffodil.util.MaybeDouble
+import org.apache.daffodil.util.DecimalUtils.OverpunchLocation
 
-abstract class ZonedTextNumberPrim(e: ElementBase, guard: Boolean) extends UnimplementedPrimitive(e, guard) {
-  // lazy val parser: DaffodilParser = new ZonedTextNumberParser(e.elementRuntimeData)
+import java.math.{ BigDecimal => JBigDecimal }
+import java.math.{ BigInteger => JBigInt }
+
+case class ConvertZonedCombinator(e: ElementBase, value: Gram, converter: Gram)
+  extends Terminal(e, !(value.isEmpty || converter.isEmpty)) {
+
+  override lazy val parser = new ConvertZonedCombinatorParser(e.termRuntimeData, value.parser, converter.parser)
+
+  override lazy val unparser = new ConvertZonedCombinatorUnparser(e.termRuntimeData, value.unparser, converter.unparser)
 }
 
-case class ZonedTextBytePrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
-case class ZonedTextShortPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
-case class ZonedTextIntPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
-case class ZonedTextLongPrim(el: ElementBase) extends ZonedTextNumberPrim(el, false)
+abstract class ConvertZonedNumberPrim[S](e: ElementBase)
+  extends Terminal(e, true) {
+
+  def helper: ConvertTextNumberParserUnparserHelperBase[S]
+
+  def numFormatFactory: NumberFormatFactoryBase[S] = {
+    val h = helper
+
+    val pattern = {
+      val p = e.textNumberPattern
+
+      val noEscapedTicksRegex = """''""".r
+      val patternNoEscapedTicks = noEscapedTicksRegex.replaceAllIn(p, "")
+      val noQuotedRegex = """'[^']+'""".r
+      val patternNoQuoted = noQuotedRegex.replaceAllIn(patternNoEscapedTicks, "")
+      val zonedPatternRegex = "\\+?#*[0-9]+\\+?".r
+
+      if (patternNoQuoted.contains("V")) {
+        e.notYetImplemented("textNumberPattern with V symbol")
+      }
+
+      if (patternNoQuoted.contains("P")) {
+        e.notYetImplemented("textNumberPattern with P symbol")
+      }
+
+      if (patternNoQuoted.contains("@")) {
+        e.SDE("The '@' symbol may not be used in textNumberPattern for textNumberRep='zoned'")
+      }
+
+      if (patternNoQuoted.contains(";")) {
+        e.SDE("Negative patterns may not be used in textNumberPattern for textNumberRep='zoned'")
+      }
+
+      e.primType match {
+        case PrimType.Double | PrimType.Float => e.SDE("textZonedFormat='zoned' does not support Doubles/Floats")
+        case PrimType.UnsignedLong | PrimType.UnsignedInt | PrimType.UnsignedShort | PrimType.UnsignedByte => {
+          if (e.textNumberCheckPolicy == TextNumberCheckPolicy.Lax) {
+            if ((patternNoQuoted(0) != '+') && (patternNoQuoted(patternNoQuoted.length - 1) != '+'))
+              e.SDE("textNumberPattern must have '+' at the beginning or the end of the pattern when textZonedFormat='zoned' and textNumberPolicy='lax' for unsigned numbers")
+          }
+        }
+        case _ => {
+          if ((patternNoQuoted(0) != '+') && (patternNoQuoted(patternNoQuoted.length - 1) != '+'))
+            e.SDE("textNumberPattern must have '+' at the beginning or the end of the pattern when textZonedFormat='zoned' for signed numbers")
+        }
+      }
+
+      if ((patternNoQuoted(0) == '+') && (patternNoQuoted(patternNoQuoted.length - 1) == '+'))
+        e.SDE("The textNumberPattern may either begin or end with a '+', not both.")
+
+      if (!zonedPatternRegex.pattern.matcher(patternNoQuoted).matches)
+        e.SDE("Invalid characters used in textNubmerPattern for zoned number. Only the following characters may be used with zoned numbers: '+', 'V', 'P', '0-9', and '#'")
+
+      // Load the pattern to make sure it is valid
+      try {
+        new DecimalFormat(p.replace("+", ""))
+      } catch {
+        case ex: IllegalArgumentException => e.SDE("Invalid textNumberPattern: " + ex.getMessage())
+      }
+
+      p
+    }
+
+    /* Need to remove the '+' from the number pattern as '+' is only
+    *  used to indicate which digit of the number is overpunched when
+    *  dealing with zoned decimal formats. If the '+' is not removed
+    *  DecimalFormat will attempt to use it as an indicator for exponent
+    *  numbers, which will most likely not match the zoned number being
+    *  parsed */
+    val zonedPattern = pattern.replace("+", "")
+
+    val (roundingIncrement, roundingMode) =
+      e.textNumberRounding match {
+        case TextNumberRounding.Explicit => (MaybeDouble(e.textNumberRoundingIncrement), One(e.textNumberRoundingMode))
+        case TextNumberRounding.Pattern => (MaybeDouble.Nope, Nope)
+      }
+
+    val nff = new NumberFormatFactoryStatic[S](
+      e.termRuntimeData, h,
+      Maybe.Nope,
+      Maybe.Nope,
+      Maybe.Nope,
+      Maybe.Nope,
+      Maybe.Nope,
+      e.textNumberCheckPolicy,
+      zonedPattern,
+      e.textNumberRounding,
+      roundingMode,
+      roundingIncrement)
+    nff
+  }
+
+  val opindex = e.textNumberPattern.indexOf('+')
+  val opl = {
+    if (opindex == 0)
+      OverpunchLocation.Start
+    else if (opindex == e.textNumberPattern.length - 1)
+      OverpunchLocation.End
+    else
+      OverpunchLocation.None
+  }
+
+  lazy val parser: Parser = new ConvertZonedNumberParser[S](helper, opl, numFormatFactory, e.textZonedSignStyle, e.elementRuntimeData)
+
+  override lazy val unparser: Unparser = new ConvertZonedNumberUnparser[S](helper, opl, e.textZonedSignStyle, e.elementRuntimeData)
+}
+
+case class ConvertZonedIntegerPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigInt](e) {
+  val helper = new ConvertTextIntegerParserUnparserHelper[JBigInt](List(), false)
+}
+
+case class ConvertZonedDecimalPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigDecimal](e) {
+  val helper = new ConvertTextDecimalParserUnparserHelper[JBigDecimal](List(), false)
+}
+
+case class ConvertZonedNonNegativeIntegerPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigInt](e) {
+  val helper = new ConvertTextNonNegativeIntegerParserUnparserHelper[JBigDecimal](List(), false)
+}
+
+case class ConvertZonedLongPrim(e: ElementBase) extends ConvertZonedNumberPrim[Long](e) {
+  val helper = new ConvertTextLongParserUnparserHelper[Long](List(), false)
+}
+
+case class ConvertZonedIntPrim(e: ElementBase) extends ConvertZonedNumberPrim[Int](e) {
+  val helper = new ConvertTextIntParserUnparserHelper[Int](List(), false)
+}
+
+case class ConvertZonedShortPrim(e: ElementBase) extends ConvertZonedNumberPrim[Short](e) {
+  val helper = new ConvertTextShortParserUnparserHelper[Short](List(), false)
+}
+
+case class ConvertZonedBytePrim(e: ElementBase) extends ConvertZonedNumberPrim[Byte](e) {
+  val helper = new ConvertTextByteParserUnparserHelper[Byte](List(), false)
+}
+
+case class ConvertZonedUnsignedLongPrim(e: ElementBase) extends ConvertZonedNumberPrim[JBigInt](e) {
+  val helper = new ConvertTextUnsignedLongParserUnparserHelper[JBigInt](List(), false)
+}
+
+case class ConvertZonedUnsignedIntPrim(e: ElementBase) extends ConvertZonedNumberPrim[Long](e) {
+  val helper = ConvertTextUnsignedIntParserUnparserHelper[Long](List(), false)
+}
+
+case class ConvertZonedUnsignedShortPrim(e: ElementBase) extends ConvertZonedNumberPrim[Int](e) {
+  val helper = new ConvertTextUnsignedShortParserUnparserHelper[Int](List(), false)
+}
+
+case class ConvertZonedUnsignedBytePrim(e: ElementBase) extends ConvertZonedNumberPrim[Short](e) {
+  val helper = new ConvertTextUnsignedByteParserUnparserHelper[Short](List(), false)
+}
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala
index a6e452c..9acbac6 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/util/DecimalUtils.scala
@@ -19,6 +19,7 @@ package org.apache.daffodil.util
 
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.schema.annotation.props.gen.BinaryNumberCheckPolicy
+import org.apache.daffodil.schema.annotation.props.gen.TextZonedSignStyle
 
 import java.math.{ BigInteger => JBigInteger, BigDecimal => JBigDecimal }
 
@@ -111,7 +112,7 @@ object DecimalUtils {
   }
 
   def packedToBigDecimal(num: Array[Byte], scale: Int, signCodes: PackedSignCodes): JBigDecimal = {
-    return new JBigDecimal(packedToBigInteger(num, signCodes), scale)
+    new JBigDecimal(packedToBigInteger(num, signCodes), scale)
   }
 
   def packedFromBigInteger(bigInt: JBigInteger, minLengthInBits: Int, signCodes: PackedSignCodes): Array[Byte] = {
@@ -335,4 +336,155 @@ object DecimalUtils {
     outArray
   }
 
+  def convertFromAsciiStandard(digit: Char): (Int, Boolean) = {
+    if ((digit >= '0') && (digit <= '9')) // positive 0-9
+      (digit - 48, false)
+    else if ((digit >= 'p') && (digit <= 'y')) // negative 0-9
+      (digit - 112, true)
+    else
+      throw new NumberFormatException("Invalid zoned digit: " + digit)
+  }
+
+  def convertToAsciiStandard(digit: Char, positive: Boolean): Char = {
+    if (positive)
+      digit
+    else
+      (digit + 64).asInstanceOf[Char]
+  }
+
+  def convertFromAsciiTranslatedEBCDIC(digit: Char): (Int, Boolean) = {
+    if (digit == '{')
+      (0, false)
+    else if (digit == '}')
+      (0, true)
+    else if ((digit >= 'A') && (digit <= 'I')) // positive 1-9
+      (digit - 64, false)
+    else if ((digit >= 'J') && (digit <= 'R')) // negative 1-9
+      (digit - 73, true)
+    else if ((digit >= '0') && (digit <= '9'))
+      (digit - 48, false) // non-overpunched digit
+    else
+      throw new NumberFormatException("Invalid zoned digit: " + digit)
+  }
+
+  def convertToAsciiTranslatedEBCDIC(digit: Char, positive: Boolean): Char = {
+    if (positive) {
+      if (digit == '0')
+        '{'
+      else
+        (digit + 16).asInstanceOf[Char]
+    } else {
+      if (digit == '0')
+        '}'
+      else
+        (digit + 25).asInstanceOf[Char]
+    }
+  }
+
+  def convertFromAsciiCARealiaModified(digit: Char): (Int, Boolean) = {
+    if ((digit >= '0') && (digit <= '9')) // positive 0-9
+      (digit - 48, false)
+    else if ((digit >= ' ') && (digit <= ')')) // negative 0-9
+      (digit - 32, true)
+    else
+      throw new NumberFormatException("Invalid zoned digit: " + digit)
+  }
+
+  def convertToAsciiCARealiaModified(digit: Char, positive: Boolean): Char = {
+    if (positive)
+      digit
+    else
+      (digit - 16).asInstanceOf[Char]
+  }
+
+  def convertFromAsciiTandemModified(digit: Char): (Int, Boolean) = {
+    if ((digit >= '0') && (digit <= '9')) // positive 0-9
+      (digit - 48, false)
+    else if ((digit >= 128) && (digit <= 137)) // negative 0-9
+      (digit - 128, true)
+    else
+      throw new NumberFormatException("Invalid zoned digit: " + digit)
+  }
+
+  def convertToAsciiTandemModified(digit: Char, positive: Boolean): Char = {
+    if (positive)
+      digit
+    else
+      (digit + 80).asInstanceOf[Char]
+  }
+
+  object OverpunchLocation extends Enumeration {
+    type OverpunchLocation = Value
+    val Start, End, None = Value
+  }
+
+  def zonedToNumber(num: String, zonedStyle: TextZonedSignStyle, opl: OverpunchLocation.Value): String = {
+    val opindex = opl match {
+      case OverpunchLocation.Start => 0
+      case OverpunchLocation.End => num.length - 1
+      case _ => -1
+    }
+
+    val decodedValue = {
+      if (opl == OverpunchLocation.None) {
+        num
+      } else {
+        val (digit, opneg) = zonedStyle match {
+          case TextZonedSignStyle.AsciiStandard => convertFromAsciiStandard(num(opindex))
+          case TextZonedSignStyle.AsciiTranslatedEBCDIC => convertFromAsciiTranslatedEBCDIC(num(opindex))
+          case TextZonedSignStyle.AsciiCARealiaModified => convertFromAsciiCARealiaModified(num(opindex))
+          case TextZonedSignStyle.AsciiTandemModified => convertFromAsciiTandemModified(num(opindex))
+        }
+
+        val convertedNum = (opneg, opl) match {
+          case (true, OverpunchLocation.Start) => "-" + digit + num.substring(1)
+          case (false, OverpunchLocation.Start) => digit + num.substring(1)
+          case (true, OverpunchLocation.End) => "-" + num.substring(0, opindex) + digit
+          case (false, OverpunchLocation.End) => num.substring(0, opindex) + digit
+          case _ => Assert.impossible()
+        }
+
+        convertedNum
+      }
+    }
+
+    decodedValue
+  }
+
+  def zonedFromNumber(num: String, zonedStyle: TextZonedSignStyle, opl: OverpunchLocation.Value): String = {
+    val positive = (num.charAt(0) != '-')
+    val inStr = positive match {
+      case true => num
+      case false => num.substring(1)
+    }
+    val opindex = opl match {
+      case OverpunchLocation.Start => 0
+      case OverpunchLocation.End => inStr.length - 1
+      case _ => -1
+    }
+
+    val encodedValue = {
+      if (opl == OverpunchLocation.None) {
+        if (!positive) Assert.impossible()
+        inStr
+      } else {
+        val digit = zonedStyle match {
+          case TextZonedSignStyle.AsciiStandard => convertToAsciiStandard(inStr(opindex), positive)
+          case TextZonedSignStyle.AsciiTranslatedEBCDIC => convertToAsciiTranslatedEBCDIC(inStr(opindex), positive)
+          case TextZonedSignStyle.AsciiCARealiaModified => convertToAsciiCARealiaModified(inStr(opindex), positive)
+          case TextZonedSignStyle.AsciiTandemModified => convertToAsciiTandemModified(inStr(opindex), positive)
+        }
+
+        val convertedNum = opl match {
+          case OverpunchLocation.Start => digit + inStr.substring(1)
+          case OverpunchLocation.End => inStr.substring(0, opindex) + digit
+          case _ => Assert.impossible()
+        }
+
+        convertedNum
+      }
+    }
+
+    encodedValue
+  }
 }
diff --git a/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala b/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala
index 74cb96e..2f8b94b 100644
--- a/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala
+++ b/daffodil-lib/src/test/scala/org/apache/daffodil/util/TestDecimalUtils.scala
@@ -18,7 +18,7 @@
 package org.apache.daffodil.util
 
 import org.apache.daffodil.util.DecimalUtils._
-import org.apache.daffodil.schema.annotation.props.gen.BinaryNumberCheckPolicy
+import org.apache.daffodil.schema.annotation.props.gen.{ BinaryNumberCheckPolicy, TextZonedSignStyle }
 
 import java.math.{ BigInteger => JBigInteger, BigDecimal => JBigDecimal }
 import org.junit.Test
@@ -1249,4 +1249,517 @@ class TestDecimalUtils {
       case nfe: NumberFormatException => assertTrue(nfe.getMessage().contains("Invalid low nibble"))
     }
   }
+
+  @Test def zonedIntAsciiStandardPos1() {
+    val num = "1"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiStandardPos2() {
+    val num = "12"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.End)
+    System.out.println("Result: " + result)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiStandardPos3() {
+    val num = "123"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.None)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.None), num)
+  }
+
+  @Test def zonedIntAsciiStandardPos4() {
+    val num = "1234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiStandardPos5() {
+    val num = "000000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+    assertEquals(result, "000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "000000000001234567890")
+  }
+
+  @Test def zonedIntAsciiStandardNeg1() {
+    val num = "q"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiStandardNeg2() {
+    val num = "1r"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.End)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiStandardNeg3() {
+    val num = "q23"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiStandardNeg4() {
+    val num = "123456789p"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.End)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiStandardNeg5() {
+    val num = "p00000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+    assertEquals(result, "-000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiStandardInvalidDigit() {
+    val num = "z123"
+    try {
+      val result = zonedToNumber(num, TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start)
+      assertEquals(result, "-0123")
+    } catch {
+      case nfe: NumberFormatException => assertTrue(nfe.getMessage().contains("Invalid zoned digit"))
+    }
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICPos1() {
+    val num = "A"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICPos2() {
+    val num = "1B"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICPos3() {
+    val num = "123"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICPos4() {
+    val num = "A234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICPos5() {
+    val num = "00000000000123456789{"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End)
+    assertEquals(result, "000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICNeg1() {
+    val num = "J"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICNeg2() {
+    val num = "1K"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICNeg3() {
+    val num = "J23"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICNeg4() {
+    val num = "123456789}"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICNeg5() {
+    val num = "}00000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start)
+    assertEquals(result, "-000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICInvalidDigit() {
+    val num = "z123"
+    try {
+      val result = zonedToNumber(num, TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start)
+      assertEquals(result, "-0123")
+    } catch {
+      case nfe: NumberFormatException => assertTrue(nfe.getMessage().contains("Invalid zoned digit"))
+    }
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedPos1() {
+    val num = "1"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedPos2() {
+    val num = "12"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedPos3() {
+    val num = "123"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.None)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.None), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedPos4() {
+    val num = "1234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedPos5() {
+    val num = "000000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End)
+    assertEquals(result, "000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedNeg1() {
+    val num = "!"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedNeg2() {
+    val num = "1\""
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedNeg3() {
+    val num = "!23"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedNeg4() {
+    val num = "123456789 "
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedNeg5() {
+    val num = " 00000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start)
+    assertEquals(result, "-000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedInvalidDigit() {
+    val num = "z123"
+    try {
+      val result = zonedToNumber(num, TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start)
+      assertEquals(result, "-0123")
+    } catch {
+      case nfe: NumberFormatException => assertTrue(nfe.getMessage().contains("Invalid zoned digit"))
+    }
+  }
+
+  @Test def zonedIntAsciiTandemModifiedPos1() {
+    val num = "1"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start)
+    assertEquals(result, "1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedPos2() {
+    val num = "12"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End)
+    assertEquals(result, "12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedPos3() {
+    val num = "123"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.None)
+    assertEquals(result, "123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.None), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedPos4() {
+    val num = "1234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start)
+    assertEquals(result, "1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedPos5() {
+    val num = "000000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End)
+    assertEquals(result, "000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedNeg1() {
+    val num = ""
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start)
+    assertEquals(result, "-1")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedNeg2() {
+    val num = "1‚"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End)
+    assertEquals(result, "-12")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedNeg3() {
+    val num = "23"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start)
+    assertEquals(result, "-123")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedNeg4() {
+    val num = "123456789€"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End)
+    assertEquals(result, "-1234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedNeg5() {
+    val num = "€00000000001234567890"
+    val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start)
+    assertEquals(result, "-000000000001234567890")
+    assertEquals(zonedFromNumber(result, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), num)
+  }
+
+  @Test def zonedIntAsciiTandemModifiedInvalidDigit() {
+    val num = "z123"
+    try {
+      val result = zonedToNumber(num, TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start)
+      assertEquals(result, "-0123")
+    } catch {
+      case nfe: NumberFormatException => assertTrue(nfe.getMessage().contains("Invalid zoned digit"))
+    }
+  }
+
+  @Test def zonedIntAsciiStandardAllDigits() {
+    assertEquals(zonedToNumber("0", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "0")
+    assertEquals(zonedFromNumber("0", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "0")
+    assertEquals(zonedToNumber("1", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "1")
+    assertEquals(zonedFromNumber("1", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "1")
+    assertEquals(zonedToNumber("2", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "2")
+    assertEquals(zonedFromNumber("2", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "2")
+    assertEquals(zonedToNumber("3", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "3")
+    assertEquals(zonedFromNumber("3", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "3")
+    assertEquals(zonedToNumber("4", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "4")
+    assertEquals(zonedFromNumber("4", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "4")
+    assertEquals(zonedToNumber("5", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "5")
+    assertEquals(zonedFromNumber("5", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "5")
+    assertEquals(zonedToNumber("6", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "6")
+    assertEquals(zonedFromNumber("6", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "6")
+    assertEquals(zonedToNumber("7", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "7")
+    assertEquals(zonedFromNumber("7", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "7")
+    assertEquals(zonedToNumber("8", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "8")
+    assertEquals(zonedFromNumber("8", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "8")
+    assertEquals(zonedToNumber("9", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "9")
+    assertEquals(zonedFromNumber("9", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "9")
+    assertEquals(zonedToNumber("1p", TextZonedSignStyle.AsciiStandard, OverpunchLocation.End), "-10")
+    assertEquals(zonedFromNumber("-10", TextZonedSignStyle.AsciiStandard, OverpunchLocation.End), "1p")
+    assertEquals(zonedToNumber("q", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-1")
+    assertEquals(zonedFromNumber("-1", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "q")
+    assertEquals(zonedToNumber("r", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-2")
+    assertEquals(zonedFromNumber("-2", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "r")
+    assertEquals(zonedToNumber("s", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-3")
+    assertEquals(zonedFromNumber("-3", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "s")
+    assertEquals(zonedToNumber("t", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-4")
+    assertEquals(zonedFromNumber("-4", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "t")
+    assertEquals(zonedToNumber("u", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-5")
+    assertEquals(zonedFromNumber("-5", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "u")
+    assertEquals(zonedToNumber("v", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-6")
+    assertEquals(zonedFromNumber("-6", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "v")
+    assertEquals(zonedToNumber("w", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-7")
+    assertEquals(zonedFromNumber("-7", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "w")
+    assertEquals(zonedToNumber("x", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-8")
+    assertEquals(zonedFromNumber("-8", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "x")
+    assertEquals(zonedToNumber("y", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "-9")
+    assertEquals(zonedFromNumber("-9", TextZonedSignStyle.AsciiStandard, OverpunchLocation.Start), "y")
+  }
+
+  @Test def zonedIntAsciiTranslatedEBCDICAllDigits() {
+    assertEquals(zonedToNumber("0", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "0")
+    assertEquals(zonedFromNumber("0", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "0")
+    assertEquals(zonedToNumber("1", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "1")
+    assertEquals(zonedFromNumber("1", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "1")
+    assertEquals(zonedToNumber("2", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "2")
+    assertEquals(zonedFromNumber("2", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "2")
+    assertEquals(zonedToNumber("3", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "3")
+    assertEquals(zonedFromNumber("3", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "3")
+    assertEquals(zonedToNumber("4", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "4")
+    assertEquals(zonedFromNumber("4", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "4")
+    assertEquals(zonedToNumber("5", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "5")
+    assertEquals(zonedFromNumber("5", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "5")
+    assertEquals(zonedToNumber("6", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "6")
+    assertEquals(zonedFromNumber("6", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "6")
+    assertEquals(zonedToNumber("7", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "7")
+    assertEquals(zonedFromNumber("7", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "7")
+    assertEquals(zonedToNumber("8", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "8")
+    assertEquals(zonedFromNumber("8", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "8")
+    assertEquals(zonedToNumber("9", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "9")
+    assertEquals(zonedFromNumber("9", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.None), "9")
+    assertEquals(zonedToNumber("{", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "0")
+    assertEquals(zonedFromNumber("0", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "{")
+    assertEquals(zonedToNumber("A", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "1")
+    assertEquals(zonedFromNumber("1", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "A")
+    assertEquals(zonedToNumber("B", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "2")
+    assertEquals(zonedFromNumber("2", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "B")
+    assertEquals(zonedToNumber("C", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "3")
+    assertEquals(zonedFromNumber("3", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "C")
+    assertEquals(zonedToNumber("D", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "4")
+    assertEquals(zonedFromNumber("4", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "D")
+    assertEquals(zonedToNumber("E", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "5")
+    assertEquals(zonedFromNumber("5", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "E")
+    assertEquals(zonedToNumber("F", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "6")
+    assertEquals(zonedFromNumber("6", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "F")
+    assertEquals(zonedToNumber("G", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "7")
+    assertEquals(zonedFromNumber("7", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "G")
+    assertEquals(zonedToNumber("H", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "8")
+    assertEquals(zonedFromNumber("8", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "H")
+    assertEquals(zonedToNumber("I", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "9")
+    assertEquals(zonedFromNumber("9", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "I")
+    assertEquals(zonedToNumber("1}", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End), "-10")
+    assertEquals(zonedFromNumber("-10", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.End), "1}")
+    assertEquals(zonedToNumber("J", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-1")
+    assertEquals(zonedFromNumber("-1", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "J")
+    assertEquals(zonedToNumber("K", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-2")
+    assertEquals(zonedFromNumber("-2", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "K")
+    assertEquals(zonedToNumber("L", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-3")
+    assertEquals(zonedFromNumber("-3", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "L")
+    assertEquals(zonedToNumber("M", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-4")
+    assertEquals(zonedFromNumber("-4", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "M")
+    assertEquals(zonedToNumber("N", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-5")
+    assertEquals(zonedFromNumber("-5", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "N")
+    assertEquals(zonedToNumber("O", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-6")
+    assertEquals(zonedFromNumber("-6", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "O")
+    assertEquals(zonedToNumber("P", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-7")
+    assertEquals(zonedFromNumber("-7", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "P")
+    assertEquals(zonedToNumber("Q", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-8")
+    assertEquals(zonedFromNumber("-8", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "Q")
+    assertEquals(zonedToNumber("R", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "-9")
+    assertEquals(zonedFromNumber("-9", TextZonedSignStyle.AsciiTranslatedEBCDIC, OverpunchLocation.Start), "R")
+  }
+
+  @Test def zonedIntAsciiCARealiaModifiedAllDigits() {
+    assertEquals(zonedToNumber("0", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "0")
+    assertEquals(zonedFromNumber("0", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "0")
+    assertEquals(zonedToNumber("1", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "1")
+    assertEquals(zonedFromNumber("1", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "1")
+    assertEquals(zonedToNumber("2", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "2")
+    assertEquals(zonedFromNumber("2", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "2")
+    assertEquals(zonedToNumber("3", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "3")
+    assertEquals(zonedFromNumber("3", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "3")
+    assertEquals(zonedToNumber("4", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "4")
+    assertEquals(zonedFromNumber("4", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "4")
+    assertEquals(zonedToNumber("5", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "5")
+    assertEquals(zonedFromNumber("5", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "5")
+    assertEquals(zonedToNumber("6", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "6")
+    assertEquals(zonedFromNumber("6", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "6")
+    assertEquals(zonedToNumber("7", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "7")
+    assertEquals(zonedFromNumber("7", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "7")
+    assertEquals(zonedToNumber("8", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "8")
+    assertEquals(zonedFromNumber("8", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "8")
+    assertEquals(zonedToNumber("9", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "9")
+    assertEquals(zonedFromNumber("9", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "9")
+    assertEquals(zonedToNumber("1 ", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End), "-10")
+    assertEquals(zonedFromNumber("-10", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.End), "1 ")
+    assertEquals(zonedToNumber("!", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-1")
+    assertEquals(zonedFromNumber("-1", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "!")
+    assertEquals(zonedToNumber("\"", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-2")
+    assertEquals(zonedFromNumber("-2", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "\"")
+    assertEquals(zonedToNumber("#", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-3")
+    assertEquals(zonedFromNumber("-3", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "#")
+    assertEquals(zonedToNumber("$", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-4")
+    assertEquals(zonedFromNumber("-4", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "$")
+    assertEquals(zonedToNumber("%", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-5")
+    assertEquals(zonedFromNumber("-5", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "%")
+    assertEquals(zonedToNumber("&", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-6")
+    assertEquals(zonedFromNumber("-6", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "&")
+    assertEquals(zonedToNumber("'", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-7")
+    assertEquals(zonedFromNumber("-7", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "'")
+    assertEquals(zonedToNumber("(", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-8")
+    assertEquals(zonedFromNumber("-8", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "(")
+    assertEquals(zonedToNumber(")", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), "-9")
+    assertEquals(zonedFromNumber("-9", TextZonedSignStyle.AsciiCARealiaModified, OverpunchLocation.Start), ")")
+  }
+
+  @Test def zonedIntAsciiTandemModifiedAllDigits() {
+    assertEquals(zonedToNumber("0", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "0")
+    assertEquals(zonedFromNumber("0", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "0")
+    assertEquals(zonedToNumber("1", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "1")
+    assertEquals(zonedFromNumber("1", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "1")
+    assertEquals(zonedToNumber("2", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "2")
+    assertEquals(zonedFromNumber("2", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "2")
+    assertEquals(zonedToNumber("3", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "3")
+    assertEquals(zonedFromNumber("3", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "3")
+    assertEquals(zonedToNumber("4", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "4")
+    assertEquals(zonedFromNumber("4", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "4")
+    assertEquals(zonedToNumber("5", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "5")
+    assertEquals(zonedFromNumber("5", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "5")
+    assertEquals(zonedToNumber("6", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "6")
+    assertEquals(zonedFromNumber("6", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "6")
+    assertEquals(zonedToNumber("7", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "7")
+    assertEquals(zonedFromNumber("7", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "7")
+    assertEquals(zonedToNumber("8", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "8")
+    assertEquals(zonedFromNumber("8", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "8")
+    assertEquals(zonedToNumber("9", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "9")
+    assertEquals(zonedFromNumber("9", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "9")
+    assertEquals(zonedToNumber("1€", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End), "-10")
+    assertEquals(zonedFromNumber("-10", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.End), "1€")
+    assertEquals(zonedToNumber("", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-1")
+    assertEquals(zonedFromNumber("-1", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "")
+    assertEquals(zonedToNumber("‚", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-2")
+    assertEquals(zonedFromNumber("-2", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "‚")
+    assertEquals(zonedToNumber("ƒ", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-3")
+    assertEquals(zonedFromNumber("-3", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "ƒ")
+    assertEquals(zonedToNumber("„", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-4")
+    assertEquals(zonedFromNumber("-4", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "„")
+    assertEquals(zonedToNumber("…", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-5")
+    assertEquals(zonedFromNumber("-5", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "…")
+    assertEquals(zonedToNumber("†", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-6")
+    assertEquals(zonedFromNumber("-6", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "†")
+    assertEquals(zonedToNumber("‡", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-7")
+    assertEquals(zonedFromNumber("-7", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "‡")
+    assertEquals(zonedToNumber("ˆ", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-8")
+    assertEquals(zonedFromNumber("-8", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "ˆ")
+    assertEquals(zonedToNumber("‰", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "-9")
+    assertEquals(zonedFromNumber("-9", TextZonedSignStyle.AsciiTandemModified, OverpunchLocation.Start), "‰")
+  }
 }
diff --git a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part1_simpletypes.xsd b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part1_simpletypes.xsd
index 1d1635c..280c367 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part1_simpletypes.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/DFDL_part1_simpletypes.xsd
@@ -314,6 +314,7 @@
       <xsd:enumeration value="asciiStandard" />
       <xsd:enumeration value="asciiTranslatedEBCDIC" />
       <xsd:enumeration value="asciiCARealiaModified" />
+      <xsd:enumeration value="asciiTandemModified" />
     </xsd:restriction>
   </xsd:simpleType>
 
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertZonedNumberUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertZonedNumberUnparser.scala
new file mode 100644
index 0000000..9bdf0c7
--- /dev/null
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ConvertZonedNumberUnparser.scala
@@ -0,0 +1,72 @@
+/*
+ * 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.processors.unparsers
+
+import org.apache.daffodil.schema.annotation.props.gen.TextZonedSignStyle
+import org.apache.daffodil.processors._
+import org.apache.daffodil.util.DecimalUtils
+import org.apache.daffodil.util.DecimalUtils.OverpunchLocation
+import org.apache.daffodil.processors.parsers.ConvertTextNumberParserUnparserHelperBase
+
+case class ConvertZonedCombinatorUnparser(
+  rd: TermRuntimeData,
+  valueUnparser: Unparser,
+  converterUnparser: Unparser)
+  extends CombinatorUnparser(rd) {
+
+  override lazy val runtimeDependencies = Nil
+
+  override lazy val childProcessors = Seq(converterUnparser, valueUnparser)
+
+  override def unparse(state: UState): Unit = {
+    converterUnparser.unparse1(state)
+
+    if (state.processorStatus eq Success) {
+      valueUnparser.unparse1(state)
+    }
+  }
+}
+
+case class ConvertZonedNumberUnparser[S](
+  helper: ConvertTextNumberParserUnparserHelperBase[S],
+  opl: OverpunchLocation.Value,
+  zonedSignStyle: TextZonedSignStyle,
+  override val context: ElementRuntimeData)
+  extends PrimUnparser
+  with ToBriefXMLImpl {
+
+  override lazy val runtimeDependencies = Nil
+
+  override def toString = "to(xs:" + helper.xsdType + ")"
+  override lazy val childProcessors = Nil
+
+  override def unparse(state: UState): Unit = {
+
+    val node = state.currentInfosetNode.asSimple
+    val value = node.dataValueAsString
+
+    // The type of value should have the type of S, but type erasure makes this
+    // difficult to assert. Could probably check this with TypeTags or Manifest
+    // if we find this is not the case. Want something akin to:
+    // Assert.invariant(value.isInstanceOf[S])
+
+    val strRep = DecimalUtils.zonedFromNumber(value, zonedSignStyle, opl)
+
+    node.overwriteDataValue(strRep)
+  }
+}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesTextNumber1.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesTextNumber1.scala
index d9d6614..a2b933d 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesTextNumber1.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PrimitivesTextNumber1.scala
@@ -442,7 +442,7 @@ abstract class NumberFormatFactoryBase[S](parserHelper: ConvertTextNumberParserU
 
   protected def generateNumFormat(decimalSepList: Maybe[List[Character]],
     groupingSep: Maybe[Character],
-    exponentRep: String,
+    exponentRep: Maybe[String],
     infRep: Maybe[String],
     nanRep: Maybe[String],
     checkPolicy: TextNumberCheckPolicy,
@@ -463,7 +463,9 @@ abstract class NumberFormatFactoryBase[S](parserHelper: ConvertTextNumberParserU
     }
 
     // TODO: this is allowed to be case insenstive, ICU doesn't support that
-    dfs.setExponentSeparator(exponentRep)
+    if (exponentRep.isDefined) {
+      dfs.setExponentSeparator(exponentRep.get)
+    }
 
     if (infRep.isDefined) {
       // TODO: this is allowed to be case insensitive, ICU doesn't support that
@@ -557,7 +559,7 @@ class NumberFormatFactoryStatic[S](context: ThrowsSDE,
   parserHelper: ConvertTextNumberParserUnparserHelperBase[S],
   decimalSepExpEv: Maybe[Evaluatable[List[String]]],
   groupingSepExpEv: Maybe[Evaluatable[String]],
-  exponentRepExpEv: Evaluatable[String],
+  exponentRepExpEv: Maybe[Evaluatable[String]],
   infRep: Maybe[String],
   nanRep: Maybe[String],
   checkPolicy: TextNumberCheckPolicy,
@@ -566,9 +568,6 @@ class NumberFormatFactoryStatic[S](context: ThrowsSDE,
   roundingMode: Maybe[TextNumberRoundingMode],
   roundingIncrement: MaybeDouble)
   extends NumberFormatFactoryBase[S](parserHelper) {
-  Assert.invariant((!decimalSepExpEv.isDefined || decimalSepExpEv.get.isConstant) &&
-    (!groupingSepExpEv.isDefined || groupingSepExpEv.get.isConstant) &&
-    exponentRepExpEv.isConstant)
 
   val decSep =
     if (decimalSepExpEv.isEmpty) Nope else One {
@@ -582,17 +581,18 @@ class NumberFormatFactoryStatic[S](context: ThrowsSDE,
       getGroupingSep(gse, context)
     }
 
-  val expRep = {
-    Assert.invariant(exponentRepExpEv.isConstant)
-    getExponentRep(exponentRepExpEv.maybeConstant.get, context)
-  }
+  val expRep =
+    if (exponentRepExpEv.isEmpty) Nope else One {
+      Assert.invariant(exponentRepExpEv.get.isConstant)
+      getExponentRep(exponentRepExpEv.get.maybeConstant.get, context)
+    }
 
   val roundingInc: MaybeDouble = if (roundingIncrement.isEmpty) MaybeDouble.Nope else MaybeDouble { getRoundingIncrement(roundingIncrement.value, context) }
 
   checkUnique(
     decSep,
     groupSep,
-    One(expRep),
+    expRep,
     infRep,
     nanRep,
     parserHelper.zeroRepListRaw,
@@ -623,7 +623,7 @@ class NumberFormatFactoryDynamic[S](staticContext: ThrowsSDE,
   parserHelper: ConvertTextNumberParserUnparserHelperBase[S],
   decimalSepExpEv: Maybe[Evaluatable[List[String]]],
   groupingSepExpEv: Maybe[Evaluatable[String]],
-  exponentRepExpEv: Evaluatable[String],
+  exponentRepExpEv: Maybe[Evaluatable[String]],
   infRep: Maybe[String],
   nanRep: Maybe[String],
   checkPolicy: TextNumberCheckPolicy,
@@ -644,14 +644,14 @@ class NumberFormatFactoryDynamic[S](staticContext: ThrowsSDE,
       (a: String) => getGroupingSep(a, staticContext)
     }
 
-  val exponentRepCached: CachedDynamic[String, String] =
-    cacheConstantExpression(exponentRepExpEv) {
+  val exponentRepCached: Maybe[CachedDynamic[String, String]] =
+    cacheConstantExpressionMaybe(exponentRepExpEv) {
       (a: String) => getExponentRep(a, staticContext)
     }
 
   checkUnique(getStaticMaybe(decimalSepListCached),
     getStaticMaybe(groupingSepCached),
-    getStatic(exponentRepCached),
+    getStaticMaybe(exponentRepCached),
     infRep,
     nanRep,
     parserHelper.zeroRepListRaw,
@@ -675,7 +675,7 @@ class NumberFormatFactoryDynamic[S](staticContext: ThrowsSDE,
         }
     }
 
-    val exponentRep = evalWithConversion(state, exponentRepCached) {
+    val exponentRep = evalWithConversionMaybe(state, exponentRepCached) {
       (s: ParseOrUnparseState, c: String) =>
         {
           getExponentRep(c, s)
@@ -685,7 +685,7 @@ class NumberFormatFactoryDynamic[S](staticContext: ThrowsSDE,
     checkUnique(
       decimalSepList,
       groupingSep,
-      One(exponentRep),
+      exponentRep,
       infRep,
       nanRep,
       parserHelper.zeroRepListRaw,
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala
index d300cd8..3419351 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ZonedTextParsers.scala
@@ -17,14 +17,100 @@
 
 package org.apache.daffodil.processors.parsers
 
+import org.apache.daffodil.schema.annotation.props.gen.TextZonedSignStyle
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.exceptions.UnsuppressableException
+import org.apache.daffodil.util.DecimalUtils
+import org.apache.daffodil.util.DecimalUtils.OverpunchLocation
+import java.text.ParsePosition
+import java.lang.{ Number => JNumber }
+import org.apache.daffodil.infoset.DISimple
 import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors.TermRuntimeData
 
-class ZonedTextNumberParser(override val context: ElementRuntimeData)
-  extends TextPrimParser {
+case class ConvertZonedCombinatorParser(
+  rd: TermRuntimeData,
+  valueParser: Parser,
+  converterParser: Parser)
+  extends CombinatorParser(rd) {
 
   override lazy val runtimeDependencies = Nil
 
+  override lazy val childProcessors = Seq(valueParser, converterParser)
+
+  def parse(start: PState): Unit = {
+    valueParser.parse1(start)
+    if (start.processorStatus eq Success) {
+      converterParser.parse1(start)
+    }
+  }
+}
+
+case class ConvertZonedNumberParser[S](
+  helper: ConvertTextNumberParserUnparserHelperBase[S],
+  opl: OverpunchLocation.Value,
+  nff: NumberFormatFactoryBase[S],
+  zonedSignStyle: TextZonedSignStyle,
+  override val context: ElementRuntimeData) extends TextPrimParser {
+  override lazy val runtimeDependencies = Nil
+
+  override def toString = "to(xs:" + helper.xsdType + ")"
+
   def parse(start: PState): Unit = {
-    context.notYetImplemented("Zoned Numbers")
+    val node: DISimple = start.simpleElement
+    val str = node.dataValueAsString
+
+    Assert.invariant(str != null) // worst case it should be empty string. But not null.
+    if (str == "") {
+      PE(start, "Convert to %s (for xs:%s): Cannot parse number from empty string", helper.prettyType, helper.xsdType)
+      return
+    }
+
+    var checkLength = str.length
+    val numValue = {
+      val df = nff.getNumFormat(start)
+      val pos = new ParsePosition(0)
+      val num = try {
+        val decodedNum = DecimalUtils.zonedToNumber(str, zonedSignStyle, opl)
+        if (decodedNum(0) == '-')
+          checkLength = checkLength + 1
+        df.get.parse(decodedNum, pos)
+      } catch {
+        case s: scala.util.control.ControlThrowable => throw s
+        case u: UnsuppressableException => throw u
+        case e: Exception => {
+          PE(start, "Convert to %s (for xs:%s): Parse of '%s' threw exception %s",
+            helper.prettyType, helper.xsdType, str, e)
+          return
+        }
+      }
+
+      // Verify that what was parsed was what was passed exactly in byte count.
+      // Use pos to verify all characters consumed & check for errors!
+      if (num == null || pos.getIndex != checkLength) {
+        PE(start, "Convert to %s (for xs:%s): Unable to parse '%s' (using up all characters).",
+          helper.prettyType, helper.xsdType, str)
+        return
+      }
+
+      val numValue = {
+        if (helper.isInvalidRange(num)) {
+          PE(start, "Convert to %s (for xs:%s): Out of Range: '%s' converted to %s, is not in range for the type.",
+            helper.prettyType, helper.xsdType, str, num)
+          return
+        }
+
+        // convert to proper type
+        val asNumber = helper.getNum(num)
+        Assert.invariant(!asNumber.isInstanceOf[String])
+
+        asNumber
+      }
+      numValue
+    }
+
+    node.overwriteDataValue(numValue.asInstanceOf[JNumber])
+
   }
 }
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section13/zoned/zoned.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section13/zoned/zoned.tdml
new file mode 100644
index 0000000..7a79dea
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section13/zoned/zoned.tdml
@@ -0,0 +1,427 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+
+<tdml:testSuite suiteName="zoned"
+  description="Zoned Number Properties" xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ct="http://w3.ibm.com/xmlns/dfdl/ctInfoset"
+  xmlns:ex="http://example.com" xmlns="http://example.com" xmlns:tns="http://example.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+  defaultRoundTrip="true">
+
+  <tdml:defineSchema name="s1">
+    <dfdl:format ref="ex:GeneralFormat" lengthKind="explicit" encoding="ISO-8859-1" occursCountKind="implicit"
+      textNumberCheckPolicy="strict" textNumberPadCharacter="0" textNumberJustification="right" textNumberRep="zoned"
+      textZonedSignStyle="asciiStandard"
+    lengthUnits="characters"/>
+
+    <xs:element name="int01" type="xs:int" dfdl:length="4" dfdl:textNumberPattern="+00@00" />
+    <xs:element name="int02" type="xs:int" dfdl:length="4" dfdl:textNumberPattern="0000" />
+    <xs:element name="int03" type="xs:int" dfdl:length="4" dfdl:textNumberPattern="+00E00" />
+    <xs:element name="uint01" type="xs:unsignedInt" dfdl:length="4" dfdl:textNumberPattern="0000" dfdl:textNumberCheckPolicy="lax"/>
+    <xs:element name="int04" type="xs:int" dfdl:length="4" dfdl:textNumberPattern="+00;00" />
+    <xs:element name="int05" type="xs:int" dfdl:length="4" dfdl:textNumberPattern="+0000+" />
+    <xs:element name="float01" type="xs:float" dfdl:length="4" dfdl:textNumberPattern="+00;00" />
+    <xs:element name="double01" type="xs:double" dfdl:length="4" dfdl:textNumberPattern="+00;00" />
+
+
+    <xs:element name="standard01" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="+0000000000" />
+    <xs:element name="standard02" type="xs:unsignedInt" dfdl:length="10" dfdl:textNumberPattern="0000000000" />
+    <xs:element name="standard03" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="0000000000+" />
+
+    <xs:element name="trans01" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="+0000000000" dfdl:textZonedSignStyle="asciiTranslatedEBCDIC" />
+    <xs:element name="trans02" type="xs:unsignedInt" dfdl:length="10" dfdl:textNumberPattern="0000000000" dfdl:textZonedSignStyle="asciiTranslatedEBCDIC" />
+    <xs:element name="trans03" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="0000000000+" dfdl:textZonedSignStyle="asciiTranslatedEBCDIC" />
+<xs:element name="mod01" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="+0000000000" dfdl:textZonedSignStyle="asciiCARealiaModified" />
+    <xs:element name="mod02" type="xs:unsignedInt" dfdl:length="10" dfdl:textNumberPattern="0000000000" dfdl:textZonedSignStyle="asciiCARealiaModified" />
+    <xs:element name="mod03" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="0000000000+" dfdl:textZonedSignStyle="asciiCARealiaModified" />
+
+    <xs:element name="tmod01" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="+0000000000" dfdl:textZonedSignStyle="asciiTandemModified" />
+    <xs:element name="tmod02" type="xs:unsignedInt" dfdl:length="10" dfdl:textNumberPattern="0000000000" dfdl:textZonedSignStyle="asciiTandemModified" />
+    <xs:element name="tmod03" type="xs:int" dfdl:length="10" dfdl:textNumberPattern="0000000000+" dfdl:textZonedSignStyle="asciiTandemModified" />
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="zoned_textNumberPattern_fail01" root="int01" model="s1"
+    description="This test should fail because '@' is not allowed to appear in the textNumberPattern for zoned numbers">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>The '@' symbol may not be used</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_textNumberPattern_fail02" root="int02" model="s1"
+    description="This test should fail because '+' must be the first or last character in the textNumberPattern when using zoned numbers with signed types">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>textNumberPattern must have '+' at the beginning</tdml:error>
+      <tdml:error>for signed numbers</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_textNumberPattern_fail03" root="uint01" model="s1"
+    description="This test should fail because '+' must be the first or last character in the textNumberPattern when using zoned numbers and textNumberCheckPolicy=strict">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>textNumberPattern must have '+' at the beginning</tdml:error>
+      <tdml:error>for unsigned numbers</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_textNumberPattern_fail04" root="int04" model="s1"
+    description="This test should fail because negative number patterns may not be used with zoned numbers">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Negative patterns may not be used</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_textNumberPattern_fail05" root="int03" model="s1"
+    description="This test should fail because 'E' is not an allowed character in the textNumberPattern for zoned numbers">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Invalid characters used in textNubmerPattern for zoned number</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_textNumberPattern_fail06" root="int05" model="s1"
+    description="This test should fail because '+' is not allowed to be at both the start and end of the textNumberPattern">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>The textNumberPattern may either begin or end with a '+', not both</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_float_fail01" root="float01" model="s1"
+    description="This test should fail because floats are not supported with zoned decimal">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Zoned not supported for float and double</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="zoned_double_fail01" root="double01" model="s1"
+    description="This test should fail because doubles are not supported with zoned decimal">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1988</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>Zoned not supported for float and double</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedStandard01" root="standard01" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiStandard">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <standard01>1234567890</standard01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedStandard02" root="standard01" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiStandard with overpunch on first char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">q234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <standard01>-1234567890</standard01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedStandard03" root="standard01" model="s1"
+    description="This test should fail as 'z' is not a valid digit in zoned AsciiStandard">
+
+    <tdml:document>
+      <tdml:documentPart type="text">z234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>NumberFormatException: Invalid zoned digit</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedStandard04" root="standard02" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiStandard with no overpunch char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <standard02>1234567890</standard02>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedStandard05" root="standard03" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiStandard with overpunch on last char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">123456789p</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <standard03>-1234567890</standard03>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTranslatedEBCDIC01" root="trans01" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiTranslatedEBCDIC">
+
+    <tdml:document>
+      <tdml:documentPart type="text">A234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <trans01>1234567890</trans01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTranslatedEBCDIC02" root="trans01" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiTranslatedEBCDIC with overpunch on first char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">J234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <trans01>-1234567890</trans01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTranslatedEBCDIC03" root="trans01" model="s1"
+    description="This test should fail as 'z' is not a valid digit in zoned AsciiTranslatedEBCDIC">
+
+    <tdml:document>
+      <tdml:documentPart type="text">z234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>NumberFormatException: Invalid zoned digit</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTranslatedEBCDIC04" root="trans02" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiTranslatedEBCDIC with no overpunch char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <trans02>1234567890</trans02>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTranslatedEBCDIC05" root="trans03" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiTranslatedEBCDIC with overpunch on last char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">123456789}</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <trans03>-1234567890</trans03>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedCARealiaModified01" root="mod01" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiCARealiaModified">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <mod01>1234567890</mod01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedCARealiaModified02" root="mod01" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiCARealiaModified with overpunch on first char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">!234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <mod01>-1234567890</mod01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedCARealiaModified03" root="mod01" model="s1"
+    description="This test should fail as 'z' is not a valid digit in zoned AsciiCARealiaModified">
+
+    <tdml:document>
+      <tdml:documentPart type="text">z234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>NumberFormatException: Invalid zoned digit</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedCARealiaModified04" root="mod02" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiCARealiaModified with no overpunch char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <mod02>1234567890</mod02>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedCARealiaModified05" root="mod03" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiCARealiaModified with overpunch on last char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">123456789 </tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <mod03>-1234567890</mod03>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTandemModified01" root="tmod01" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiTandemModified">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tmod01>1234567890</tmod01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTandemModified02" root="tmod01" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiTandemModified with overpunch on first char">
+
+    <tdml:document>
+      <tdml:documentPart type="text" encoding="ISO-8859-1">&#x81;234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tmod01>-1234567890</tmod01>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTandemModified03" root="tmod01" model="s1"
+    description="This test should fail as 'z' is not a valid digit in zoned AsciiTandemModified">
+
+    <tdml:document>
+      <tdml:documentPart type="text">z234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>NumberFormatException: Invalid zoned digit</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTandemModified04" root="tmod02" model="s1"
+    description="Test of a positive number with zonedSignStyle=asciiTandemModified with no overpunch char">
+
+    <tdml:document>
+      <tdml:documentPart type="text">1234567890</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tmod02>1234567890</tmod02>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="ZonedTandemModified05" root="tmod03" model="s1"
+    description="Test of a negative number with zonedSignStyle=asciiTandemModified with overpunch on last char">
+
+    <tdml:document>
+      <tdml:documentPart type="text" encoding="ISO-8859-1">123456789&#x80;</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <tmod03>-1234567890</tmod03>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+
+  </tdml:parserTestCase>
+</tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section13/zoned/TestZoned.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section13/zoned/TestZoned.scala
new file mode 100644
index 0000000..73f9ace
--- /dev/null
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section13/zoned/TestZoned.scala
@@ -0,0 +1,69 @@
+/*
+ * 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.section13.zoned
+
+import org.apache.daffodil.tdml.Runner
+import org.junit.AfterClass
+import org.junit.Test
+
+object TestZoned {
+  val testDir = "/org/apache/daffodil/section13/zoned/"
+  lazy val runner = Runner(testDir, "zoned.tdml")
+
+  @AfterClass def shutdown(): Unit = {
+    runner.reset
+  }
+
+}
+
+class TestZoned {
+  import TestZoned._
+
+  @Test def testZonedPatternFail01(): Unit = { runner.runOneTest("zoned_textNumberPattern_fail01") }
+  @Test def testZonedPatternFail02(): Unit = { runner.runOneTest("zoned_textNumberPattern_fail02") }
+  @Test def testZonedPatternFail03(): Unit = { runner.runOneTest("zoned_textNumberPattern_fail03") }
+  @Test def testZonedPatternFail04(): Unit = { runner.runOneTest("zoned_textNumberPattern_fail04") }
+  @Test def testZonedPatternFail05(): Unit = { runner.runOneTest("zoned_textNumberPattern_fail05") }
+  @Test def testZonedPatternFail06(): Unit = { runner.runOneTest("zoned_textNumberPattern_fail06") }
+  @Test def testZonedFloatFail01(): Unit = { runner.runOneTest("zoned_float_fail01") }
+  @Test def testZonedDoubleFail01(): Unit = { runner.runOneTest("zoned_double_fail01") }
+
+  @Test def testZonedStandard01(): Unit = { runner.runOneTest("ZonedStandard01") }
+  @Test def testZonedStandard02(): Unit = { runner.runOneTest("ZonedStandard02") }
+  @Test def testZonedStandard03(): Unit = { runner.runOneTest("ZonedStandard03") }
+  @Test def testZonedStandard04(): Unit = { runner.runOneTest("ZonedStandard04") }
+  @Test def testZonedStandard05(): Unit = { runner.runOneTest("ZonedStandard05") }
+
+  @Test def testZonedTranslatedEBCDIC01(): Unit = { runner.runOneTest("ZonedTranslatedEBCDIC01") }
+  @Test def testZonedTranslatedEBCDIC02(): Unit = { runner.runOneTest("ZonedTranslatedEBCDIC02") }
+  @Test def testZonedTranslatedEBCDIC03(): Unit = { runner.runOneTest("ZonedTranslatedEBCDIC03") }
+  @Test def testZonedTranslatedEBCDIC04(): Unit = { runner.runOneTest("ZonedTranslatedEBCDIC04") }
+  @Test def testZonedTranslatedEBCDIC05(): Unit = { runner.runOneTest("ZonedTranslatedEBCDIC05") }
+
+  @Test def testZonedCARealiaModified01(): Unit = { runner.runOneTest("ZonedCARealiaModified01") }
+  @Test def testZonedCARealiaModified02(): Unit = { runner.runOneTest("ZonedCARealiaModified02") }
+  @Test def testZonedCARealiaModified03(): Unit = { runner.runOneTest("ZonedCARealiaModified03") }
+  @Test def testZonedCARealiaModified04(): Unit = { runner.runOneTest("ZonedCARealiaModified04") }
+  @Test def testZonedCARealiaModified05(): Unit = { runner.runOneTest("ZonedCARealiaModified05") }
+
+  @Test def testZonedTandemModified01(): Unit = { runner.runOneTest("ZonedTandemModified01") }
+  @Test def testZonedTandemModified02(): Unit = { runner.runOneTest("ZonedTandemModified02") }
+  @Test def testZonedTandemModified03(): Unit = { runner.runOneTest("ZonedTandemModified03") }
+  @Test def testZonedTandemModified04(): Unit = { runner.runOneTest("ZonedTandemModified04") }
+  @Test def testZonedTandemModified05(): Unit = { runner.runOneTest("ZonedTandemModified05") }
+}