From 6306250131c4368c81bc26e49c4914cfdd34d2d1 Mon Sep 17 00:00:00 2001
From: Sandor Oroszi <sandor.oroszi@balabit.com>
Date: Mon, 26 Oct 2020 16:35:23 +0100
Subject: [PATCH] Handle datetimes with time zone information in
 crypto.X509Store.set_time()

#907 fixed the issue with set_time() not working on Windows.
It also changed set_time()'s behavior in an incompatible way: instead of
treating vfy_time always being in local time (regardless if it had a time
zone attached or not), it now treats vfy_time as a time in UTC.

This patch improves on that by taking the time zone into account, and also
documents the incompatible change.

Note that it is not always possible to convert a timestamp in an arbitrary
time zone into UTC unambiguously, due to repeated or skipped local times
around DST changes. The best is to use a timezone-aware vfy_time
using the UTC time zone.
---
 CHANGELOG.rst         |  4 ++++
 src/OpenSSL/crypto.py | 10 ++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5df0a0515..913f66a20 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -15,6 +15,10 @@ Backward-incompatible changes:
 - Removed deprecated ``OpenSSL.SSL.Context.set_npn_advertise_callback``, ``OpenSSL.SSL.Context.set_npn_select_callback``, and ``OpenSSL.SSL.Connection.get_next_proto_negotiated``.
 - Drop support for Python 3.4
 - Drop support for OpenSSL 1.0.1
+- Honor time zones in the ``vfy_time`` parameter to ``OpenSSL.crypto.X509Store.set_time()``,
+  and assume that datetimes without a time zone are in UTC instead of in local time.
+  `#907 <https://github.com/pyca/pyopenssl/pull/907>`_
+  `#952 <https://github.com/pyca/pyopenssl/pull/952>`_
 
 Deprecations:
 ^^^^^^^^^^^^^
diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py
index 11be813ca..9ba96a99f 100644
--- a/src/OpenSSL/crypto.py
+++ b/src/OpenSSL/crypto.py
@@ -1660,6 +1660,11 @@ def set_time(self, vfy_time):
 
         Normally the current time is used.
 
+        The verification time can be a ``datetime`` object with or without time
+        zone information. A time without a time zone is assumed to be in UTC.
+        To avoid ambiguity, ``vfy_time`` should be a timezone-aware
+        ``datetime`` in the UTC time zone.
+
         .. note::
 
           For example, you can determine if a certificate was valid at a given
@@ -1667,14 +1672,15 @@ def set_time(self, vfy_time):
 
         .. versionadded:: 17.0.0
 
-        :param datetime vfy_time: The verification time to set on this store.
+        :param vfy_time: The verification time to set on this store.
+        :type vfy_time: :class:`datetime.datetime`
         :return: ``None`` if the verification time was successfully set.
         """
         param = _lib.X509_VERIFY_PARAM_new()
         param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
 
         _lib.X509_VERIFY_PARAM_set_time(
-            param, calendar.timegm(vfy_time.timetuple())
+            param, calendar.timegm(vfy_time.utctimetuple())
         )
         _openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)