diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ad080..0a01b51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.8.0] - 2026-08-31 + +- Time delta nodes can now be converted to seconds (float) and milliseconds (int) ## [1.7.0] - 2026-08-27 -### Added - - String save node now supports append mode diff --git a/README.md b/README.md index aa54a38..b1f82be 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ Date and time manipulation nodes: - **String formatting/parsing**: TimeFormat, TimeParse - **Time calculations**: TimeDelta, TimeAddDelta, TimeSubtractDelta, TimeDifference - **Component extraction**: TimeExtract (year, month, day, hour, etc.) +- **Duration conversion**: TimeDeltaToSeconds (float seconds), TimeDeltaToMilliseconds (int milliseconds) ## Understanding Data Types diff --git a/pyproject.toml b/pyproject.toml index 5b2af7c..82b8ff5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "basic_data_handling" -version = "1.7.0" +version = "1.8.0" description = """Basic Python functions for manipulating data that every programmer is used to, lightweight with no additional dependencies. Supported data types: diff --git a/src/basic_data_handling/time_nodes.py b/src/basic_data_handling/time_nodes.py index ad86c7e..6b7f20b 100644 --- a/src/basic_data_handling/time_nodes.py +++ b/src/basic_data_handling/time_nodes.py @@ -317,6 +317,59 @@ class TimeExtract(ComfyNodeABC): datetime.minute, datetime.second, datetime.microsecond, datetime.weekday()) +class TimeDeltaToSeconds(ComfyNodeABC): + """ + Converts a TIMEDELTA object to a float representing its duration in seconds. + A value of 1.0 corresponds to a time delta of one second. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "delta": (IO.TIMEDELTA, {}), + } + } + + RETURN_TYPES = (IO.FLOAT,) + RETURN_NAMES = ("seconds",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "to_seconds" + + def to_seconds(self, delta: datetime.timedelta) -> tuple[float]: + """ + Converts a timedelta to a float value representing seconds. + """ + return (delta.total_seconds(),) + + +class TimeDeltaToMilliseconds(ComfyNodeABC): + """ + Converts a TIMEDELTA object to an integer representing its duration in milliseconds. + A value of 1 corresponds to a time delta of one millisecond, rounded to the nearest millisecond. + """ + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "delta": (IO.TIMEDELTA, {}), + } + } + + RETURN_TYPES = (IO.INT,) + RETURN_NAMES = ("milliseconds",) + CATEGORY = "Basic/time" + DESCRIPTION = cleandoc(__doc__ or "") + FUNCTION = "to_milliseconds" + + def to_milliseconds(self, delta: datetime.timedelta) -> tuple[int]: + """ + Converts a timedelta to an integer value representing milliseconds, + rounded to the nearest millisecond. + """ + return (round(delta.total_seconds() * 1000),) + + NODE_CLASS_MAPPINGS = { "Basic data handling: TimeNow": TimeNow, "Basic data handling: TimeNowUTC": TimeNowUTC, @@ -329,6 +382,8 @@ NODE_CLASS_MAPPINGS = { "Basic data handling: TimeSubtractDelta": TimeSubtractDelta, "Basic data handling: TimeDifference": TimeDifference, "Basic data handling: TimeExtract": TimeExtract, + "Basic data handling: TimeDeltaToSeconds": TimeDeltaToSeconds, + "Basic data handling: TimeDeltaToMilliseconds": TimeDeltaToMilliseconds, } NODE_DISPLAY_NAME_MAPPINGS = { @@ -343,4 +398,6 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Basic data handling: TimeSubtractDelta": "Subtract Time Delta", "Basic data handling: TimeDifference": "Time Difference", "Basic data handling: TimeExtract": "Extract Time Components", + "Basic data handling: TimeDeltaToSeconds": "Time Delta to Seconds", + "Basic data handling: TimeDeltaToMilliseconds": "Time Delta to Milliseconds", } diff --git a/tests/test_time_nodes.py b/tests/test_time_nodes.py index 63c14ae..cb403de 100644 --- a/tests/test_time_nodes.py +++ b/tests/test_time_nodes.py @@ -13,6 +13,8 @@ from src.basic_data_handling.time_nodes import ( TimeSubtractDelta, TimeDifference, TimeExtract, + TimeDeltaToSeconds, + TimeDeltaToMilliseconds, ) def test_time_now(): @@ -209,3 +211,60 @@ def test_time_extract(): assert microsecond == 123456 # January 2, 2023 was a Monday (weekday 0) assert weekday == 0 + +def test_time_delta_to_seconds(): + node = TimeDeltaToSeconds() + + # One second should equal 1.0 + result = node.to_seconds(timedelta(seconds=1)) + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], float) + assert result[0] == 1.0 + + # Larger durations + result = node.to_seconds(timedelta(days=1, hours=2, minutes=30)) + assert result[0] == 24*60*60 + 2*60*60 + 30*60 + + # Sub-second durations remain fractional + result = node.to_seconds(timedelta(milliseconds=500)) + assert result[0] == 0.5 + + # Negative deltas + result = node.to_seconds(timedelta(seconds=-2)) + assert result[0] == -2.0 + +def test_time_delta_to_milliseconds(): + node = TimeDeltaToMilliseconds() + + # One millisecond should equal 1 + result = node.to_milliseconds(timedelta(milliseconds=1)) + assert isinstance(result, tuple) + assert len(result) == 1 + assert isinstance(result[0], int) + assert result[0] == 1 + + # One second is 1000 milliseconds + result = node.to_milliseconds(timedelta(seconds=1)) + assert result[0] == 1000 + + # Larger durations + result = node.to_milliseconds(timedelta(seconds=90)) + assert result[0] == 90000 + + # Negative deltas + result = node.to_milliseconds(timedelta(milliseconds=-5)) + assert result[0] == -5 + + # Sub-millisecond durations are rounded to the nearest millisecond + # 0.51 ms rounds up to 1 + result = node.to_milliseconds(timedelta(microseconds=510)) + assert result[0] == 1 + + # 0.49 ms rounds down to 0 + result = node.to_milliseconds(timedelta(microseconds=490)) + assert result[0] == 0 + + # 0.999 ms rounds up to 1 + result = node.to_milliseconds(timedelta(microseconds=999)) + assert result[0] == 1