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);