From acc7ce8a7aead57915c41becb3c467ee70c7da59 Mon Sep 17 00:00:00 2001
From: Charly C <changaco@changaco.oy.lc>
Date: Wed, 20 Feb 2019 16:56:32 +0100
Subject: [PATCH 1/3] don't encode dates as datetimes by default

---
 cbor2/encoder.py | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/cbor2/encoder.py b/cbor2/encoder.py
index b6e24e4d..e150a07f 100644
--- a/cbor2/encoder.py
+++ b/cbor2/encoder.py
@@ -66,6 +66,9 @@ class CBOREncoder(object):
         when True, use "canonical" CBOR representation; this typically involves
         sorting maps, sets, etc. into a pre-determined order ensuring that
         serializations are comparable without decoding
+    :param bool date_as_datetime: set to ``True`` to serialize date objects as
+        datetimes (CBOR tag 0), which was the default behavior in previous
+        releases (cbor2 <= 4.1.2).
 
     .. _CBOR: https://cbor.io/
     """
@@ -76,7 +79,8 @@ class CBOREncoder(object):
         '_canonical')
 
     def __init__(self, fp, datetime_as_timestamp=False, timezone=None,
-                 value_sharing=False, default=None, canonical=False):
+                 value_sharing=False, default=None, canonical=False,
+                 date_as_datetime=False):
         self.fp = fp
         self.datetime_as_timestamp = datetime_as_timestamp
         self.timezone = timezone
@@ -87,6 +91,8 @@ def __init__(self, fp, datetime_as_timestamp=False, timezone=None,
         self._encoders = default_encoders.copy()
         if canonical:
             self._encoders.update(canonical_encoders)
+        if date_as_datetime:
+            self._encoders[date] = CBOREncoder.encode_date
 
     def _find_encoder(self, obj_type):
         for type_, enc in list(iteritems(self._encoders)):
@@ -471,7 +477,6 @@ def encode_undefined(self, value):
     (FrozenDict,                    CBOREncoder.encode_map),
     (type(undefined),               CBOREncoder.encode_undefined),
     (datetime,                      CBOREncoder.encode_datetime),
-    (date,                          CBOREncoder.encode_date),
     (type(re.compile('')),          CBOREncoder.encode_regexp),
     (('fractions', 'Fraction'),     CBOREncoder.encode_rational),
     (('email.message', 'Message'),  CBOREncoder.encode_mime),

From ec61f88bd8598ccd189871c6ec372e8f555a75fd Mon Sep 17 00:00:00 2001
From: Charly C <changaco@changaco.oy.lc>
Date: Wed, 20 Feb 2019 17:37:20 +0100
Subject: [PATCH 2/3] update the tests

---
 tests/test_encoder.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tests/test_encoder.py b/tests/test_encoder.py
index bae4da08..0eb29d67 100644
--- a/tests/test_encoder.py
+++ b/tests/test_encoder.py
@@ -236,8 +236,13 @@ def test_datetime(impl, value, as_timestamp, expected):
 
 
 def test_date(impl):
+    with pytest.raises(impl.CBOREncodeError):
+        impl.dumps(date(2013, 3, 21))
+
+
+def test_date_as_datetime(impl):
     expected = unhexlify('c074323031332d30332d32315430303a30303a30305a')
-    assert impl.dumps(date(2013, 3, 21), timezone=timezone.utc) == expected
+    assert impl.dumps(date(2013, 3, 21), timezone=timezone.utc, date_as_datetime=True) == expected
 
 
 def test_naive_datetime(impl):

From 1eb4bd5820a7a3bfec040de951ef9554db553253 Mon Sep 17 00:00:00 2001
From: Changaco <changaco@changaco.oy.lc>
Date: Tue, 4 Jun 2019 08:57:50 +0200
Subject: [PATCH 3/3] modify the C implementation accordingly

---
 source/encoder.c | 21 ++++++++++++++++-----
 source/module.c  |  2 ++
 source/module.h  |  1 +
 3 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/source/encoder.c b/source/encoder.c
index 701fedc5..a1058d41 100644
--- a/source/encoder.c
+++ b/source/encoder.c
@@ -111,19 +111,21 @@ CBOREncoder_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
 
 
 // CBOREncoder.__init__(self, fp=None, datetime_as_timestamp=0, timezone=None,
-//                      value_sharing=False, default=None, canonical=False)
+//                      value_sharing=False, default=None, canonical=False,
+//                      date_as_datetime=False)
 int
 CBOREncoder_init(CBOREncoderObject *self, PyObject *args, PyObject *kwargs)
 {
     static char *keywords[] = {
         "fp", "datetime_as_timestamp", "timezone", "value_sharing", "default",
-        "canonical", NULL
+        "canonical", "date_as_datetime", NULL
     };
-    PyObject *tmp, *fp = NULL, *default_handler = NULL, *tz = NULL;
+    PyObject *tmp, *fp = NULL, *default_handler = NULL, *tz = NULL,
+    *date_as_datetime = NULL;
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|pOpOB", keywords,
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|pOpOBO", keywords,
                 &fp, &self->timestamp_format, &tz, &self->value_sharing,
-                &default_handler, &self->enc_style))
+                &default_handler, &self->enc_style, &date_as_datetime))
         return -1;
 
     if (_CBOREncoder_set_fp(self, fp, NULL) == -1)
@@ -153,6 +155,15 @@ CBOREncoder_init(CBOREncoderObject *self, PyObject *args, PyObject *kwargs)
                     _CBOR2_str_update, _CBOR2_canonical_encoders, NULL))
             return -1;
     }
+    if (date_as_datetime) {
+        PyObject *encode_date = PyObject_GetAttr((PyObject *) &CBOREncoderType, _CBOR2_str_encode_date);
+        if (!encode_date)
+            return -1;
+        PyObject *datetime_class = PyDateTimeAPI->DateType;
+        if (PyObject_SetItem(self->encoders, datetime_class, encode_date) == -1)
+            return -1;
+        Py_DECREF(encode_date);
+    }
 
     return 0;
 }
diff --git a/source/module.c b/source/module.c
index 0baf9b6b..d4ac8de4 100644
--- a/source/module.c
+++ b/source/module.c
@@ -601,6 +601,7 @@ PyObject *_CBOR2_str_datestr_re = NULL;
 PyObject *_CBOR2_str_Decimal = NULL;
 PyObject *_CBOR2_str_default_encoders = NULL;
 PyObject *_CBOR2_str_denominator = NULL;
+PyObject *_CBOR2_str_encode_date = NULL;
 PyObject *_CBOR2_str_Fraction = NULL;
 PyObject *_CBOR2_str_fromtimestamp = NULL;
 PyObject *_CBOR2_str_FrozenDict = NULL;
@@ -861,6 +862,7 @@ PyInit__cbor2(void)
     INTERN_STRING(Decimal);
     INTERN_STRING(default_encoders);
     INTERN_STRING(denominator);
+    INTERN_STRING(encode_date);
     INTERN_STRING(Fraction);
     INTERN_STRING(fromtimestamp);
     INTERN_STRING(FrozenDict);
diff --git a/source/module.h b/source/module.h
index 56443af6..62e9038f 100644
--- a/source/module.h
+++ b/source/module.h
@@ -43,6 +43,7 @@ extern PyObject *_CBOR2_str_datestr_re;
 extern PyObject *_CBOR2_str_Decimal;
 extern PyObject *_CBOR2_str_default_encoders;
 extern PyObject *_CBOR2_str_denominator;
+extern PyObject *_CBOR2_str_encode_date;
 extern PyObject *_CBOR2_str_Fraction;
 extern PyObject *_CBOR2_str_fromtimestamp;
 extern PyObject *_CBOR2_str_FrozenDict;