diff --git a/frappe/core/doctype/security_settings/security_settings.json b/frappe/core/doctype/security_settings/security_settings.json index bbaf9b7c16..088acaceb7 100644 --- a/frappe/core/doctype/security_settings/security_settings.json +++ b/frappe/core/doctype/security_settings/security_settings.json @@ -18,7 +18,7 @@ "label": "Security.txt" }, { - "description": "Date after which this security.txt should be considered stale.", + "description": "Date after which this security.txt should be considered stale. Expires timestamp is converted to UTC.", "fieldname": "public_expires", "fieldtype": "Datetime", "label": "Expires" @@ -56,7 +56,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-04-14 12:50:57.138749", + "modified": "2026-04-17 13:07:45.259146", "modified_by": "Administrator", "module": "Core", "name": "Security Settings", @@ -77,5 +77,6 @@ "rows_threshold_for_grid_search": 20, "sort_field": "creation", "sort_order": "DESC", - "states": [] + "states": [], + "track_changes": 1 } diff --git a/frappe/core/doctype/security_settings/security_settings.py b/frappe/core/doctype/security_settings/security_settings.py index 478e4e909b..7bf439869a 100644 --- a/frappe/core/doctype/security_settings/security_settings.py +++ b/frappe/core/doctype/security_settings/security_settings.py @@ -2,12 +2,19 @@ # For license information, please see license.txt from datetime import UTC, datetime +from zoneinfo import ZoneInfo import frappe import frappe.utils from frappe import _ from frappe.model.document import Document -from frappe.utils import validate_email_address, validate_phone_number, validate_url +from frappe.utils import ( + get_system_timezone, + now_datetime, + validate_email_address, + validate_phone_number, + validate_url, +) class SecuritySettings(Document): @@ -69,8 +76,7 @@ class SecuritySettings(Document): expires = self.public_expires or frappe.utils.add_years(frappe.utils.now_datetime(), 1) if isinstance(expires, str): expires = datetime.fromisoformat(expires) - expires = expires.replace(microsecond=0) - expires = expires.astimezone(UTC) + expires = expires.replace(microsecond=0, tzinfo=ZoneInfo(get_system_timezone())).astimezone(UTC) value = expires.strftime("%Y-%m-%dT%H:%M:%SZ") return f"Expires: {value}" @@ -112,5 +118,5 @@ class SecuritySettings(Document): expires = self.public_expires if isinstance(expires, str): expires = datetime.fromisoformat(expires) - if expires <= datetime.now(): + if expires <= now_datetime(): frappe.throw(_("Expiration date must be in the future")) diff --git a/frappe/core/doctype/security_settings/test_security_settings.py b/frappe/core/doctype/security_settings/test_security_settings.py index 9052e407cc..198e16687a 100644 --- a/frappe/core/doctype/security_settings/test_security_settings.py +++ b/frappe/core/doctype/security_settings/test_security_settings.py @@ -4,10 +4,10 @@ from datetime import UTC, datetime, timedelta import frappe -from frappe.tests import UnitTestCase +from frappe.tests import IntegrationTestCase -class TestSecuritySettings(UnitTestCase): +class TestSecuritySettings(IntegrationTestCase): def test_public_policy_section_default(self): doc = frappe.get_doc( { @@ -239,10 +239,11 @@ class TestSecuritySettings(UnitTestCase): # Should not raise doc.validate_expires() + @IntegrationTestCase.change_settings("System Settings", {"time_zone": "Etc/UTC"}) def test_public_expires_section_future_date(self): from datetime import timezone - future_date = datetime(2027, 12, 31, 23, 59, 59, tzinfo=UTC) + future_date = datetime(2027, 12, 31, 23, 59, 59) doc = frappe.get_doc( { "doctype": "Security Settings", @@ -252,11 +253,12 @@ class TestSecuritySettings(UnitTestCase): section = doc.public_expires_section self.assertIn("2027-12-31T23:59:59Z", section) + @IntegrationTestCase.change_settings("System Settings", {"time_zone": "Asia/Kolkata"}) def test_public_expires_section_string(self): doc = frappe.get_doc( { "doctype": "Security Settings", - "public_expires": "2027-12-31T23:59:59+00:00", + "public_expires": "2028-01-01T05:29:59", } ) section = doc.public_expires_section