From acdcbeb30537e9bdbab2f89bfbd8358530648a53 Mon Sep 17 00:00:00 2001 From: Shxiao101 Date: Sun, 6 Sep 2026 20:14:51 +0900 Subject: [PATCH 1/2] fix(python): return local timezone info from TimePlugin timeZoneOffset/timeZoneName datetime.now() returns a naive datetime, so strftime('%z')/('%Z') always returned empty strings. Attach the local timezone via astimezone() when the datetime is naive; aware datetimes are rendered as-is. --- python/semantic_kernel/core_plugins/time_plugin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/semantic_kernel/core_plugins/time_plugin.py b/python/semantic_kernel/core_plugins/time_plugin.py index 1b8444961644..89e8a0254b37 100644 --- a/python/semantic_kernel/core_plugins/time_plugin.py +++ b/python/semantic_kernel/core_plugins/time_plugin.py @@ -226,9 +226,13 @@ def time_zone_offset(self) -> str: """Get the current time zone offset. Example: - {{time.timeZoneOffset}} => -08:00 + {{time.timeZoneOffset}} => -0800 """ now = datetime.datetime.now() + if now.tzinfo is None: + # astimezone() attaches the local timezone to a naive datetime; + # on a naive datetime strftime("%z") returns an empty string. + now = now.astimezone() return now.strftime("%z") @kernel_function(description="Get the current time zone name", name="timeZoneName") @@ -239,4 +243,6 @@ def time_zone_name(self) -> str: {{time.timeZoneName}} => PST """ now = datetime.datetime.now() + if now.tzinfo is None: + now = now.astimezone() return now.strftime("%Z") From 4c5e36f19427aea836dadf9ae27bbb1d464e2f7a Mon Sep 17 00:00:00 2001 From: Shxiao101 Date: Sun, 6 Sep 2026 20:46:19 +0900 Subject: [PATCH 2/2] test(python): cover TimePlugin naive-now timezone rendering The new tests pin the regression fixed in the previous commit: with a naive datetime, timeZoneOffset/timeZoneName must render the local timezone instead of returning empty strings. Expected values are computed via the same astimezone() reference, so they hold on any machine and timezone. --- .../unit/core_plugins/test_time_plugin.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/python/tests/unit/core_plugins/test_time_plugin.py b/python/tests/unit/core_plugins/test_time_plugin.py index 60fdec31f12c..18e3b7808df3 100644 --- a/python/tests/unit/core_plugins/test_time_plugin.py +++ b/python/tests/unit/core_plugins/test_time_plugin.py @@ -178,9 +178,39 @@ def test_time_zone_offset(): assert plugin.time_zone_offset() == "+0000" +def test_time_zone_offset_naive_now_is_not_empty(): + """strftime('%z') on a naive datetime returns '' (Python docs); the plugin + must attach the local timezone instead of returning an empty string.""" + plugin = TimePlugin() + naive_now = datetime.datetime(2031, 1, 12, 12, 24, 56) + + with mock.patch("datetime.datetime", wraps=datetime.datetime) as dt: + dt.now.return_value = naive_now + offset = plugin.time_zone_offset() + + # The expected value cancels out the machine's local timezone because + # both sides run astimezone() on the same naive datetime. + assert offset == naive_now.astimezone().strftime("%z") + assert offset != "" + + def test_time_zone_name(): plugin = TimePlugin() with mock.patch("datetime.datetime", wraps=datetime.datetime) as dt: dt.now.return_value = test_mock_now assert plugin.time_zone_name() == "UTC" + + +def test_time_zone_name_naive_now_is_not_empty(): + """strftime('%Z') on a naive datetime returns '' (Python docs); the plugin + must attach the local timezone instead of returning an empty string.""" + plugin = TimePlugin() + naive_now = datetime.datetime(2031, 1, 12, 12, 24, 56) + + with mock.patch("datetime.datetime", wraps=datetime.datetime) as dt: + dt.now.return_value = naive_now + name = plugin.time_zone_name() + + assert name == naive_now.astimezone().strftime("%Z") + assert name != ""