Add support for day count dates, RFC 8943 (#106)

This addresses #98 
diff --git a/README.md b/README.md
index ecc93df..7a55e3e 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,14 @@
 # QCBOR
 
-QCBOR is a powerful, commercial quality implementation of nearly everything in
-[RFC8949](https://tools.ietf.org/html/rfc8949). This RFC defines the
-Concise Binary Object Representation (CBOR). Since RFC 8949 is fully
-compatible with RFC 7049, this is also a near-complete implementation
-of it.
+QCBOR is a powerful, commercial-quality CBOR encoder/decoder that
+completely implements these RFCs except as noted:
 
-QCBOR also implements [RFC8742](https://tools.ietf.org/html/rfc8742)
-which defines a CBOR sequence.
+* [RFC8949](https://tools.ietf.org/html/rfc8949) The CBOR Standard. _Everything_
+except sorting of encoded maps is implemented.
+* [RFC7049](https://tools.ietf.org/html/rfc7049) The previous CBOR standard.
+Replaced by RFC 8949.
+* [RFC8742](https://tools.ietf.org/html/rfc8742) CBOR Sequences
+* [RFC8943](https://tools.ietf.org/html/rfc8943) CBOR Dates
 
 ## New Version With Spiffy Decode
 
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 6883e1b..7e52a26 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -204,12 +204,20 @@
     (https://tools.ietf.org/html/rfc8152). No API is provided for this
     tag. */
 #define CBOR_TAG_SIGN          98
+/** Tag for date counted by days from Jan 1 1970 per [RFC 8943]
+    (https://tools.ietf.org/html/rfc8943). See
+    QCBOREncode_AddTDaysEpoch(). */
+#define CBOR_TAG_DAYS_EPOCH    100
 /** Not Decoded by QCBOR. World geographic coordinates. See ISO 6709, [RFC 5870]
     (https://tools.ietf.org/html/rfc5870) and WGS-84. No API is
     provided for this tag. */
 #define CBOR_TAG_GEO_COORD     103
 /** Binary MIME.*/
 #define CBOR_TAG_BINARY_MIME   257
+/** Tag for date string without time or time zone per [RFC 8943]
+    (https://tools.ietf.org/html/rfc8943). See
+    QCBOREncode_AddTDaysString(). */
+#define CBOR_TAG_DAYS_STRING   1004
 /** The magic number, self-described CBOR. No API is provided for this
     tag. */
 #define CBOR_TAG_CBOR_MAGIC    55799
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 94126bd..a1a64b9 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -204,8 +204,6 @@
     being traversed as an array. See QCBORDecode_Init() */
 #define QCBOR_TYPE_MAP_AS_ARRAY  32
 
-/* Start of QCBOR types that are defined as the CBOR tag + 12 */
-
 /** Encoded CBOR that is wrapped in a byte string. Often used when the
     CBOR is to be hashed for signing or HMAC. See also @ref
     QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE. Data is in @c val.string. */
@@ -236,12 +234,20 @@
     QBCOR_TYPE_WRAPPED_CBOR.  Data is in @c val.string. */
 #define QBCOR_TYPE_WRAPPED_CBOR_SEQUENCE  75
 
-/* End of QCBOR types that are CBOR tag + 12 */
-
 /** Binary MIME per RFC 2045. See also @ref QCBOR_TYPE_MIME. Data is
     in @c val.string. */
 #define QCBOR_TYPE_BINARY_MIME   76
 
+/** Type for [RFC 8943](https://tools.ietf.org/html/rfc8943) date
+    string, a date with no time or time zone info. Data is in
+    @c val.string */
+#define QCBOR_TYPE_DAYS_STRING   77
+
+/** Type for integer days since Jan 1 1970 described in
+    [RFC 8943](https://tools.ietf.org/html/rfc8943). Data is in
+    @c val.epochDays */
+#define QCBOR_TYPE_DAYS_EPOCH    78
+
 #define QCBOR_TYPE_TAG        254 // Used internally; never returned
 
 #define QCBOR_TYPE_OPTTAG   QCBOR_TYPE_TAG // Depricated in favor of QCBOR_TYPE_TAG
@@ -290,7 +296,8 @@
       /** The value for uDataType @ref QCBOR_TYPE_UINT64. */
       uint64_t    uint64;
       /** The value for @c uDataType @ref QCBOR_TYPE_BYTE_STRING and
-          @ref QCBOR_TYPE_TEXT_STRING. */
+          @ref QCBOR_TYPE_TEXT_STRING. Also
+          @ref QCBOR_TYPE_DAYS_STRING. */
       UsefulBufC  string;
       /** The "value" for @c uDataType @ref QCBOR_TYPE_ARRAY or @ref
           QCBOR_TYPE_MAP -- the number of items in the array or map.
@@ -311,8 +318,14 @@
          int64_t  nSeconds;
          double   fSecondsFraction;
       } epochDate;
+
+      /** The value for @c uDataType @ref QCBOR_TYPE_DAYS_EPOCH -- the
+          number of days before or after Jan 1, 1970. */
+      int64_t     epochDays;
+
       /** The value for @c uDataType @ref QCBOR_TYPE_DATE_STRING. */
       UsefulBufC  dateString;
+
       /** The value for @c uDataType @ref QCBOR_TYPE_POSBIGNUM and
            @ref QCBOR_TYPE_NEGBIGNUM. */
       UsefulBufC  bigNum;
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index ba654ee..016226b 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -730,7 +730,7 @@
  this is always UTC and does not include the time zone.  Use
  QCBOREncode_AddDateString() if you want to include the time zone.
 
- The integer encoding rules apply here so the date will be encoded in
+ The preferred integer encoding rules apply here so the date will be encoded in
  a minimal number of bytes. Until about the year 2106 these dates will
  encode in 6 bytes -- one byte for the tag, one byte for the type and
  4 bytes for the integer. After that it will encode to 10 bytes.
@@ -743,10 +743,11 @@
 
  This implementation cannot encode fractional seconds using float or
  double even though that is allowed by CBOR, but you can encode them
- if you want to by calling QCBOREncode_AddDouble() and
- QCBOREncode_AddTag().
+ if you want to by calling QCBOREncode_AddTag() and QCBOREncode_AddDouble().
 
  Error handling is the same as QCBOREncode_AddInt64().
+
+ See also QCBOREncode_AddTDaysEpoch().
  */
 static void QCBOREncode_AddTDateEpoch(QCBOREncodeContext *pCtx,
                                       uint8_t             uTagRequirement,
@@ -775,6 +776,43 @@
                                            int64_t             nDate);
 
 
+
+/**
+  @brief  Add an epoch-based day-count date.
+
+  @param[in] pCtx             The encoding context to add the date to.
+  @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
+                              @ref QCBOR_ENCODE_AS_BORROWED.
+  @param[in] nDays            Number of days before or after 1970-01-0.
+
+ This date format is described in
+ [RFC 8943] (https://tools.ietf.org/html/rfc8943).
+
+ The integer encoding rules apply here so the date will be encoded in
+ a minimal number of bytes. Until about the year 2149 these dates will
+ encode in 4 bytes -- one byte for the tag, one byte for the type and
+ 2 bytes for the integer.
+
+ See also QCBOREncode_AddTDateEpoch().
+
+*/
+static void QCBOREncode_AddTDaysEpoch(QCBOREncodeContext *pCtx,
+                                      uint8_t             uTagRequirement,
+                                      int64_t             nDays);
+
+static void QCBOREncode_AddTDaysEpochToMapSZ(QCBOREncodeContext *pCtx,
+                                             const char         *szLabel,
+                                             uint8_t             uTagRequirement,
+                                             int64_t             nDays);
+
+static void QCBOREncode_AddTDaysEpochToMapN(QCBOREncodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            uint8_t             uTagRequirement,
+                                            int64_t             nDays);
+
+
+
+
 /**
  @brief Add a byte string to the encoded output.
 
@@ -1403,6 +1441,8 @@
  CBOR will be incorrect and the receiver may not be able to handle it.
 
  Error handling is the same as QCBOREncode_AddInt64().
+
+ See also QCBOREncode_AddTDayString().
  */
 static void QCBOREncode_AddTDateString(QCBOREncodeContext *pCtx,
                                        uint8_t             uTagRequirement,
@@ -1430,6 +1470,50 @@
                                             int64_t             nLabel,
                                             const char         *szDate);
 
+
+/**
+ @brief  Add a date-only string.
+
+ @param[in] pCtx             The encoding context to add the date to.
+ @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
+                             @ref QCBOR_ENCODE_AS_BORROWED.
+ @param[in] szDate           Null-terminated string with date to add.
+
+ This date format is described in
+ [RFC 8943] (https://tools.ietf.org/html/rfc8943), but that mainly
+ references RFC 3339.  The string szDate must be in the forrm
+ specified the ABNF for a full-date in
+ [RFC 3339] (https://tools.ietf.org/html/rfc3339). Examples of this
+ are "1985-04-12" and "1937-01-01".  The time and the time zone are
+ never included.
+
+ Note that this function doesn't validate the format of the date
+ string at all. If you add an incorrect format date string, the
+ generated CBOR will be incorrect and the receiver may not be able to
+ handle it.
+
+ Error handling is the same as QCBOREncode_AddInt64().
+
+ See also QCBOREncode_AddTDateString().
+ */
+static void
+QCBOREncode_AddTDaysString(QCBOREncodeContext *pCtx,
+                           uint8_t             uTagRequirement,
+                           const char         *szDate);
+
+static void
+QCBOREncode_AddTDaysStringToMapSZ(QCBOREncodeContext *pCtx,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  const char         *szDate);
+
+static void
+QCBOREncode_AddTDaysStringToMapN(QCBOREncodeContext *pCtx,
+                                 int64_t             nLabel,
+                                 uint8_t             uTagRequirement,
+                                 const char         *szDate);
+
+
 /**
  @brief  Add a standard Boolean.
 
@@ -2214,6 +2298,29 @@
 }
 
 
+static inline void
+QCBOREncode_AddTDaysEpoch(QCBOREncodeContext *pMe, uint8_t uTag, int64_t nDays)
+{
+   if(uTag == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_DAYS_EPOCH);
+   }
+   QCBOREncode_AddInt64(pMe, nDays);
+}
+
+static inline void
+QCBOREncode_AddTDaysEpochToMapSZ(QCBOREncodeContext *pMe, const char *szLabel, uint8_t uTag, int64_t nDays)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTDaysEpoch(pMe, uTag, nDays);
+}
+
+static inline void
+QCBOREncode_AddTDaysEpochToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTag, int64_t nDays)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTDaysEpoch(pMe, uTag, nDays);
+}
+
 
 static inline void
 QCBOREncode_AddBytes(QCBOREncodeContext *pMe, UsefulBufC Bytes)
@@ -2957,6 +3064,34 @@
 
 
 static inline void
+QCBOREncode_AddTDaysString(QCBOREncodeContext *pMe, uint8_t uTagRequirement, const char *szDate)
+{
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_DAYS_STRING);
+   }
+   QCBOREncode_AddSZString(pMe, szDate);
+}
+
+static inline void
+QCBOREncode_AddTDaysStringToMapSZ(QCBOREncodeContext *pMe,
+                                  const char         *szLabel,
+                                  uint8_t             uTagRequirement,
+                                  const char         *szDate)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTDaysString(pMe, uTagRequirement, szDate);
+}
+
+static inline void
+QCBOREncode_AddTDaysStringToMapN(QCBOREncodeContext *pMe, int64_t nLabel, uint8_t uTagRequirement, const char *szDate)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTDaysString(pMe, uTagRequirement, szDate);
+}
+
+
+
+static inline void
 QCBOREncode_AddSimple(QCBOREncodeContext *pMe, uint64_t uNum)
 {
    QCBOREncode_AddType7(pMe, 0, uNum);
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index 3e69221..5c28c7e 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -991,7 +991,7 @@
 
  @param[in] pCtx             The decode context.
  @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
- @param[out] pDateString            The decoded URI.
+ @param[out] pDateString     The decoded date.
 
  This decodes the standard CBOR date/time string tag, integer tag
  number of 0, or encoded CBOR that is not a tag, but borrows the
@@ -1020,6 +1020,40 @@
 
 
 /**
+ @brief Decode the next item as a date-only string.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pDateString     The decoded date.
+
+ This decodes the CBOR date-only string tag, integer tag
+ number of 1004, or encoded CBOR that is not a tag, but borrows the
+ date-only string content format. An example of the format
+ is "1985-04-12".
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_DAYS_STRING, QCBOREncode_AddDaysString() and
+ @ref QCBOR_TYPE_DAYS_STRING.
+*/
+static void QCBORDecode_GetDaysString(QCBORDecodeContext *pCtx,
+                                      uint8_t             uTagRequirement,
+                                      UsefulBufC         *pDateString);
+
+static void QCBORDecode_GetDaysStringInMapN(QCBORDecodeContext *pCtx,
+                                            int64_t             nLabel,
+                                            uint8_t             uTagRequirement,
+                                            UsefulBufC         *pDateString);
+
+static void QCBORDecode_GetDaysStringInMapSZ(QCBORDecodeContext *pCtx,
+                                             const char         *szLabel,
+                                             uint8_t             uTagRequirement,
+                                             UsefulBufC         *pDateString);
+
+
+/**
  @brief Decode the next item as an epoch date.
 
  @param[in] pCtx             The decode context.
@@ -1063,6 +1097,40 @@
                                      int64_t            *pnTime);
 
 
+/**
+ @brief Decode the next item as an days-count epoch date.
+
+ @param[in] pCtx             The decode context.
+ @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ @param[out] pnDays          The decoded epoch date.
+
+ This decodes the CBOR epoch date tag, integer tag number of 100, or
+ encoded CBOR that is not a tag, but borrows the content format. The
+ date is the number of days (not number of seconds) before or after
+ Jan 1, 1970.
+
+ See @ref Decode-Errors for discussion on how error handling works.
+
+ See @ref Tag-Usage for discussion on tag requirements.
+
+ See also @ref CBOR_TAG_DAYS_EPOCH, QCBOREncode_AddTDaysEpoch() and
+ @ref QCBOR_TYPE_DAYS_EPOCH.
+*/
+void QCBORDecode_GetEpochDays(QCBORDecodeContext *pCtx,
+                              uint8_t             uTagRequirement,
+                              int64_t            *pnDays);
+
+void QCBORDecode_GetEpochDaysInMapN(QCBORDecodeContext *pCtx,
+                                    int64_t             nLabel,
+                                    uint8_t             uTagRequirement,
+                                    int64_t            *pnDays);
+
+void QCBORDecode_GetEpochDaysInMapSZ(QCBORDecodeContext *pCtx,
+                                     const char         *szLabel,
+                                     uint8_t             uTagRequirement,
+                                     int64_t            *pnDays);
+
+
 
 
 /**
@@ -2171,6 +2239,54 @@
    QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pText);
 }
 
+static inline void
+QCBORDecode_GetDaysString(QCBORDecodeContext *pMe,
+                          uint8_t             uTagRequirement,
+                          UsefulBufC         *pValue)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_DAYS_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInternal(pMe, TagSpec, pValue);
+}
+
+inline static void
+QCBORDecode_GetDaysStringInMapN(QCBORDecodeContext *pMe,
+                                int64_t             nLabel,
+                                uint8_t             uTagRequirement,
+                                UsefulBufC         *pText)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_DAYS_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapN(pMe, nLabel, TagSpec, pText);
+}
+
+inline static void
+QCBORDecode_GetDaysStringInMapSZ(QCBORDecodeContext *pMe,
+                                 const char         *szLabel,
+                                 uint8_t             uTagRequirement,
+                                 UsefulBufC         *pText)
+{
+   const TagSpecification TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_DAYS_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_TEXT_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+   QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pText);
+}
+
+
 
 static inline void QCBORDecode_GetURI(QCBORDecodeContext *pMe,
                                       uint8_t             uTagRequirement,
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 4d31b69..b3301c7 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -1987,6 +1987,49 @@
 }
 
 
+/**
+ * @brief Convert the days epoch date.
+ *
+ * pDecodedItem[in,out]  The data item to convert.
+ *
+ * @retval QCBOR_ERR_DATE_OVERFLOW
+ * @retval QCBOR_ERR_FLOAT_DATE_DISABLED
+ * @retval QCBOR_ERR_BAD_TAG_CONTENT
+ *
+ * This is much simpler than the other epoch date format because
+ * floating-porint is not allowed. This is mostly a simple type check.
+ */
+static QCBORError DecodeDaysEpoch(QCBORItem *pDecodedItem)
+{
+   QCBORError uReturn = QCBOR_SUCCESS;
+
+   switch (pDecodedItem->uDataType) {
+
+      case QCBOR_TYPE_INT64:
+         pDecodedItem->val.epochDays = pDecodedItem->val.int64;
+         break;
+
+      case QCBOR_TYPE_UINT64:
+         /* This only happens for CBOR type 0 > INT64_MAX so it is
+          * always an overflow.
+          */
+         uReturn = QCBOR_ERR_DATE_OVERFLOW;
+         goto Done;
+         break;
+
+      default:
+         uReturn = QCBOR_ERR_BAD_TAG_CONTENT;
+         goto Done;
+         break;
+   }
+
+   pDecodedItem->uDataType = QCBOR_TYPE_DAYS_EPOCH;
+
+Done:
+   return uReturn;
+}
+
+
 #ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
 /**
  * @brief Decode decimal fractions and big floats.
@@ -2142,6 +2185,7 @@
 
 static const struct StringTagMapEntry StringTagMap[] = {
    {CBOR_TAG_DATE_STRING,   QCBOR_TYPE_DATE_STRING},
+   {CBOR_TAG_DAYS_STRING,   QCBOR_TYPE_DAYS_STRING},
    {CBOR_TAG_POS_BIGNUM,    QCBOR_TYPE_POSBIGNUM | IS_BYTE_STRING_BIT},
    {CBOR_TAG_NEG_BIGNUM,    QCBOR_TYPE_NEGBIGNUM | IS_BYTE_STRING_BIT},
    {CBOR_TAG_CBOR,          QBCOR_TYPE_WRAPPED_CBOR | IS_BYTE_STRING_BIT},
@@ -2255,6 +2299,9 @@
       } else if(uTagToProcess == CBOR_TAG_DATE_EPOCH) {
          uReturn = DecodeDateEpoch(pDecodedItem);
 
+      } else if(uTagToProcess == CBOR_TAG_DAYS_EPOCH) {
+         uReturn = DecodeDaysEpoch(pDecodedItem);
+
 #ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
       } else if(uTagToProcess == CBOR_TAG_DECIMAL_FRACTION ||
                 uTagToProcess == CBOR_TAG_BIGFLOAT) {
@@ -3831,6 +3878,104 @@
 
 
 
+/*
+ * Common processing for the RFC 8943 day-count tag. Mostly
+ * make sure the tag content is correct and copy forward any
+ * further other tag numbers.
+ */
+static void ProcessEpochDays(QCBORDecodeContext *pMe,
+                             QCBORItem          *pItem,
+                             uint8_t             uTagRequirement,
+                             int64_t            *pnDays)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      /* Already in error state, do nothing */
+      return;
+   }
+
+   QCBORError uErr;
+
+   const TagSpecification TagSpec =
+   {
+      uTagRequirement,
+      {QCBOR_TYPE_DAYS_EPOCH, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+      {QCBOR_TYPE_INT64, QCBOR_TYPE_UINT64, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+   };
+
+   uErr = CheckTagRequirement(TagSpec, pItem);
+   if(uErr != QCBOR_SUCCESS) {
+      goto Done;
+   }
+
+   if(pItem->uDataType != QCBOR_TYPE_DAYS_EPOCH) {
+      uErr = DecodeDaysEpoch(pItem);
+      if(uErr != QCBOR_SUCCESS) {
+         goto Done;
+      }
+   }
+
+   /* Save the tags in the last item's tags in the decode context
+    * for QCBORDecode_GetNthTagOfLast()
+    */
+   CopyTags(pMe, pItem);
+
+   *pnDays = pItem->val.epochDays;
+
+Done:
+   pMe->uLastError = (uint8_t)uErr;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void QCBORDecode_GetEpochDays(QCBORDecodeContext *pMe,
+                              uint8_t             uTagRequirement,
+                              int64_t            *pnDays)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      /* Already in error state, do nothing */
+      return;
+   }
+
+   QCBORItem  Item;
+   pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, &Item);
+
+   ProcessEpochDays(pMe, &Item, uTagRequirement, pnDays);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetEpochDaysInMapN(QCBORDecodeContext *pMe,
+                               int64_t             nLabel,
+                               uint8_t             uTagRequirement,
+                               int64_t            *pnDays)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   ProcessEpochDays(pMe, &Item, uTagRequirement, pnDays);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetEpochDaysInMapSZ(QCBORDecodeContext *pMe,
+                                const char         *szLabel,
+                                uint8_t             uTagRequirement,
+                                int64_t            *pnDays)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   ProcessEpochDays(pMe, &Item, uTagRequirement, pnDays);
+}
+
+
+
 
 void QCBORDecode_GetTaggedStringInternal(QCBORDecodeContext *pMe,
                                          TagSpecification    TagSpec,
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 236f59c..779b094 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2576,7 +2576,7 @@
    0xc0, // tag for string date
    0xa0, // Erroneous empty map as content for date
 
-   0xa9, // Open a map for tests involving labels.
+   0xad, // Open a map for tests involving labels.
 
    0x00,
    0xc0, // tag for string date
@@ -2615,6 +2615,25 @@
    // Untagged half-precision float with value -2
    0x09,
    0xF9, 0xC0, 0x00,
+
+   /* Tagged date-only date string */
+   0x63, 0x53, 0x44, 0x53,
+   0xD9, 0x03, 0xEC,
+   0x6A, 0x31, 0x39, 0x38, 0x35, 0x2D, 0x30, 0x34, 0x2D, 0x31, 0x32, /* "1985-04-12" */
+
+   /* Untagged date-only date string */
+   0x18, 0x63,
+   0x6A, 0x31, 0x39, 0x38, 0x35, 0x2D, 0x30, 0x34, 0x2D, 0x31, 0x32, /* "1985-04-12" */
+
+   /* Tagged days-count epoch date */
+   0x63, 0x53, 0x44, 0x45,
+   0xD8, 0x64,  /* tag(100) */
+   0x39, 0x29, 0xB3, /* -10676 */
+
+   /* Untagged days-count epoch date */
+   0x11,
+   0x19, 0x0F, 0x9A /* 3994 */
+
 };
 
 int32_t SpiffyDateDecodeTest()
@@ -2623,8 +2642,8 @@
    QCBORError         uError;
    int64_t            nEpochDate2, nEpochDate3, nEpochDate5,
                       nEpochDate4, nEpochDate6, nEpochDateFail,
-                      nEpochDate1400000000;
-   UsefulBufC         StringDate1, StringDate2;
+                      nEpochDate1400000000, nEpochDays1, nEpochDays2;
+   UsefulBufC         StringDate1, StringDate2, StringDays1, StringDays2;
    uint64_t           uTag1, uTag2;
 
    QCBORDecode_Init(&DC,
@@ -2804,6 +2823,26 @@
                                    &nEpochDate6);
    uTag2 = QCBORDecode_GetNthTagOfLast(&DC, 0);
 
+   /* The days format is much simpler than the date format
+    * because it can't be a floating point value. The test
+    * of the spiffy decode functions sufficiently covers
+    * the test of the non-spiffy decode days date decoding.
+    * There is no full fan out of the error conditions
+    * and decode options as that is implemented by code
+    * that is tested well by the date testing above.
+    */
+   QCBORDecode_GetDaysStringInMapSZ(&DC, "SDS", QCBOR_TAG_REQUIREMENT_TAG,
+                                    &StringDays1);
+
+   QCBORDecode_GetDaysStringInMapN(&DC, 99, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                   &StringDays2);
+
+   QCBORDecode_GetEpochDaysInMapSZ(&DC, "SDE", QCBOR_TAG_REQUIREMENT_TAG,
+                                   &nEpochDays1);
+
+   QCBORDecode_GetEpochDaysInMapN(&DC, 17, QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
+                                  &nEpochDays2);
+
    QCBORDecode_ExitMap(&DC);
    QCBORDecode_ExitArray(&DC);
    uError = QCBORDecode_Finish(&DC);
@@ -2831,6 +2870,14 @@
       return 204;
    }
 
+   if(nEpochDays1 != -10676) {
+      return 205;
+   }
+
+   if(nEpochDays2 != 3994) {
+      return 206;
+   }
+
    if(UsefulBuf_Compare(StringDate1, UsefulBuf_FromSZ("1985-04-12"))) {
       return 205;
    }
@@ -2839,6 +2886,14 @@
       return 206;
    }
 
+   if(UsefulBuf_Compare(StringDays1, UsefulBuf_FromSZ("1985-04-12"))) {
+      return 207;
+   }
+
+   if(UsefulBuf_Compare(StringDays2, UsefulBuf_FromSZ("1985-04-12"))) {
+      return 208;
+   }
+
    return 0;
 }
 
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 6655aed..18116cb 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -1170,69 +1170,102 @@
 
 
 /*
- 83                                      # array(3)
-   C0                                   # tag(0)
-      74                                # text(20)
-         323031332D30332D32315432303A30343A30305A # "2013-03-21T20:04:00Z"
-   C1                                   # tag(1)
-      1A 514B67B0                       # unsigned(1363896240)
-   A2                                   # map(2)
-      78 19                             # text(25)
-         53616D706C6520446174652066726F6D205246432033333339 # "Sample Date from RFC 3339"
-      C0                                # tag(0)
-         77                             # text(23)
-            313938352D30342D31325432333A32303A35302E35325A # "1985-04-12T23:20:50.52Z"
-      62                                # text(2)
-         5344                           # "SD"
-      C1                                # tag(1)
-         19 03E7                        # unsigned(999)
+ * [  "2013-03-21T20:04:00Z",
+ *    0("2013-03-21T20:04:00Z"),
+ *    1363896240,
+ *    1(1363896240),
+ *    100(-10676),
+ *    3994,
+ *    1004("1940-10-09"),
+ *    "1980-12-08",
+ *    {  "Sample Date from RFC 3339": 0("1985-04-12T23:20:50.52Z"),
+ *       "SD": 1(999),
+ *       "Sample Date from RFC 8943": "1985-04-12",
+ *       42: 1004("1985-04-12T23:20:50.52Z"),
+ *       "SY": 100(-10676),
+ *        45: 3994
+ *    }
+ * ]
  */
 static const uint8_t spExpectedEncodedDates[] = {
-   0x83, 0xc0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30, 0x33,
-   0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x34, 0x3a,
-   0x30, 0x30, 0x5a, 0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0, 0xa2,
-   0x78, 0x19, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44,
-   0x61, 0x74, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x52,
-   0x46, 0x43, 0x20, 0x33, 0x33, 0x33, 0x39, 0xc0, 0x77, 0x31,
-   0x39, 0x38, 0x35, 0x2d, 0x30, 0x34, 0x2d, 0x31, 0x32, 0x54,
-   0x32, 0x33, 0x3a, 0x32, 0x30, 0x3a, 0x35, 0x30, 0x2e, 0x35,
-   0x32, 0x5a, 0x62, 0x53, 0x44, 0xc1, 0x19, 0x03, 0xe7
-};
+   0x89, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2D, 0x30, 0x33, 0x2D,
+   0x32, 0x31, 0x54, 0x32, 0x30, 0x3A, 0x30, 0x34, 0x3A, 0x30,
+   0x30, 0x5A, 0xC0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2D, 0x30,
+   0x33, 0x2D, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3A, 0x30, 0x34,
+   0x3A, 0x30, 0x30, 0x5A, 0x1A, 0x51, 0x4B, 0x67, 0xB0, 0xC1,
+   0x1A, 0x51, 0x4B, 0x67, 0xB0, 0xD8, 0x64, 0x39, 0x29, 0xB3,
+   0x19, 0x0F, 0x9A, 0xD9, 0x03, 0xEC, 0x6A, 0x31, 0x39, 0x34,
+   0x30, 0x2D, 0x31, 0x30, 0x2D, 0x30, 0x39, 0x6A, 0x31, 0x39,
+   0x38, 0x30, 0x2D, 0x31, 0x32, 0x2D, 0x30, 0x38, 0xA6, 0x78,
+   0x19, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x44, 0x61,
+   0x74, 0x65, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x52, 0x46,
+   0x43, 0x20, 0x33, 0x33, 0x33, 0x39, 0xC0, 0x77, 0x31, 0x39,
+   0x38, 0x35, 0x2D, 0x30, 0x34, 0x2D, 0x31, 0x32, 0x54, 0x32,
+   0x33, 0x3A, 0x32, 0x30, 0x3A, 0x35, 0x30, 0x2E, 0x35, 0x32,
+   0x5A, 0x62, 0x53, 0x44, 0xC1, 0x19, 0x03, 0xE7, 0x78, 0x19,
+   0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x44, 0x61, 0x74,
+   0x65, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x52, 0x46, 0x43,
+   0x20, 0x38, 0x39, 0x34, 0x33, 0x6A, 0x31, 0x39, 0x38, 0x35,
+   0x2D, 0x30, 0x34, 0x2D, 0x31, 0x32, 0x18, 0x2A, 0xD9, 0x03,
+   0xEC, 0x77, 0x31, 0x39, 0x38, 0x35, 0x2D, 0x30, 0x34, 0x2D,
+   0x31, 0x32, 0x54, 0x32, 0x33, 0x3A, 0x32, 0x30, 0x3A, 0x35,
+   0x30, 0x2E, 0x35, 0x32, 0x5A, 0x62, 0x53, 0x59, 0xD8, 0x64,
+   0x39, 0x29, 0xB3, 0x18, 0x2D, 0x19, 0x0F, 0x9A};
 
 int32_t EncodeDateTest()
 {
    QCBOREncodeContext ECtx;
-   int nReturn = 0;
 
    QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
 
    QCBOREncode_OpenArray(&ECtx);
 
-
-   QCBOREncode_AddDateString(&ECtx, "2013-03-21T20:04:00Z"); // from CBOR RFC
-   QCBOREncode_AddDateEpoch(&ECtx, 1363896240); // from CBOR RFC
-
+   /* The values are taken from the CBOR RFCs */
+   QCBOREncode_AddTDateString(&ECtx, QCBOR_ENCODE_AS_BORROWED, "2013-03-21T20:04:00Z");
+   QCBOREncode_AddDateString(&ECtx, "2013-03-21T20:04:00Z");
+   QCBOREncode_AddTDateEpoch(&ECtx, QCBOR_ENCODE_AS_BORROWED, 1363896240);
+   QCBOREncode_AddDateEpoch(&ECtx, 1363896240);
+   QCBOREncode_AddTDaysEpoch(&ECtx, QCBOR_ENCODE_AS_TAG, -10676);
+   QCBOREncode_AddTDaysEpoch(&ECtx, QCBOR_ENCODE_AS_BORROWED, 3994);
+   QCBOREncode_AddTDaysString(&ECtx, QCBOR_ENCODE_AS_TAG, "1940-10-09");
+   QCBOREncode_AddTDaysString(&ECtx, QCBOR_ENCODE_AS_BORROWED, "1980-12-08");
 
    QCBOREncode_OpenMap(&ECtx);
 
-   QCBOREncode_AddDateStringToMap(&ECtx, "Sample Date from RFC 3339", "1985-04-12T23:20:50.52Z");
-
+   QCBOREncode_AddDateStringToMap(&ECtx,
+                                  "Sample Date from RFC 3339",
+                                  "1985-04-12T23:20:50.52Z");
    QCBOREncode_AddDateEpochToMap(&ECtx, "SD", 999);
+   QCBOREncode_AddTDaysStringToMapSZ(&ECtx,
+                                     "Sample Date from RFC 8943",
+                                     QCBOR_ENCODE_AS_BORROWED,
+                                     "1985-04-12");
+   QCBOREncode_AddTDaysStringToMapN(&ECtx,
+                                     42,
+                                     QCBOR_ENCODE_AS_TAG,
+                                     "1985-04-12T23:20:50.52Z");
+   QCBOREncode_AddTDaysEpochToMapSZ(&ECtx,
+                                    "SY",
+                                    QCBOR_ENCODE_AS_TAG,
+                                    -10676);
+   QCBOREncode_AddTDaysEpochToMapN(&ECtx,
+                                   45,
+                                   QCBOR_ENCODE_AS_BORROWED,
+                                   3994);
 
    QCBOREncode_CloseMap(&ECtx);
 
    QCBOREncode_CloseArray(&ECtx);
 
    UsefulBufC ECBOR;
-
    if(QCBOREncode_Finish(&ECtx, &ECBOR)) {
-      nReturn = -1;
+      return -1;
    }
 
    if(CheckResults(ECBOR, spExpectedEncodedDates))
       return -2;
 
-   return(nReturn);
+   return 0;
 }
 
 
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index ae64f7d..69f694a 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -125,7 +125,8 @@
 
 
 /*
- Encodes most data formats that are supported */
+ * Tests Encoding most data formats that are supported.
+ */
 int32_t EncodeDateTest(void);