Add support for day count dates, RFC 8943 (#106)
This addresses #98
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,