diff --git a/__init__.py b/__init__.py
index 12577cc..732f849 100644
--- a/__init__.py
+++ b/__init__.py
@@ -87,6 +87,7 @@ from .modules.pandas_wrapper.pandas_sin import PandasSin
from .modules.pandas_wrapper.pandas_skew import PandasSkew
from .modules.pandas_wrapper.pandas_sort import PandasSort
from .modules.pandas_wrapper.pandas_std import PandasStd
+from .modules.pandas_wrapper.pandas_strftime import PandasStrftime
from .modules.pandas_wrapper.pandas_sub import PandasSub
from .modules.pandas_wrapper.pandas_sub_scalar_float import PandasSubScalarFloat
from .modules.pandas_wrapper.pandas_sub_scalar_int import PandasSubScalarInt
@@ -94,6 +95,7 @@ from .modules.pandas_wrapper.pandas_sub_series import PandasSubSeries
from .modules.pandas_wrapper.pandas_sum import PandasSum
from .modules.pandas_wrapper.pandas_summary import PandasSummary
from .modules.pandas_wrapper.pandas_tan import PandasTan
+from .modules.pandas_wrapper.pandas_to_datetime import PandasToDatetime
from .modules.pandas_wrapper.pandas_to_string import PandasToString
from .modules.pandas_wrapper.pandas_transpose import PandasTranspose
from .modules.pandas_wrapper.pandas_var import PandasVar
@@ -196,6 +198,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasSkew": PandasSkew,
"PandasSort": PandasSort,
"PandasStd": PandasStd,
+ "PandasStrftime": PandasStrftime,
"PandasSub": PandasSub,
"PandasSubScalarFloat": PandasSubScalarFloat,
"PandasSubScalarInt": PandasSubScalarInt,
@@ -203,6 +206,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasSum": PandasSum,
"PandasSummary": PandasSummary,
"PandasTan": PandasTan,
+ "PandasToDatetime": PandasToDatetime,
"PandasToString": PandasToString,
"PandasTranspose": PandasTranspose,
"PandasVar": PandasVar,
@@ -305,6 +309,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasSkew": "Pandas Skew",
"PandasSort": "Pandas Sort",
"PandasStd": "Pandas Std",
+ "PandasStrftime": "Pandas Strftime",
"PandasSub": "Pandas Sub",
"PandasSubScalarFloat": "Pandas Sub Scalar Float",
"PandasSubScalarInt": "Pandas Sub Scalar Int",
@@ -312,6 +317,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasSum": "Pandas Sum",
"PandasSummary": "Pandas Summary",
"PandasTan": "Pandas Tan",
+ "PandasToDatetime": "Pandas To Datetime",
"PandasToString": "Pandas To String",
"PandasTranspose": "Pandas Transpose",
"PandasVar": "Pandas Var",
diff --git a/docs/reference/node_reference.md b/docs/reference/node_reference.md
index fc72717..9fac6c9 100644
--- a/docs/reference/node_reference.md
+++ b/docs/reference/node_reference.md
@@ -65,6 +65,11 @@
| --- | --- |
| [Pandas Columns](pandas_columns.md) | Retrieves the column labels of a pandas DataFrame. |
| [Pandas Index](pandas_index.md) | Retrieves the row labels (index) of a pandas DataFrame. |
+## Date and time processing
+| Node | Description |
+| --- | --- |
+| [Pandas Strftime](pandas_strftime.md) | Converts one or more datetime columns in a DataFrame to string columns. |
+| [Pandas To Datetime](pandas_to_datetime.md) | Converts one or more string columns in a DataFrame to datetime columns. |
## Display data
| Node | Description |
| --- | --- |
diff --git a/docs/reference/pandas_strftime.md b/docs/reference/pandas_strftime.md
new file mode 100644
index 0000000..65779c9
--- /dev/null
+++ b/docs/reference/pandas_strftime.md
@@ -0,0 +1,21 @@
+# Pandas Strftime
+Converts one or more datetime columns in a DataFrame to string columns.
+
+Refer to [https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior](Python document) for date/time format strings.
+
+## Input
+| Name | Data type |
+|---|---|
+| dataframe | DataFrame |
+| column_names | String |
+| date_format | String |
+
+## Output
+| Data type |
+|---|
+| DataFrame |
+
+
+Category: Date and time processing
+
+ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/docs/reference/pandas_to_datetime.md b/docs/reference/pandas_to_datetime.md
new file mode 100644
index 0000000..6b08282
--- /dev/null
+++ b/docs/reference/pandas_to_datetime.md
@@ -0,0 +1,22 @@
+# Pandas To Datetime
+Converts one or more string columns in a DataFrame to datetime columns.
+
+Note: When only the time portion is provided, the date defaults to 1900-01-01.
+Refer to [https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior](the Python documentation) for valid date/time format strings.
+
+## Input
+| Name | Data type |
+|---|---|
+| dataframe | DataFrame |
+| column_names | String |
+| date_format | String |
+
+## Output
+| Data type |
+|---|
+| DataFrame |
+
+
+Category: Date and time processing
+
+ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/modules/pandas_wrapper/pandas_strftime.py b/modules/pandas_wrapper/pandas_strftime.py
new file mode 100644
index 0000000..3042d75
--- /dev/null
+++ b/modules/pandas_wrapper/pandas_strftime.py
@@ -0,0 +1,55 @@
+from typing import Any, Dict
+import pandas as pd
+import pandas.api.types as ptypes
+
+
+class PandasStrftime:
+ """
+ Converts one or more datetime columns in a DataFrame to string columns.
+
+ Refer to [https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior](Python document) for date/time format strings.
+
+ category: Date and time processing
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls) -> Dict[str, Any]:
+ """
+ Defines the input types for the function.
+
+ Returns:
+ Dict[str, Any]: A dictionary specifying required input types.
+ """
+ return {
+ "required": {
+ "dataframe": ("DATAFRAME", {}),
+ "column_names": ("STRING", {"default": ""}),
+ "date_format": ("STRING", {"default": ""}),
+ }
+ }
+
+ RETURN_TYPES: tuple = ("DATAFRAME",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Data Analysis"
+
+ def f(self, dataframe: pd.DataFrame, column_names: str, date_format: str) -> tuple:
+ """
+ Converts one or more datetime columns in a DataFrame to string columns.
+
+ Args:
+ dataframe (DataFrame): The DataFrame.
+ column_names (str): A comma-separated string of column names containing datetime values.
+ date_format (str): A date format string for strftime.
+
+ Returns:
+ tuple: A tuple containing the DataFrame with formatted date columns.
+ """
+ date_columns = [col.strip() for col in column_names.split(",")]
+
+ df2 = dataframe.copy()
+ df2[date_columns] = df2[date_columns].apply(
+ # Keep the datetime column check in case the user specifies the non-date column
+ lambda col: col.dt.strftime(date_format) if ptypes.is_datetime64_any_dtype(col) else col
+ )
+
+ return (df2,)
diff --git a/modules/pandas_wrapper/pandas_to_datetime.py b/modules/pandas_wrapper/pandas_to_datetime.py
new file mode 100644
index 0000000..d18a284
--- /dev/null
+++ b/modules/pandas_wrapper/pandas_to_datetime.py
@@ -0,0 +1,50 @@
+from typing import Any, Dict
+import pandas as pd
+import pandas.api.types as ptypes
+
+
+class PandasToDatetime:
+ """
+ Converts one or more string columns in a DataFrame to datetime columns.
+
+ Note: When only the time portion is provided, the date defaults to 1900-01-01.
+ Refer to [https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior](the Python documentation) for valid date/time format strings.
+
+ category: Date and time processing
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls) -> Dict[str, Any]:
+ """
+ Defines the input types for the function.
+
+ Returns:
+ Dict[str, Any]: A dictionary specifying required input types.
+ """
+ return {
+ "required": {
+ "dataframe": ("DATAFRAME", {}),
+ "column_names": ("STRING", {"default": ""}),
+ "date_format": ("STRING", {"default": ""}),
+ }
+ }
+
+ RETURN_TYPES: tuple = ("DATAFRAME",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Data Analysis"
+
+ def f(self, dataframe: pd.DataFrame, column_names: str, date_format: str) -> tuple:
+ """
+ Converts one or more string columns to datetime columns.
+
+ Args:
+ dataframe (DataFrame): The DataFrame.
+ column_names (str): A comma-separated string of column names containing date time strings.
+ date_format (str): A date format.
+
+ Returns:
+ tuple: A tuple containing the DataFrame with selected columns.
+ """
+ date_columns = [col.strip() for col in column_names.split(",")]
+ df2 = dataframe.apply(lambda col: pd.to_datetime(col, format=date_format) if col.name in date_columns else col)
+ return (df2,)
diff --git a/pyproject.toml b/pyproject.toml
index 0034e40..76678a8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "data-analysis"
description = "Data analysis custom modules for ComfyUI - Use Pandas & Matplotlib within ComfyUI"
-version = "1.1.20"
+version = "1.1.21"
license = {file = "LICENSE"}
[project.urls]
diff --git a/tests/pandas_wrapper/pandas_to_datetime_test.py b/tests/pandas_wrapper/pandas_to_datetime_test.py
new file mode 100644
index 0000000..a007a75
--- /dev/null
+++ b/tests/pandas_wrapper/pandas_to_datetime_test.py
@@ -0,0 +1,57 @@
+import os
+import sys
+import unittest
+import pandas as pd
+import pandas.api.types as ptypes
+
+PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
+MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
+sys.path.append(MODULE_ROOT)
+print(MODULE_ROOT)
+
+from pandas_wrapper.pandas_to_datetime import PandasToDatetime
+
+class Test(unittest.TestCase):
+ def setUp(self):
+ """Set up test data"""
+ self.node = PandasToDatetime()
+ self.pd_date = pd.DataFrame({
+ "A": ["2015-01-23", "2013-06-30"],
+ "B": ["2005-02-24", "2015-09-06"],
+ "C": [123.45, 987.65],
+ })
+
+ self.pd_date_time = pd.DataFrame({
+ "A": ["2015-01-23 00:12:34", "2013-06-30 12:50:59"],
+ "B": [123.45, 987.65],
+ })
+
+ self.pd_time = pd.DataFrame({
+ "A": ["00:12:34", "12:50:59"],
+ "B": [123.45, 987.65],
+ })
+
+ def test_date(self):
+ """Test"""
+ df2 = self.node.f(self.pd_date, column_names="A,B", date_format="%Y-%m-%d")[0]
+ self.assertEqual(df2.at[0, "A"], pd.Timestamp("2015-01-23"))
+ self.assertEqual(df2.at[0, "C"], 123.45)
+
+ def test_date_time(self):
+ """Test"""
+ df3 = self.node.f(self.pd_date_time, column_names="A", date_format="%Y-%m-%d %H:%M:%S")[0]
+ self.assertTrue(ptypes.is_datetime64_any_dtype(df3["A"]))
+ self.assertTrue(ptypes.is_float_dtype(df3["B"]))
+ self.assertEqual(df3.at[0, "A"], pd.Timestamp("2015-01-23 00:12:34"))
+ self.assertEqual(df3.at[0, "B"], 123.45)
+
+ def test_time(self):
+ """Test"""
+ df3 = self.node.f(self.pd_time, column_names="A", date_format="%H:%M:%S")[0]
+ self.assertTrue(ptypes.is_datetime64_any_dtype(df3["A"]))
+ self.assertEqual(df3.at[0, "A"], pd.Timestamp("1900-01-01 00:12:34"))
+ self.assertEqual(df3.at[0, "B"], 123.45)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pandas_wrapper/pandas_to_strftime_test.py b/tests/pandas_wrapper/pandas_to_strftime_test.py
new file mode 100644
index 0000000..8c0a0de
--- /dev/null
+++ b/tests/pandas_wrapper/pandas_to_strftime_test.py
@@ -0,0 +1,63 @@
+import os
+import sys
+import unittest
+import pandas as pd
+import pandas.api.types as ptypes
+
+PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
+MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
+sys.path.append(MODULE_ROOT)
+print(MODULE_ROOT)
+
+from pandas_wrapper.pandas_strftime import PandasStrftime
+
+class Test(unittest.TestCase):
+ def setUp(self):
+ """Set up test data"""
+ self.node = PandasStrftime()
+
+ self.pd_date = pd.DataFrame({
+ "A": [pd.Timestamp("2015-01-23"), pd.Timestamp("2013-06-30")],
+ "B": [pd.Timestamp("2005-02-24"), pd.Timestamp("2015-09-06")],
+ "C": [123.45, 987.65],
+ })
+
+ self.pd_date_time = pd.DataFrame({
+ "A": [pd.Timestamp("2015-01-23 00:12:34"), pd.Timestamp("2013-06-30 12:50:59")],
+ "B": [123.45, 987.65],
+ })
+
+ self.pd_time = pd.DataFrame({
+ "A": [pd.Timestamp("1900-01-01 00:12:34"), pd.Timestamp("1900-01-01 12:50:59")],
+ "B": [123.45, 987.65],
+ })
+
+ def test_date(self):
+ """Test date formatting"""
+ df2 = self.node.f(self.pd_date, column_names="A,B", date_format="%Y-%m-%d")[0]
+ self.assertTrue(ptypes.is_string_dtype(df2["A"]))
+ self.assertTrue(ptypes.is_string_dtype(df2["B"]))
+ self.assertEqual(df2.at[0, "A"], "2015-01-23")
+ self.assertEqual(df2.at[1, "B"], "2015-09-06")
+ self.assertEqual(df2.at[0, "C"], 123.45) # Ensure non-date columns are unchanged
+
+ def test_date_time(self):
+ """Test datetime formatting"""
+ df3 = self.node.f(self.pd_date_time, column_names="A", date_format="%Y-%m-%d %H:%M:%S")[0]
+ self.assertTrue(ptypes.is_string_dtype(df3["A"]))
+ self.assertTrue(ptypes.is_float_dtype(df3["B"]))
+ self.assertEqual(df3.at[0, "A"], "2015-01-23 00:12:34")
+ self.assertEqual(df3.at[1, "A"], "2013-06-30 12:50:59")
+ self.assertEqual(df3.at[0, "B"], 123.45)
+
+ def test_time(self):
+ """Test time formatting"""
+ df4 = self.node.f(self.pd_time, column_names="A", date_format="%H:%M:%S")[0]
+ self.assertTrue(ptypes.is_string_dtype(df4["A"]))
+ self.assertEqual(df4.at[0, "A"], "00:12:34")
+ self.assertEqual(df4.at[1, "A"], "12:50:59")
+ self.assertEqual(df4.at[0, "B"], 123.45)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pandas/utils_index_json_test.py b/tests/pandas_wrapper/utils_index_json_test.py
similarity index 100%
rename from tests/pandas/utils_index_json_test.py
rename to tests/pandas_wrapper/utils_index_json_test.py
diff --git a/tests/pandas/utils_series_json_test.py b/tests/pandas_wrapper/utils_series_json_test.py
similarity index 100%
rename from tests/pandas/utils_series_json_test.py
rename to tests/pandas_wrapper/utils_series_json_test.py