From 9759167ab5346bf55376f34f7af3e86491842f1f Mon Sep 17 00:00:00 2001 From: StableLlama Date: Wed, 16 Jul 2025 19:18:59 +0200 Subject: [PATCH 1/3] Fix formula node (catagory as well as make every input optional) --- src/basic_data_handling/__init__.py | 4 +- src/basic_data_handling/time_nodes.py | 311 ++++++++++++++++++++++++++ 2 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 src/basic_data_handling/time_nodes.py diff --git a/src/basic_data_handling/__init__.py b/src/basic_data_handling/__init__.py index 7c8a138..8dfbb20 100644 --- a/src/basic_data_handling/__init__.py +++ b/src/basic_data_handling/__init__.py @@ -1,7 +1,7 @@ from . import (boolean_nodes, casting_nodes, comparison_nodes, control_flow_nodes, data_list_nodes, dict_nodes, float_nodes, int_nodes, list_nodes, math_nodes, math_formula_node, path_nodes, regex_nodes, set_nodes, - string_nodes) + string_nodes, time_nodes) NODE_CLASS_MAPPINGS = {} NODE_CLASS_MAPPINGS.update(boolean_nodes.NODE_CLASS_MAPPINGS) @@ -19,6 +19,7 @@ NODE_CLASS_MAPPINGS.update(math_nodes.NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(math_formula_node.NODE_CLASS_MAPPINGS) NODE_CLASS_MAPPINGS.update(string_nodes.NODE_CLASS_MAPPINGS) +NODE_CLASS_MAPPINGS.update(time_nodes.NODE_CLASS_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS = {} NODE_DISPLAY_NAME_MAPPINGS.update(boolean_nodes.NODE_DISPLAY_NAME_MAPPINGS) @@ -36,3 +37,4 @@ NODE_DISPLAY_NAME_MAPPINGS.update(math_nodes.NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(math_formula_node.NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(string_nodes.NODE_DISPLAY_NAME_MAPPINGS) +NODE_DISPLAY_NAME_MAPPINGS.update(time_nodes.NODE_DISPLAY_NAME_MAPPINGS) diff --git a/src/basic_data_handling/time_nodes.py b/src/basic_data_handling/time_nodes.py new file mode 100644 index 0000000..dc14364 --- /dev/null +++ b/src/basic_data_handling/time_nodes.py @@ -0,0 +1,311 @@ +from inspect import cleandoc +import datetime +import time + +try: + from comfy.comfy_types.node_typing import IO, ComfyNodeABC +except ImportError: + # Define dummy classes if comfy is not available + class IO: + BOOLEAN = "BOOLEAN" + INT = "INT" + FLOAT = "FLOAT" + STRING = "STRING" + NUMBER = "FLOAT,INT" + ANY = "*" + ComfyNodeABC = object + +# Add custom IO types for DATETIME and TIMEDELTA +IO.DATETIME = "DATETIME" +IO.TIMEDELTA = "TIMEDELTA" + + +class TimeNow(ComfyNodeABC): + """ + Returns the current time and date as a DATETIME object. + Note: Output changes for every run, providing a fresh timestamp each time. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "optional": { + "trigger": (IO.ANY, {"description": "Optional input to trigger execution"}) + } + } + + RETURN_TYPES = (IO.DATETIME,) + RETURN_NAMES = ("now",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "get_now" + + @classmethod + def IS_CHANGED(s, **kwargs): + # Always return a changing value to indicate the output changes every run + return time.time() + + def get_now(self, trigger=None) -> tuple[datetime.datetime]: + """ + Retrieves the current system time. + The optional trigger input can be used to trigger execution. + """ + return (datetime.datetime.now(),) + + +class TimeToUnix(ComfyNodeABC): + """ + Converts a DATETIME object to a Unix timestamp (a float representing seconds since the epoch). + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "dt": (IO.DATETIME, {}), + } + } + + RETURN_TYPES = (IO.FLOAT,) + RETURN_NAMES = ("unix_timestamp",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "to_unix" + + def to_unix(self, dt: datetime.datetime) -> tuple[float]: + """ + Converts the given datetime object to a Unix timestamp. + """ + return (dt.timestamp(),) + + +class UnixToTime(ComfyNodeABC): + """ + Converts a Unix timestamp (float or int) to a DATETIME object. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "unix_timestamp": (IO.NUMBER, {"default": 0.0}), + } + } + + RETURN_TYPES = (IO.DATETIME,) + RETURN_NAMES = ("datetime",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "from_unix" + + def from_unix(self, unix_timestamp: float) -> tuple[datetime.datetime]: + """ + Creates a datetime object from a Unix timestamp. + """ + return (datetime.datetime.fromtimestamp(unix_timestamp),) + + +class TimeFormat(ComfyNodeABC): + """ + Formats a DATETIME object into a string using a specified format code. + Common format codes: %Y (year), %m (month), %d (day), %H (hour), %M (minute), %S (second). + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "dt": (IO.DATETIME, {}), + "format_string": (IO.STRING, {"default": "%Y-%m-%d %H:%M:%S"}), + } + } + + RETURN_TYPES = (IO.STRING,) + RETURN_NAMES = ("formatted_string",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "format_time" + + def format_time(self, dt: datetime.datetime, format_string: str) -> tuple[str]: + """ + Formats a datetime object into a string. + """ + return (dt.strftime(format_string),) + + +class TimeParse(ComfyNodeABC): + """ + Parses a string containing a date and time into a DATETIME object, using a specified format code. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "time_string": (IO.STRING, {}), + "format_string": (IO.STRING, {"default": "%Y-%m-%d %H:%M:%S"}), + } + } + + RETURN_TYPES = (IO.DATETIME,) + RETURN_NAMES = ("datetime",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "parse_time" + + def parse_time(self, time_string: str, format_string: str) -> tuple[datetime.datetime]: + """ + Parses a string into a datetime object. + """ + return (datetime.datetime.strptime(time_string, format_string),) + + +class TimeDelta(ComfyNodeABC): + """ + Creates a TIMEDELTA object, which represents a duration and can be used for date calculations. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "optional": { + "days": (IO.FLOAT, {"default": 0}), + "seconds": (IO.FLOAT, {"default": 0}), + "microseconds": (IO.FLOAT, {"default": 0}), + "milliseconds": (IO.FLOAT, {"default": 0}), + "minutes": (IO.FLOAT, {"default": 0}), + "hours": (IO.FLOAT, {"default": 0}), + "weeks": (IO.FLOAT, {"default": 0}), + } + } + + RETURN_TYPES = (IO.TIMEDELTA,) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "create_delta" + + def create_delta(self, **kwargs: float) -> tuple[datetime.timedelta]: + """ + Creates a timedelta object from the given durations. + """ + return (datetime.timedelta(**kwargs),) + + +class TimeAddDelta(ComfyNodeABC): + """ + Adds a TIMEDELTA (duration) to a DATETIME object, resulting in a new DATETIME. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "dt": (IO.DATETIME, {}), + "delta": (IO.TIMEDELTA, {}), + } + } + + RETURN_TYPES = (IO.DATETIME,) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "add" + + def add(self, dt: datetime.datetime, delta: datetime.timedelta) -> tuple[datetime.datetime]: + """ + Adds a timedelta to a datetime object. + """ + return (dt + delta,) + + +class TimeSubtractDelta(ComfyNodeABC): + """ + Subtracts a TIMEDELTA (duration) from a DATETIME object, resulting in a new DATETIME. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "dt": (IO.DATETIME, {}), + "delta": (IO.TIMEDELTA, {}), + } + } + + RETURN_TYPES = (IO.DATETIME,) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "subtract" + + def subtract(self, dt: datetime.datetime, delta: datetime.timedelta) -> tuple[datetime.datetime]: + """ + Subtracts a timedelta from a datetime object. + """ + return (dt - delta,) + + +class TimeDifference(ComfyNodeABC): + """ + Calculates the difference between two DATETIME objects, returning a TIMEDELTA object. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "dt1": (IO.DATETIME, {}), + "dt2": (IO.DATETIME, {}), + } + } + + RETURN_TYPES = (IO.TIMEDELTA,) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "difference" + + def difference(self, dt1: datetime.datetime, dt2: datetime.datetime) -> tuple[datetime.timedelta]: + """ + Calculates the duration between two datetime objects. + """ + return (dt1 - dt2,) + + +class TimeExtract(ComfyNodeABC): + """ + Extracts individual components (year, month, day, hour, etc.) from a DATETIME object. + Weekday is returned as an integer, where Monday is 0 and Sunday is 6. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { "dt": (IO.DATETIME, {}) } + } + + RETURN_TYPES = (IO.INT, IO.INT, IO.INT, IO.INT, IO.INT, IO.INT, IO.INT, IO.INT) + RETURN_NAMES = ("year", "month", "day", "hour", "minute", "second", "microsecond", "weekday") + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "extract" + + def extract(self, dt: datetime.datetime) -> tuple[int, int, int, int, int, int, int, int]: + """ + Extracts all time components from a datetime object. + """ + return (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, dt.weekday()) + + +NODE_CLASS_MAPPINGS = { + "Basic data handling: TimeNow": TimeNow, + "Basic data handling: TimeToUnix": TimeToUnix, + "Basic data handling: UnixToTime": UnixToTime, + "Basic data handling: TimeFormat": TimeFormat, + "Basic data handling: TimeParse": TimeParse, + "Basic data handling: TimeDelta": TimeDelta, + "Basic data handling: TimeAddDelta": TimeAddDelta, + "Basic data handling: TimeSubtractDelta": TimeSubtractDelta, + "Basic data handling: TimeDifference": TimeDifference, + "Basic data handling: TimeExtract": TimeExtract, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "Basic data handling: TimeNow": "Time Now", + "Basic data handling: TimeToUnix": "Time to Unix Timestamp", + "Basic data handling: UnixToTime": "Unix Timestamp to Time", + "Basic data handling: TimeFormat": "Format Time String", + "Basic data handling: TimeParse": "Parse Time String", + "Basic data handling: TimeDelta": "Create Time Delta", + "Basic data handling: TimeAddDelta": "Add Time Delta", + "Basic data handling: TimeSubtractDelta": "Subtract Time Delta", + "Basic data handling: TimeDifference": "Time Difference", + "Basic data handling: TimeExtract": "Extract Time Components", +} From eae08bbf7b8123300a9208a36bee680a9e2fbe83 Mon Sep 17 00:00:00 2001 From: StableLlama Date: Thu, 17 Jul 2025 21:22:08 +0200 Subject: [PATCH 2/3] Add tests for time --- src/basic_data_handling/time_nodes.py | 39 ++--- tests/test_time_nodes.py | 197 ++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 19 deletions(-) create mode 100644 tests/test_time_nodes.py diff --git a/src/basic_data_handling/time_nodes.py b/src/basic_data_handling/time_nodes.py index dc14364..78c2bf5 100644 --- a/src/basic_data_handling/time_nodes.py +++ b/src/basic_data_handling/time_nodes.py @@ -60,7 +60,7 @@ class TimeToUnix(ComfyNodeABC): def INPUT_TYPES(cls): return { "required": { - "dt": (IO.DATETIME, {}), + "datetime": (IO.DATETIME, {}), } } @@ -70,11 +70,11 @@ def INPUT_TYPES(cls): DESCRIPTION = cleandoc(__doc__ or "") FUNCTION = "to_unix" - def to_unix(self, dt: datetime.datetime) -> tuple[float]: + def to_unix(self, datetime: datetime.datetime) -> tuple[float]: """ Converts the given datetime object to a Unix timestamp. """ - return (dt.timestamp(),) + return (datetime.timestamp(),) class UnixToTime(ComfyNodeABC): @@ -111,7 +111,7 @@ class TimeFormat(ComfyNodeABC): def INPUT_TYPES(cls): return { "required": { - "dt": (IO.DATETIME, {}), + "datetime": (IO.DATETIME, {}), "format_string": (IO.STRING, {"default": "%Y-%m-%d %H:%M:%S"}), } } @@ -122,11 +122,11 @@ def INPUT_TYPES(cls): DESCRIPTION = cleandoc(__doc__ or "") FUNCTION = "format_time" - def format_time(self, dt: datetime.datetime, format_string: str) -> tuple[str]: + def format_time(self, datetime: datetime.datetime, format_string: str) -> tuple[str]: """ Formats a datetime object into a string. """ - return (dt.strftime(format_string),) + return (datetime.strftime(format_string),) class TimeParse(ComfyNodeABC): @@ -193,7 +193,7 @@ class TimeAddDelta(ComfyNodeABC): def INPUT_TYPES(cls): return { "required": { - "dt": (IO.DATETIME, {}), + "datetime": (IO.DATETIME, {}), "delta": (IO.TIMEDELTA, {}), } } @@ -203,11 +203,11 @@ def INPUT_TYPES(cls): DESCRIPTION = cleandoc(__doc__ or "") FUNCTION = "add" - def add(self, dt: datetime.datetime, delta: datetime.timedelta) -> tuple[datetime.datetime]: + def add(self, datetime: datetime.datetime, delta: datetime.timedelta) -> tuple[datetime.datetime]: """ Adds a timedelta to a datetime object. """ - return (dt + delta,) + return (datetime + delta,) class TimeSubtractDelta(ComfyNodeABC): @@ -218,7 +218,7 @@ class TimeSubtractDelta(ComfyNodeABC): def INPUT_TYPES(cls): return { "required": { - "dt": (IO.DATETIME, {}), + "datetime": (IO.DATETIME, {}), "delta": (IO.TIMEDELTA, {}), } } @@ -228,11 +228,11 @@ def INPUT_TYPES(cls): DESCRIPTION = cleandoc(__doc__ or "") FUNCTION = "subtract" - def subtract(self, dt: datetime.datetime, delta: datetime.timedelta) -> tuple[datetime.datetime]: + def subtract(self, datetime: datetime.datetime, delta: datetime.timedelta) -> tuple[datetime.datetime]: """ Subtracts a timedelta from a datetime object. """ - return (dt - delta,) + return (datetime - delta,) class TimeDifference(ComfyNodeABC): @@ -243,8 +243,8 @@ class TimeDifference(ComfyNodeABC): def INPUT_TYPES(cls): return { "required": { - "dt1": (IO.DATETIME, {}), - "dt2": (IO.DATETIME, {}), + "datetime1": (IO.DATETIME, {}), + "datetime2": (IO.DATETIME, {}), } } @@ -253,11 +253,11 @@ def INPUT_TYPES(cls): DESCRIPTION = cleandoc(__doc__ or "") FUNCTION = "difference" - def difference(self, dt1: datetime.datetime, dt2: datetime.datetime) -> tuple[datetime.timedelta]: + def difference(self, datetime1: datetime.datetime, datetime2: datetime.datetime) -> tuple[datetime.timedelta]: """ Calculates the duration between two datetime objects. """ - return (dt1 - dt2,) + return (datetime1 - datetime2,) class TimeExtract(ComfyNodeABC): @@ -268,7 +268,7 @@ class TimeExtract(ComfyNodeABC): @classmethod def INPUT_TYPES(cls): return { - "required": { "dt": (IO.DATETIME, {}) } + "required": { "datetime": (IO.DATETIME, {}) } } RETURN_TYPES = (IO.INT, IO.INT, IO.INT, IO.INT, IO.INT, IO.INT, IO.INT, IO.INT) @@ -277,11 +277,12 @@ def INPUT_TYPES(cls): DESCRIPTION = cleandoc(__doc__ or "") FUNCTION = "extract" - def extract(self, dt: datetime.datetime) -> tuple[int, int, int, int, int, int, int, int]: + def extract(self, datetime: datetime.datetime) -> tuple[int, int, int, int, int, int, int, int]: """ Extracts all time components from a datetime object. """ - return (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, dt.weekday()) + return (datetime.year, datetime.month, datetime.day, datetime.hour, + datetime.minute, datetime.second, datetime.microsecond, datetime.weekday()) NODE_CLASS_MAPPINGS = { diff --git a/tests/test_time_nodes.py b/tests/test_time_nodes.py new file mode 100644 index 0000000..a810a17 --- /dev/null +++ b/tests/test_time_nodes.py @@ -0,0 +1,197 @@ +#import pytest + +from datetime import datetime, timedelta +from src.basic_data_handling.time_nodes import ( + TimeNow, + TimeToUnix, + UnixToTime, + TimeFormat, + TimeParse, + TimeDelta, + TimeAddDelta, + TimeSubtractDelta, + TimeDifference, + TimeExtract, +) + +def test_time_now(): + node = TimeNow() + result = node.get_now() + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], datetime) + + # Test with trigger parameter + result_with_trigger = node.get_now(trigger="any value") + assert isinstance(result_with_trigger, tuple) + assert len(result_with_trigger) == 1 + assert isinstance(result_with_trigger[0], datetime) + +def test_time_to_unix(): + node = TimeToUnix() + test_datetime = datetime(2023, 1, 1, 12, 0, 0) + result = node.to_unix(test_datetime) + + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], float) + # 2023-01-01 12:00:00 should convert to a specific timestamp + expected_timestamp = test_datetime.timestamp() + assert result[0] == expected_timestamp + +def test_unix_to_time(): + node = UnixToTime() + # Test with a specific timestamp (2023-01-01 12:00:00) + test_timestamp = 1672574400.0 # This is 2023-01-01 12:00:00 in UTC + result = node.from_unix(test_timestamp) + + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], datetime) + + # The returned datetime will be in local timezone, so hours might differ + # Just check that the date is correct and hours are reasonable + returned_dt = result[0] + assert returned_dt.year == 2023 + assert returned_dt.month == 1 + assert returned_dt.day == 1 + # Allow for timezone differences (typically within ±12 hours) + assert 0 <= returned_dt.hour < 24 + assert returned_dt.minute == 0 + assert returned_dt.second == 0 + +def test_time_format(): + node = TimeFormat() + test_datetime = datetime(2023, 1, 1, 12, 30, 45) + + # Test with default format + result = node.format_time(test_datetime, "%Y-%m-%d %H:%M:%S") + assert result == ("2023-01-01 12:30:45",) + + # Test with custom format + result = node.format_time(test_datetime, "%d/%m/%Y") + assert result == ("01/01/2023",) + + # Test with time-only format + result = node.format_time(test_datetime, "%H:%M") + assert result == ("12:30",) + +def test_time_parse(): + node = TimeParse() + + # Test standard format + result = node.parse_time("2023-01-01 12:30:45", "%Y-%m-%d %H:%M:%S") + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], datetime) + assert result[0] == datetime(2023, 1, 1, 12, 30, 45) + + # Test custom format + result = node.parse_time("01/01/2023", "%d/%m/%Y") + assert result[0] == datetime(2023, 1, 1, 0, 0, 0) + + # Test time-only format + result = node.parse_time("12:30", "%H:%M") + parsed_datetime = result[0] + assert parsed_datetime.hour == 12 + assert parsed_datetime.minute == 30 + +def test_time_delta(): + node = TimeDelta() + + # Test with days only + result = node.create_delta(days=5) + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], timedelta) + assert result[0].days == 5 + + # Test with multiple parameters + result = node.create_delta(days=1, hours=2, minutes=30) + delta = result[0] + # Convert to seconds for easier comparison + total_seconds = delta.total_seconds() + expected_seconds = 24*60*60 + 2*60*60 + 30*60 # 1 day, 2 hours, 30 minutes + assert total_seconds == expected_seconds + + # Test with all parameters + result = node.create_delta( + days=1, + seconds=30, + microseconds=500, + milliseconds=100, + minutes=5, + hours=2, + weeks=1 + ) + delta = result[0] + # A week plus a day + assert delta.days == 8 + # Convert the remainder to seconds for comparison + remainder_seconds = delta.seconds + expected_remainder = 2*60*60 + 5*60 + 30 + 0.1 # 2 hours, 5 minutes, 30 seconds, 100 milliseconds + assert abs(remainder_seconds - expected_remainder) < 1 + assert delta.microseconds == 100500 + +def test_time_add_delta(): + node = TimeAddDelta() + test_datetime = datetime(2023, 1, 1, 12, 0, 0) + test_delta = timedelta(days=1, hours=2, minutes=30) + + result = node.add(test_datetime, test_delta) + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], datetime) + + expected_datetime = datetime(2023, 1, 2, 14, 30, 0) # 1 day, 2 hours, 30 minutes later + assert result[0] == expected_datetime + +def test_time_subtract_delta(): + node = TimeSubtractDelta() + test_datetime = datetime(2023, 1, 2, 14, 30, 0) + test_delta = timedelta(days=1, hours=2, minutes=30) + + result = node.subtract(test_datetime, test_delta) + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], datetime) + + expected_datetime = datetime(2023, 1, 1, 12, 0, 0) # 1 day, 2 hours, 30 minutes earlier + assert result[0] == expected_datetime + +def test_time_difference(): + node = TimeDifference() + datetime1 = datetime(2023, 1, 2, 14, 30, 0) + datetime2 = datetime(2023, 1, 1, 12, 0, 0) + + result = node.difference(datetime1, datetime2) + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], timedelta) + + expected_delta = timedelta(days=1, hours=2, minutes=30) + assert result[0] == expected_delta + + # Test reverse order (should give negative timedelta) + result = node.difference(datetime2, datetime1) + assert result[0] == -expected_delta + +def test_time_extract(): + node = TimeExtract() + test_datetime = datetime(2023, 1, 2, 14, 30, 45, 123456) + + result = node.extract(test_datetime) + assert isinstance(result, tuple) + assert len(result) == 8 + + year, month, day, hour, minute, second, microsecond, weekday = result + + assert year == 2023 + assert month == 1 + assert day == 2 + assert hour == 14 + assert minute == 30 + assert second == 45 + assert microsecond == 123456 + # January 2, 2023 was a Monday (weekday 0) + assert weekday == 0 From bf51ca06b5a909c6c40e7987c184ede854d152ee Mon Sep 17 00:00:00 2001 From: StableLlama Date: Thu, 17 Jul 2025 21:25:04 +0200 Subject: [PATCH 3/3] Bump to version 0.5.0 --- pyproject.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de28374..2b1e18c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "basic_data_handling" -version = "0.4.7" +version = "0.5.0" description = """Basic Python functions for manipulating data that every programmer is used to, lightweight with no additional dependencies. Supported data types: - ComfyUI native: BOOLEAN, FLOAT, INT, STRING, and data lists -- Python types as custom data types: DICT, LIST, SET +- Python types as custom data types: DICT, LIST, SET, DATETIME, TIMEDELTA Feature categories: - Boolean logic operations @@ -21,7 +21,8 @@ Feature categories: - Mathematical formula node in a safe implementation - String manipulation - File system path handling, including STRING, IMAGE and MASK load and save -- SET operations""" +- SET operations +- time and date handling""" authors = [ {name = "StableLlama"} ]