From c0b678c12def760eef2b7e38c8a3d89815285f52 Mon Sep 17 00:00:00 2001
From: mathislucka <mathis.lucka@gmail.com>
Date: Fri, 7 Mar 2025 17:16:52 +0100
Subject: [PATCH 1/3] feat: time extension for ChatPromptBuilder

---
 .../builders/chat_prompt_builder.py           | 10 ++-
 .../builders/test_chat_prompt_builder.py      | 83 +++++++++++++++++++
 2 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/haystack/components/builders/chat_prompt_builder.py b/haystack/components/builders/chat_prompt_builder.py
index 33e2feda2d..2730314aaf 100644
--- a/haystack/components/builders/chat_prompt_builder.py
+++ b/haystack/components/builders/chat_prompt_builder.py
@@ -10,6 +10,8 @@
 
 from haystack import component, default_from_dict, default_to_dict, logging
 from haystack.dataclasses.chat_message import ChatMessage, ChatRole, TextContent
+from haystack.utils import Jinja2TimeExtension
+
 
 logger = logging.getLogger(__name__)
 
@@ -124,7 +126,13 @@ def __init__(
         self.required_variables = required_variables or []
         self.template = template
         variables = variables or []
-        self._env = SandboxedEnvironment()
+        try:
+            # The Jinja2TimeExtension needs an optional dependency to be installed.
+            # If it's not available we can do without it and use the PromptBuilder as is.
+            self._env = SandboxedEnvironment(extensions=[Jinja2TimeExtension])
+        except ImportError:
+            self._env = SandboxedEnvironment()
+
         if template and not variables:
             for message in template:
                 if message.is_from(ChatRole.USER) or message.is_from(ChatRole.SYSTEM):
diff --git a/test/components/builders/test_chat_prompt_builder.py b/test/components/builders/test_chat_prompt_builder.py
index 1c39922df4..9fe5991fd2 100644
--- a/test/components/builders/test_chat_prompt_builder.py
+++ b/test/components/builders/test_chat_prompt_builder.py
@@ -1,5 +1,7 @@
 from typing import Any, Dict, List, Optional
 from jinja2 import TemplateSyntaxError
+
+import arrow
 import pytest
 
 from haystack.components.builders.chat_prompt_builder import ChatPromptBuilder
@@ -335,6 +337,87 @@ def test_example_in_pipeline_simple(self):
         }
         assert result == expected_dynamic
 
+    def test_with_custom_dateformat(self) -> None:
+        template = [ChatMessage.from_user("Formatted date: {% now 'UTC', '%Y-%m-%d' %}")]
+        builder = ChatPromptBuilder(template=template)
+
+        result = builder.run()["prompt"]
+
+        now_formatted = f"Formatted date: {arrow.now('UTC').strftime('%Y-%m-%d')}"
+        assert len(result) == 1
+        assert result[0].role == "user"
+        assert result[0].text == now_formatted
+
+    def test_with_different_timezone(self) -> None:
+        template = [ChatMessage.from_user("Current time in New York is: {% now 'America/New_York' %}")]
+        builder = ChatPromptBuilder(template=template)
+
+        result = builder.run()["prompt"]
+
+        now_ny = f"Current time in New York is: {arrow.now('America/New_York').strftime('%Y-%m-%d %H:%M:%S')}"
+        assert len(result) == 1
+        assert result[0].role == "user"
+        assert result[0].text == now_ny
+
+    def test_date_with_addition_offset(self) -> None:
+        template = [ChatMessage.from_user("Time after 2 hours is: {% now 'UTC' + 'hours=2' %}")]
+        builder = ChatPromptBuilder(template=template)
+
+        result = builder.run()["prompt"]
+
+        now_plus_2 = f"Time after 2 hours is: {(arrow.now('UTC').shift(hours=+2)).strftime('%Y-%m-%d %H:%M:%S')}"
+        assert len(result) == 1
+        assert result[0].role == "user"
+        assert result[0].text == now_plus_2
+
+    def test_date_with_subtraction_offset(self) -> None:
+        template = [ChatMessage.from_user("Time after 12 days is: {% now 'UTC' - 'days=12' %}")]
+        builder = ChatPromptBuilder(template=template)
+
+        result = builder.run()["prompt"]
+
+        now_minus_12 = f"Time after 12 days is: {(arrow.now('UTC').shift(days=-12)).strftime('%Y-%m-%d %H:%M:%S')}"
+        assert len(result) == 1
+        assert result[0].role == "user"
+        assert result[0].text == now_minus_12
+
+    def test_invalid_timezone(self) -> None:
+        template = [ChatMessage.from_user("Current time is: {% now 'Invalid/Timezone' %}")]
+        builder = ChatPromptBuilder(template=template)
+
+        # Expect ValueError for invalid timezone
+        with pytest.raises(ValueError, match="Invalid timezone"):
+            builder.run()
+
+    def test_invalid_offset(self) -> None:
+        template = [ChatMessage.from_user("Time after invalid offset is: {% now 'UTC' + 'invalid_offset' %}")]
+        builder = ChatPromptBuilder(template=template)
+
+        # Expect ValueError for invalid offset
+        with pytest.raises(ValueError, match="Invalid offset or operator"):
+            builder.run()
+
+    def test_multiple_messages_with_date_template(self) -> None:
+        template = [
+            ChatMessage.from_user("Current date is: {% now 'UTC' %}"),
+            ChatMessage.from_assistant("Thank you for providing the date"),
+            ChatMessage.from_user("Yesterday was: {% now 'UTC' - 'days=1' %}"),
+        ]
+        builder = ChatPromptBuilder(template=template)
+
+        result = builder.run()["prompt"]
+
+        now = f"Current date is: {arrow.now('UTC').strftime('%Y-%m-%d %H:%M:%S')}"
+        yesterday = f"Yesterday was: {(arrow.now('UTC').shift(days=-1)).strftime('%Y-%m-%d %H:%M:%S')}"
+
+        assert len(result) == 3
+        assert result[0].role == "user"
+        assert result[0].text == now
+        assert result[1].role == "assistant"
+        assert result[1].text == "Thank you for providing the date"
+        assert result[2].role == "user"
+        assert result[2].text == yesterday
+
 
 class TestChatPromptBuilderDynamic:
     def test_multiple_templated_chat_messages(self):

From be018c36da0c3836dfa53ddc8c5f31011802aea1 Mon Sep 17 00:00:00 2001
From: mathislucka <mathis.lucka@gmail.com>
Date: Fri, 7 Mar 2025 17:20:29 +0100
Subject: [PATCH 2/3] chore: release notes

---
 haystack/components/builders/chat_prompt_builder.py          | 1 -
 ...t-time-extension-chatprompt-builder-fb159207088943d8.yaml | 5 +++++
 2 files changed, 5 insertions(+), 1 deletion(-)
 create mode 100644 releasenotes/notes/feat-time-extension-chatprompt-builder-fb159207088943d8.yaml

diff --git a/haystack/components/builders/chat_prompt_builder.py b/haystack/components/builders/chat_prompt_builder.py
index 2730314aaf..b8d4acdfac 100644
--- a/haystack/components/builders/chat_prompt_builder.py
+++ b/haystack/components/builders/chat_prompt_builder.py
@@ -12,7 +12,6 @@
 from haystack.dataclasses.chat_message import ChatMessage, ChatRole, TextContent
 from haystack.utils import Jinja2TimeExtension
 
-
 logger = logging.getLogger(__name__)
 
 
diff --git a/releasenotes/notes/feat-time-extension-chatprompt-builder-fb159207088943d8.yaml b/releasenotes/notes/feat-time-extension-chatprompt-builder-fb159207088943d8.yaml
new file mode 100644
index 0000000000..f1c707c0dc
--- /dev/null
+++ b/releasenotes/notes/feat-time-extension-chatprompt-builder-fb159207088943d8.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Users can now work with date and time in the ChatPromptBuilder.
+    In the same way as the PromptBuilder, the ChatPromptBuilder now supports arrow to work with datetime.

From 3f4ffc377902ef74ef34ace36ed28cc3d7d719be Mon Sep 17 00:00:00 2001
From: Sebastian Husch Lee <sjrl423@gmail.com>
Date: Wed, 19 Mar 2025 13:57:32 +0100
Subject: [PATCH 3/3] Fix comment

---
 haystack/components/builders/chat_prompt_builder.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/haystack/components/builders/chat_prompt_builder.py b/haystack/components/builders/chat_prompt_builder.py
index b8d4acdfac..eadc68209a 100644
--- a/haystack/components/builders/chat_prompt_builder.py
+++ b/haystack/components/builders/chat_prompt_builder.py
@@ -127,7 +127,7 @@ def __init__(
         variables = variables or []
         try:
             # The Jinja2TimeExtension needs an optional dependency to be installed.
-            # If it's not available we can do without it and use the PromptBuilder as is.
+            # If it's not available we can do without it and use the ChatPromptBuilder as is.
             self._env = SandboxedEnvironment(extensions=[Jinja2TimeExtension])
         except ImportError:
             self._env = SandboxedEnvironment()