diff --git a/CHANGELOG.md b/CHANGELOG.md index 6977ebc..eacfd59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Fixes +- Fix `renew_before_days` not triggering renewal on the exact day boundary (e.g. `renew_before_days = 3` did not renew when 3 days and 23 hours remained, even though status showed "3 days") + ### Changes ## 0.8.0 (2026-04-17) diff --git a/debian/changelog b/debian/changelog index d4804b2..a438804 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ certforge (0.9.0-1) unstable; urgency=low - * No changes + * Fix renew_before_days not triggering on exact day boundary -- Roman Kivalin Thu, 17 Apr 2026 00:00:00 +0000 diff --git a/src/certs.rs b/src/certs.rs index a337907..24e8c06 100644 --- a/src/certs.rs +++ b/src/certs.rs @@ -52,16 +52,24 @@ impl CertInfo { } /// Check if the certificate expires within the given number of days. + /// Uses rounded day count so that renew_before_days = 3 triggers + /// renewal when days_until_expiry() reports 3 or less. pub fn expires_within_days(&self, days: u32) -> bool { - let now = chrono::Utc::now(); - let threshold = now + chrono::Duration::days(days as i64); - self.not_after <= threshold + self.days_until_expiry() <= days as i64 } - /// Days until expiry (negative if already expired). + /// Days until expiry, rounded to nearest day (negative if already expired). + /// Rounding provides a half-day buffer that absorbs systemd timer + /// randomization without missing the renewal window. pub fn days_until_expiry(&self) -> i64 { let now = chrono::Utc::now(); - (self.not_after - now).num_days() + let secs = (self.not_after - now).num_seconds(); + let half_day = 43200; // 12 * 60 * 60 + if secs >= 0 { + (secs + half_day) / 86400 + } else { + (secs - half_day) / 86400 + } } }