diff --git a/Knot.files b/Knot.files
index ad47d6ea0bdfb2d24ca1aa0f8d70bbff66720f14..1aaa0530d8da390bfaa9718532baa4ba3cfb754e 100644
--- a/Knot.files
+++ b/Knot.files
@@ -16,6 +16,7 @@ doc/running.texi
 doc/security.texi
 doc/troubleshooting.texi
 man/Makefile.am
+patches/Makefile.am
 samples/Makefile.am
 src/Makefile.am
 src/common/acl.c
@@ -141,6 +142,7 @@ src/libknot/dnssec/key.h
 src/libknot/dnssec/nsec-bitmap.h
 src/libknot/dnssec/nsec3.c
 src/libknot/dnssec/nsec3.h
+src/libknot/dnssec/policy.c
 src/libknot/dnssec/policy.h
 src/libknot/dnssec/random.h
 src/libknot/dnssec/rrset-sign.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 9a1eb3d9eb7dbc295439d94c0494810e5848c1ed..c1fd6babbffb42cd09d74520940f09472d62e1e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -163,6 +163,7 @@ libknot_la_SOURCES =				\
 	libknot/dnssec/nsec-bitmap.h		\
 	libknot/dnssec/nsec3.c			\
 	libknot/dnssec/nsec3.h			\
+	libknot/dnssec/policy.c			\
 	libknot/dnssec/policy.h			\
 	libknot/dnssec/random.h			\
 	libknot/dnssec/rrset-sign.c		\
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index e616443854ee8753e84f676b5b64388037df6c26..acb9207ea0b2fba59e79c8db9015ebd3a2b6183c 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -178,12 +178,10 @@ static int remote_zone_sign(server_t *server, const knot_zone_t *zone)
 	log_server_info("Requested zone resign for '%s'.\n", zone_name);
 	free(zone_name);
 
-	uint32_t expires_at = 0;
+	uint32_t refresh_at = 0;
 	zones_cancel_dnssec((knot_zone_t *)zone);
-	rcu_read_lock();
-	zones_dnssec_sign((knot_zone_t *)zone, true, &expires_at);
-	rcu_read_unlock();
-	zones_schedule_dnssec((knot_zone_t *)zone, expires_at);
+	zones_dnssec_sign((knot_zone_t *)zone, true, &refresh_at);
+	zones_schedule_dnssec((knot_zone_t *)zone, refresh_at);
 
 	return KNOT_EOK;
 }
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index bea3ca7aad6f6faeaf47c4815627d3d58fe41b1f..eb87d3dd19ffcae64a961554b590884deb454a1b 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -269,7 +269,6 @@ int zones_refresh_ev(event_t *e)
 	dbg_zones("zones: RETRY of '%s' after %u seconds\n",
 	          zd->conf->name, retry_tmr / 1000);
 
-
 	/* Issue request. */
 	evsched_event_finished(e->parent);
 	ret = xfr_enqueue(zd->server->xfr, rq);
@@ -277,9 +276,6 @@ int zones_refresh_ev(event_t *e)
 		xfr_task_free(rq);
 	}
 
-
-
-
 	return ret;
 }
 
@@ -840,20 +836,6 @@ static int zones_merge_and_store_changesets(knot_zone_t *zone,
 	return KNOT_EOK;
 }
 
-static uint32_t expiration_to_relative(uint32_t exp,
-                                       const knot_zone_t *zone) {
-	time_t t = time(NULL);
-	if (t >= exp) {
-		char *zname = knot_dname_to_str(zone->name);
-		log_zone_warning("DNSSEC: Zone %s: Signature lifetime too low, "
-		                 "set higher value in configuration!\n", zname);
-		free(zname);
-		return 0;
-	} else {
-		return (exp - t) * 1000;
-	}
-}
-
 /*----------------------------------------------------------------------------*/
 
 static int zones_check_tsig_query(const knot_zone_t *zone,
@@ -875,7 +857,6 @@ static int zones_check_tsig_query(const knot_zone_t *zone,
 	int ret = zones_query_check_zone(zone, knot_packet_opcode(query),
 	                                 addr, tsig_key_zone, rcode);
 
-
 	/* Accept found OR unknown key results. */
 	if (ret == KNOT_EOK || ret == KNOT_EACCES) {
 		if (*tsig_key_zone != NULL) {
@@ -1007,21 +988,17 @@ static uint32_t zones_next_serial(knot_zone_t *zone)
 /*----------------------------------------------------------------------------*/
 
 static int replan_zone_sign_after_ddns(knot_zone_t *zone, zonedata_t *zd,
-                                       uint32_t expires_at)
+                                       uint32_t refresh_at)
 {
 	assert(zone);
 	assert(zd);
 
-	int ret = KNOT_EOK;
-	uint32_t new_expire = time(NULL) + expires_at;
-	if (new_expire < zd->dnssec_timer->tv.tv_sec) {
-		// Drop old event, earlier signing needed
-		zones_cancel_dnssec(zone);
-		ret = zones_schedule_dnssec(zone,
-		                            expiration_to_relative(new_expire,
-								   zone));
+	if (zd->dnssec_timer->tv.tv_sec <= refresh_at) {
+		return KNOT_EOK;
 	}
-	return ret;
+
+	zones_cancel_dnssec(zone);
+	return zones_schedule_dnssec(zone, refresh_at);
 }
 
 static bool apex_rr_changed(const knot_zone_contents_t *old_contents,
@@ -1050,7 +1027,6 @@ static bool zones_nsec3param_changed(const knot_zone_contents_t *old_contents,
 	return apex_rr_changed(old_contents, new_contents, KNOT_RRTYPE_NSEC3PARAM);
 }
 
-
 /*! \brief Process UPDATE query.
  *
  * Functions expects that the query is already authenticated
@@ -1144,7 +1120,7 @@ static int zones_process_update_auth(knot_zone_t *zone,
 
 	knot_changesets_t *sec_chs = NULL;
 	knot_changeset_t *sec_ch = NULL;
-	uint32_t expires_at = 0;
+	uint32_t refresh_at = 0;
 
 	conf_zone_t *zone_config = ((zonedata_t *)knot_zone_data(zone))->conf;
 	assert(zone_config);
@@ -1186,20 +1162,15 @@ static int zones_process_update_auth(knot_zone_t *zone,
 		    zones_nsec3param_changed(old_contents, new_contents)) {
 			ret = knot_dnssec_zone_sign(fake_zone, sec_ch,
 			                            KNOT_SOA_SERIAL_KEEP,
-			                            &expires_at, new_serial);
+			                            &refresh_at, new_serial);
 		} else {
 			// Sign the created changeset
-			uint32_t used_lifetime = 0;
-			uint32_t used_refresh = 0;
 
 			knot_zone_contents_load_nsec3param(new_contents);
 			ret = knot_dnssec_sign_changeset(fake_zone,
 			                      knot_changesets_get_last(chgsets),
 			                      sec_ch, KNOT_SOA_SERIAL_KEEP,
-			                      &used_lifetime, &used_refresh,
-			                      new_serial);
-
-			expires_at = used_lifetime - used_refresh;
+			                      &refresh_at, new_serial);
 		}
 
 		if (ret != KNOT_EOK) {
@@ -1256,7 +1227,7 @@ static int zones_process_update_auth(knot_zone_t *zone,
 		// Plan zone resign if needed
 		zonedata_t *zd = (zonedata_t *)zone->data;
 		assert(zd && zd->dnssec_timer);
-		ret = replan_zone_sign_after_ddns(zone, zd, expires_at);
+		ret = replan_zone_sign_after_ddns(zone, zd, refresh_at);
 		if (ret != KNOT_EOK) {
 			log_zone_error("%s: Failed to replan zone sign %s\n",
 			               msg, knot_strerror(ret));
@@ -1864,7 +1835,7 @@ int zones_process_update(knot_nameserver_t *nameserver,
 			return ret;
 		}
 	}
-	
+
 	/*
 	 * Check if UPDATE not running already.
 	 */
@@ -2357,7 +2328,6 @@ static int zones_serialize_and_store_chgset(const knot_changeset_t *chs,
 
 	}
 
-
 	return KNOT_EOK;
 }
 
@@ -2719,11 +2689,11 @@ int zones_schedule_refresh(knot_zone_t *zone, int64_t time)
 	return KNOT_EOK;
 }
 
-int zones_dnssec_sign(knot_zone_t *zone, bool force, uint32_t *expires_at)
+int zones_dnssec_sign(knot_zone_t *zone, bool force, uint32_t *refresh_at)
 {
 	int ret = KNOT_EOK;
 	char *msgpref = NULL;
-	*expires_at = 0;
+	*refresh_at = 0;
 
 	knot_changesets_t *chs = knot_changesets_create();
 	if (chs == NULL) {
@@ -2754,11 +2724,11 @@ int zones_dnssec_sign(knot_zone_t *zone, bool force, uint32_t *expires_at)
 	uint32_t new_serial = zones_next_serial(zone);
 
 	if (force) {
-		ret = knot_dnssec_zone_sign_force(zone, ch, expires_at,
+		ret = knot_dnssec_zone_sign_force(zone, ch, refresh_at,
 		                                  new_serial);
 	} else {
 		ret = knot_dnssec_zone_sign(zone, ch, KNOT_SOA_SERIAL_UPDATE,
-		                            expires_at, new_serial);
+		                            refresh_at, new_serial);
 	}
 	if (ret != KNOT_EOK) {
 		goto done;
@@ -2789,13 +2759,11 @@ int zones_dnssec_ev(event_t *event)
 	// We will be working with zone, don't want it to change in the meantime
 	rcu_read_lock();
 	knot_zone_t *zone = (knot_zone_t *)event->data;
-	uint32_t expires_at = 0;
+	uint32_t refresh_at = 0;
 
-	int ret = zones_dnssec_sign(zone, false, &expires_at);
-	if (expires_at != 0) {
-		ret = zones_schedule_dnssec(zone,
-		                            expiration_to_relative(expires_at,
-								   zone));
+	int ret = zones_dnssec_sign(zone, false, &refresh_at);
+	if (refresh_at != 0) {
+		ret = zones_schedule_dnssec(zone, refresh_at);
 	}
 	rcu_read_unlock();
 
@@ -2818,7 +2786,7 @@ int zones_cancel_dnssec(knot_zone_t *zone)
 	return KNOT_EOK;
 }
 
-int zones_schedule_dnssec(knot_zone_t *zone, uint32_t time)
+int zones_schedule_dnssec(knot_zone_t *zone, time_t unixtime)
 {
 	if (!zone || !zone->data) {
 		return KNOT_EINVAL;
@@ -2827,21 +2795,45 @@ int zones_schedule_dnssec(knot_zone_t *zone, uint32_t time)
 	zonedata_t *zd = (zonedata_t *)zone->data;
 	evsched_t *scheduler = zd->server->sched;
 
-	char *zname = knot_dname_to_str(knot_zone_name(zone));
-	log_zone_info("DNSSEC: Zone %s - planning next resign %" PRIu32 "s"
-	              "(%" PRIu32 "h) from now.\n", zname, time / 1000,
-	              time / 3600000);
-	free(zname);
+	// event not created yet
+	if (zd->dnssec_timer == NULL) {
+		return KNOT_EAGAIN;
+	}
 
-	if (zd->dnssec_timer) {
-		// Event created already, just reschedule
-		evsched_schedule(scheduler, zd->dnssec_timer, time);
+	char *zname = knot_dname_to_str(zone->name);
+
+	// absolute time -> relative time
+
+	time_t now = time(NULL);
+	int32_t relative = 0;
+	if (unixtime <= now) {
+		log_zone_warning("DNSSEC: Zone %s: Signature life time too low, "
+		                 "set higher value in configuration!\n", zname);
+	} else {
+		relative = unixtime - now;
 	}
 
+	// log the message
+
+	char time_str[64] = {'\0'};
+	struct tm time_gm = {0};
+
+	gmtime_r(&unixtime, &time_gm);
+
+	strftime(time_str, sizeof(time_str), KNOT_LOG_TIME_FORMAT, &time_gm);
+
+	log_zone_info("DNSSEC: Zone %s: Next signing planned on %s.\n",
+	              zname, time_str);
+
+	free(zname);
+
+	// schedule
+
+	evsched_schedule(scheduler, zd->dnssec_timer, relative * 1000);
+
 	return KNOT_EOK;
 }
 
-
 /*!
  * \brief Schedule IXFR sync for given zone.
  */
@@ -2878,7 +2870,6 @@ int zones_process_update_response(knot_ns_xfr_t *data, uint8_t *rwire, size_t *r
 	return ret;
 }
 
-
 int zones_verify_tsig_query(const knot_packet_t *query,
                             const knot_tsig_key_t *key,
                             knot_rcode_t *rcode, uint16_t *tsig_rcode,
@@ -3146,7 +3137,7 @@ int zones_do_diff_and_sign(const conf_zone_t *z, knot_zone_t *zone,
 	knot_changesets_t *sec_chs = NULL;
 	knot_changeset_t *sec_ch = NULL;
 	knot_zone_contents_t *new_contents = NULL;
-	uint32_t expires_at = 0;
+	uint32_t refresh_at = 0;
 	if (z->dnssec_enable) {
 		sec_chs = knot_changesets_create();
 		if (sec_chs == NULL) {
@@ -3174,7 +3165,7 @@ int zones_do_diff_and_sign(const conf_zone_t *z, knot_zone_t *zone,
 		 */
 		int ret = knot_dnssec_zone_sign(zone, sec_ch,
 		                                KNOT_SOA_SERIAL_UPDATE,
-		                                &expires_at, new_serial);
+		                                &refresh_at, new_serial);
 		if (ret != KNOT_EOK) {
 			knot_changesets_free(&diff_chs);
 			knot_changesets_free(&sec_chs);
@@ -3250,9 +3241,7 @@ int zones_do_diff_and_sign(const conf_zone_t *z, knot_zone_t *zone,
 
 	// Schedule next zone signing
 	if (z->dnssec_enable) {
-		ret = zones_schedule_dnssec(zone,
-					    expiration_to_relative(expires_at,
-								   zone));
+		ret = zones_schedule_dnssec(zone, refresh_at);
 	}
 
 	return ret;
diff --git a/src/knot/server/zones.h b/src/knot/server/zones.h
index fe4866fccc4030d142645a5b33dff44cb1a635ac..58427dcd4c29509b383f213a828a760a3cfaea12 100644
--- a/src/knot/server/zones.h
+++ b/src/knot/server/zones.h
@@ -315,12 +315,12 @@ int zones_cancel_dnssec(knot_zone_t *zone);
 /*!
  * \brief Schedule DNSSEC event.
  * \param zone Related zone.
- * \param time When to schedule. Time difference in milliseconds from now.
+ * \param unixtime When to schedule.
  * \param force Force sign or not
  *
  * \return Error code, KNOT_OK if successful.
  */
-int zones_schedule_dnssec(knot_zone_t *zone, uint32_t time);
+int zones_schedule_dnssec(knot_zone_t *zone, time_t unixtime);
 
 /*!
  * \brief Schedule IXFR sync for given zone.
diff --git a/src/libknot/dnssec/policy.c b/src/libknot/dnssec/policy.c
new file mode 100644
index 0000000000000000000000000000000000000000..a862d55272790befde90cbbb3ef8a021326c850e
--- /dev/null
+++ b/src/libknot/dnssec/policy.c
@@ -0,0 +1,65 @@
+/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+
+#include "libknot/dnssec/policy.h"
+
+uint32_t knot_dnssec_policy_refresh_time(const knot_dnssec_policy_t *policy,
+                                         uint32_t earliest_expiration)
+{
+	if (policy == NULL) {
+		return 0;
+	}
+
+	uint32_t signature_safety = policy->sign_lifetime / 10;
+	if (earliest_expiration <= signature_safety) {
+		return 0;
+	}
+
+	return earliest_expiration - signature_safety;
+}
+
+void knot_dnssec_policy_set_sign_lifetime(knot_dnssec_policy_t *policy,
+                                          uint32_t sign_lifetime)
+{
+	if (policy == NULL) {
+		return;
+	}
+
+	uint32_t max_expiration = policy->now + sign_lifetime;
+
+	policy->sign_lifetime = sign_lifetime;
+	policy->refresh_before = knot_dnssec_policy_refresh_time(policy,
+	                                                         max_expiration);
+}
+
+void knot_dnssec_init_default_policy(knot_dnssec_policy_t *policy)
+{
+	if (policy == NULL) {
+		return;
+	}
+
+	memset(policy, 0, sizeof(*policy));
+
+	policy->now = time(NULL);
+	policy->soa_up = KNOT_SOA_SERIAL_UPDATE;
+
+	knot_dnssec_policy_set_sign_lifetime(policy, KNOT_DNSSEC_DEFAULT_LIFETIME);
+}
diff --git a/src/libknot/dnssec/policy.h b/src/libknot/dnssec/policy.h
index a4877718ba1e480aefc38b09f27f9036c1a3ec6c..a4daf0d6c160d9c96e0ab4cc57dd33966207d3be 100644
--- a/src/libknot/dnssec/policy.h
+++ b/src/libknot/dnssec/policy.h
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -27,6 +27,9 @@
 #ifndef _KNOT_DNSSEC_POLICY_H_
 #define _KNOT_DNSSEC_POLICY_H_
 
+#include <stdbool.h>
+#include <stdint.h>
+
 typedef enum knot_update_serial {
 	KNOT_SOA_SERIAL_UPDATE = 1 << 0,
 	KNOT_SOA_SERIAL_KEEP = 1 << 1
@@ -34,25 +37,31 @@ typedef enum knot_update_serial {
 
 typedef struct {
 	uint32_t now;               //! Current time.
+	uint32_t refresh_before;    //! Refresh signatures expiring before to this time.
 	uint32_t sign_lifetime;     //! Signature life time.
-	uint32_t sign_refresh;      //! Sig. refresh time before expiration.
 	bool forced_sign;           //! Drop valid signatures as well.
 	knot_update_serial_t soa_up;//! Policy for serial updating.
 } knot_dnssec_policy_t;
 
 #define KNOT_DNSSEC_DEFAULT_LIFETIME 2592000
-#define KNOT_DNSSEC_DEFAULT_REFRESH 7200
-
-#define DEFAULT_DNSSEC_POLICY { .now = time_now(), \
-				.sign_lifetime = KNOT_DNSSEC_DEFAULT_LIFETIME, \
-				.sign_refresh = KNOT_DNSSEC_DEFAULT_REFRESH, \
-				.forced_sign = false, \
-				.soa_up = KNOT_SOA_SERIAL_UPDATE }
-#define FORCED_DNSSEC_POLICY {  .now = time_now(), \
-				.sign_lifetime = KNOT_DNSSEC_DEFAULT_LIFETIME, \
-				.sign_refresh = KNOT_DNSSEC_DEFAULT_REFRESH, \
-				.forced_sign = true, \
-				.soa_up = KNOT_SOA_SERIAL_UPDATE }
+
+/*!
+ * \brief Initialize default signing policy.
+ */
+void knot_dnssec_init_default_policy(knot_dnssec_policy_t *policy);
+
+/*!
+ * \brief Set policy timing data according to requested signature lifetime.
+ */
+void knot_dnssec_policy_set_sign_lifetime(knot_dnssec_policy_t *policy,
+                                          uint32_t sign_lifetime);
+
+/*!
+ * \brief Get signature refresh time from the earliest expiration time.
+ */
+uint32_t knot_dnssec_policy_refresh_time(const knot_dnssec_policy_t *policy,
+                                         uint32_t earliest_expiration);
+
 
 #endif // _KNOT_DNSSEC_POLICY_H_
 
diff --git a/src/libknot/dnssec/rrset-sign.c b/src/libknot/dnssec/rrset-sign.c
index 75995fc01abedc8802fd483b62a62570c4fbd9b5..d0dd14d1a52b05d741e2a96fcc588ea7c66a7344 100644
--- a/src/libknot/dnssec/rrset-sign.c
+++ b/src/libknot/dnssec/rrset-sign.c
@@ -334,11 +334,9 @@ static bool is_expired_signature(const knot_rrset_t *rrsigs, size_t pos,
 	assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
 	assert(policy);
 
-	uint32_t now = policy->now;
-	uint32_t refresh = policy->sign_refresh;
 	uint32_t expiration = knot_rdata_rrsig_sig_expiration(rrsigs, pos);
 
-	return (expiration - refresh) <= now;
+	return (expiration <= policy->refresh_before);
 }
 
 /*!
diff --git a/src/libknot/dnssec/zone-events.c b/src/libknot/dnssec/zone-events.c
index 645aa31525f73fad7d44ae42ba26430f9223ef3d..1289cff3a3931ab96cdef6df62b2819693cd47a6 100644
--- a/src/libknot/dnssec/zone-events.c
+++ b/src/libknot/dnssec/zone-events.c
@@ -27,27 +27,6 @@
 #include "libknot/util/debug.h"
 #include "libknot/zone/zone.h"
 
-static uint32_t time_now(void)
-{
-	return (uint32_t)time(NULL);
-}
-
-static void init_default_policy(knot_dnssec_policy_t *p,
-                                knot_update_serial_t soa_up)
-{
-	knot_dnssec_policy_t p_image = DEFAULT_DNSSEC_POLICY;
-	memcpy(p, &p_image, sizeof(knot_dnssec_policy_t));
-	p->soa_up = soa_up;
-}
-
-static void init_forced_policy(knot_dnssec_policy_t *p,
-                               knot_update_serial_t soa_up)
-{
-	knot_dnssec_policy_t p_image = FORCED_DNSSEC_POLICY;
-	memcpy(p, &p_image, sizeof(knot_dnssec_policy_t));
-	p->soa_up = soa_up;
-}
-
 static int init_dnssec_structs(const knot_zone_t *zone,
                                knot_zone_keys_t *zone_keys,
                                knot_dnssec_policy_t *policy,
@@ -78,23 +57,20 @@ static int init_dnssec_structs(const knot_zone_t *zone,
 	}
 
 	// Init sign policy
-	if (force) {
-		init_forced_policy(policy, soa_up);
-	} else {
-		init_default_policy(policy, soa_up);
-	}
+	knot_dnssec_init_default_policy(policy);
+	policy->soa_up = soa_up;
+	policy->forced_sign = force;
 
 	// Override signature lifetime, if set in config
-	int sig_lf = config->sig_lifetime;
-	if (sig_lf > 0) {
-		policy->sign_lifetime = sig_lf;
+	if (config->sig_lifetime > 0) {
+		knot_dnssec_policy_set_sign_lifetime(policy, config->sig_lifetime);
 	}
 
 	return KNOT_EOK;
 }
 
 static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
-                     knot_update_serial_t soa_up, uint32_t *expires_at,
+                     knot_update_serial_t soa_up, uint32_t *refresh_at,
                      uint32_t new_serial)
 {
 	assert(zone);
@@ -143,7 +119,7 @@ static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
 
 	// add missing signatures
 	result = knot_zone_sign(zone->contents, &zone_keys, &policy, out_ch,
-	                        expires_at);
+	                        refresh_at);
 	if (result != KNOT_EOK) {
 		log_zone_error("%s Error while signing (%s).\n",
 		               msgpref, knot_strerror(result));
@@ -188,25 +164,25 @@ static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force,
 }
 
 int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
-                          knot_update_serial_t soa_up, uint32_t *expires_at,
+                          knot_update_serial_t soa_up, uint32_t *refresh_at,
                           uint32_t new_serial)
 {
 	if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	return zone_sign(zone, out_ch, false, soa_up, expires_at, new_serial);
+	return zone_sign(zone, out_ch, false, soa_up, refresh_at, new_serial);
 }
 
 int knot_dnssec_zone_sign_force(knot_zone_t *zone,
-                                knot_changeset_t *out_ch, uint32_t *expires_at,
+                                knot_changeset_t *out_ch, uint32_t *refresh_at,
                                 uint32_t new_serial)
 {
 	if (zone == NULL || zone->contents == NULL || out_ch == NULL) {
 		return KNOT_EINVAL;
 	}
 
-	return zone_sign(zone, out_ch, true, KNOT_SOA_SERIAL_UPDATE, expires_at,
+	return zone_sign(zone, out_ch, true, KNOT_SOA_SERIAL_UPDATE, refresh_at,
 	                 new_serial);
 }
 
@@ -214,17 +190,13 @@ int knot_dnssec_sign_changeset(const knot_zone_t *zone,
                                const knot_changeset_t *in_ch,
                                knot_changeset_t *out_ch,
                                knot_update_serial_t soa_up,
-                               uint32_t *used_lifetime,
-                               uint32_t *used_refresh,
+                               uint32_t *refresh_at,
                                uint32_t new_serial)
 {
-	if (!used_lifetime || !used_refresh) {
+	if (!refresh_at) {
 		return KNOT_EINVAL;
 	}
 
-	*used_lifetime = 0;
-	*used_refresh = 0;
-
 	if (!conf()->dnssec_enable) {
 		return KNOT_EOK;
 	}
@@ -288,8 +260,7 @@ int knot_dnssec_sign_changeset(const knot_zone_t *zone,
 	knot_free_zone_keys(&zone_keys);
 	free(msgpref);
 
-	*used_lifetime = policy.sign_lifetime;
-	*used_refresh = policy.sign_refresh;
+	*refresh_at = policy.refresh_before; // only new signatures are made
 
 	return KNOT_EOK;
 }
diff --git a/src/libknot/dnssec/zone-events.h b/src/libknot/dnssec/zone-events.h
index f473194420d1e9b2bdc0ff327b3c3c6da4953ebd..693a08dacc8aa6bc58339df0c6c89c7665b52b8f 100644
--- a/src/libknot/dnssec/zone-events.h
+++ b/src/libknot/dnssec/zone-events.h
@@ -38,12 +38,12 @@
  * \param zone        Zone to be signed.
  * \param out_ch      New records will be added to this changeset.
  * \param soa_up      SOA serial update policy.
- * \param expires_at  Expiration time of the oldest signature in zone
+ * \param refresh_at  Signature refresh time of the oldest signature in zone.
  *
  * \return Error code, KNOT_EOK if successful.
  */
 int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
-                          knot_update_serial_t soa_up, uint32_t *expires_at,
+                          knot_update_serial_t soa_up, uint32_t *refresh_at,
                           uint32_t new_serial);
 
 /*!
@@ -52,12 +52,12 @@ int knot_dnssec_zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch,
  *
  * \param zone    Zone to be signed.
  * \param out_ch  New records will be added to this changeset.
- * \param expires_at  Expiration time of the oldest signature in zone
+ * \param expires_at  Signature refresh time of the oldest signature in zone.
  *
  * \return Error code, KNOT_EOK if successful.
  */
 int knot_dnssec_zone_sign_force(knot_zone_t *zone, knot_changeset_t *out_ch,
-                                uint32_t *expires_at, uint32_t new_serial);
+                                uint32_t *refresh_at, uint32_t new_serial);
 
 /*!
  * \brief Sign changeset created by DDNS or zone-diff.
@@ -66,8 +66,8 @@ int knot_dnssec_zone_sign_force(knot_zone_t *zone, knot_changeset_t *out_ch,
  * \param in_ch          Changeset created bvy DDNS or zone-diff
  * \param out_ch         New records will be added to this changeset.
  * \param soa_up         SOA serial update policy.
- * \param used_lifetime  Pointer to sig lifetime used to sign the changeset.
- * \param used_refresh   Pointer to refresh period used to sign the changeset.
+ * \param refresh_at     Signature refresh time of the new signatures.
+ * \param new_serial     New SOA serial.
  *
  * \return Error code, KNOT_EOK if successful.
  */
@@ -75,8 +75,8 @@ int knot_dnssec_sign_changeset(const knot_zone_t *zone,
                                const knot_changeset_t *in_ch,
                                knot_changeset_t *out_ch,
                                knot_update_serial_t soa_up,
-                               uint32_t *used_lifetime,
-                               uint32_t *used_refresh, uint32_t new_serial);
+                               uint32_t *refresh_at,
+                               uint32_t new_serial);
 
 #endif // _KNOT_DNSSEC_ZONE_EVENTS_H_
 /*! @} */
diff --git a/src/libknot/dnssec/zone-sign.c b/src/libknot/dnssec/zone-sign.c
index 2ea184090e78ef041dcf73b2a5589ed5f7f312bf..8e4179f77427c95925f2d944df5656c826c9ec8f 100644
--- a/src/libknot/dnssec/zone-sign.c
+++ b/src/libknot/dnssec/zone-sign.c
@@ -1030,9 +1030,9 @@ int knot_zone_sign(const knot_zone_contents_t *zone,
                    const knot_zone_keys_t *zone_keys,
                    const knot_dnssec_policy_t *policy,
                    knot_changeset_t *changeset,
-                   uint32_t *expires_at)
+                   uint32_t *refresh_at)
 {
-	if (!zone || !zone_keys || !policy || !changeset || !expires_at) {
+	if (!zone || !zone_keys || !policy || !changeset || !refresh_at) {
 		return KNOT_EINVAL;
 	}
 
@@ -1062,14 +1062,12 @@ int knot_zone_sign(const knot_zone_contents_t *zone,
 
 	// renew the signatures a little earlier
 	uint32_t expiration = MIN(normal_tree_expiration, nsec3_tree_expiration);
-	assert(expiration >= policy->sign_refresh);
-	expiration = expiration - policy->sign_refresh;
 
 	// DNSKEY updates
 	uint32_t dnskey_update = knot_get_next_zone_key_event(zone_keys);
 	expiration = MIN(expiration, dnskey_update);
 
-	*expires_at = expiration;
+	*refresh_at = knot_dnssec_policy_refresh_time(policy, expiration);
 
 	return KNOT_EOK;
 }
diff --git a/src/libknot/dnssec/zone-sign.h b/src/libknot/dnssec/zone-sign.h
index 27dac340e4a51f9797ddb5dd77b89bb92c9d8d85..5a05968fce0ce4cda41645450f6ca977d0321523 100644
--- a/src/libknot/dnssec/zone-sign.h
+++ b/src/libknot/dnssec/zone-sign.h
@@ -43,14 +43,14 @@
  * \param zone_keys   Zone keys.
  * \param policy      DNSSEC policy.
  * \param changeset   Changeset to be updated.
- * \param expires_at  Pointer to expiration time of the oldest signature in zone
+ * \param refresh_at  Pointer to refresh time when the zone should be resigned.
  *
  * \return Error code, KNOT_EOK if successful.
  */
 int knot_zone_sign(const knot_zone_contents_t *zone,
                    const knot_zone_keys_t *zone_keys,
                    const knot_dnssec_policy_t *policy,
-                   knot_changeset_t *out_ch, uint32_t *expires_at);
+                   knot_changeset_t *out_ch, uint32_t *refresh_at);
 
 /*!
  * \brief Update and sign SOA and store performed changes in changeset.