diff --git a/src/corelibs/string_handling/timestamp_strings.py b/src/corelibs/string_handling/timestamp_strings.py index 59ecb72..375ae4b 100644 --- a/src/corelibs/string_handling/timestamp_strings.py +++ b/src/corelibs/string_handling/timestamp_strings.py @@ -110,3 +110,57 @@ def convert_to_seconds(time_string: str | int | float) -> int: seen_units.append(unit) return total_seconds + + +def seconds_to_string(seconds: str | int | float, show_microseconds: bool = False) -> str: + """ + Convert seconds to compact human readable format (e.g., "1d 2h 3m 4.567s") + Supports negative values with "-" prefix + + Args: + seconds (float): Time in seconds (can be negative) + show_microseconds (bool): Whether to show microseconds precision + + Returns: + str: Compact human readable time format + """ + # if not int or float, return as is + if not isinstance(seconds, (int, float)): + return seconds + # Handle negative values + negative = seconds < 0 + seconds = abs(seconds) + + whole_seconds = int(seconds) + fractional = seconds - whole_seconds + + days = whole_seconds // 86400 + hours = (whole_seconds % 86400) // 3600 + minutes = (whole_seconds % 3600) // 60 + secs = whole_seconds % 60 + + parts: list[str] = [] + if days > 0: + parts.append(f"{days}d") + if hours > 0: + parts.append(f"{hours}h") + if minutes > 0: + parts.append(f"{minutes}m") + + # Handle seconds with fractional part + if fractional > 0: + if show_microseconds: + total_seconds = secs + fractional + formatted = f"{total_seconds:.6f}".rstrip('0').rstrip('.') + parts.append(f"{formatted}s") + else: + total_seconds = secs + fractional + formatted = f"{total_seconds:.3f}".rstrip('0').rstrip('.') + parts.append(f"{formatted}s") + elif secs > 0 or not parts: + parts.append(f"{secs}s") + + result = " ".join(parts) + return f"-{result}" if negative else result + +# __END__ diff --git a/test-run/string_handling/timestamp_strings.py b/test-run/string_handling/timestamp_strings.py index 39d63c6..4cd472d 100644 --- a/test-run/string_handling/timestamp_strings.py +++ b/test-run/string_handling/timestamp_strings.py @@ -4,7 +4,9 @@ timestamp string checks """ -from corelibs.string_handling.timestamp_strings import convert_to_seconds, TimeParseError, TimeUnitError +from corelibs.string_handling.timestamp_strings import ( + seconds_to_string, convert_to_seconds, TimeParseError, TimeUnitError +) def main() -> None: @@ -21,10 +23,9 @@ def main() -> None: "1d 12h", # 1 day, 12 hours "3M 2d 4h", # 3 months, 2 days, 4 hours "45s", # 45 seconds - "1 year 2 months", # 1 year, 2 months + "1 year 2 months", # 1 year, 2 months "2Y 6M 15d 8h 30m 45s", # Complex example - # ] - # invalid_test_cases = [ + # invalid tests "5M 6d 2M", # months appears twice "2h 30m 45s 1h", # hours appears twice "1d 2 days", # days appears twice (short and long form) @@ -43,10 +44,33 @@ def main() -> None: for time_string in test_cases: try: result = convert_to_seconds(time_string) - print(f"{time_string} => {result}") + print(f"Human readable to seconds: {time_string} => {result}") except (TimeParseError, TimeUnitError) as e: print(f"Error encountered for {time_string}: {type(e).__name__}: {e}") + test_values = [ + 'as is string', + -172800.001234, # -2 days, -0.001234 seconds + -90061.789, # -1 day, -1 hour, -1 minute, -1.789 seconds + -3661.456, # -1 hour, -1 minute, -1.456 seconds + -65.123, # -1 minute, -5.123 seconds + -1.5, # -1.5 seconds + -0.001, # -1 millisecond + -0.000001, # -1 microsecond + 0, # 0 seconds + 0.000001, # 1 microsecond + 0.001, # 1 millisecond + 1.5, # 1.5 seconds + 65.123, # 1 minute, 5.123 seconds + 3661.456, # 1 hour, 1 minute, 1.456 seconds + 90061.789, # 1 day, 1 hour, 1 minute, 1.789 seconds + 172800.001234 # 2 days, 0.001234 seconds + ] + + for time_value in test_values: + result = seconds_to_string(time_value, show_microseconds=True) + print(f"Seconds to human readable: {time_value} => {result}") + if __name__ == "__main__": main() diff --git a/uv.lock b/uv.lock index f0a03e7..eb74d11 100644 --- a/uv.lock +++ b/uv.lock @@ -53,7 +53,7 @@ wheels = [ [[package]] name = "corelibs" -version = "0.22.6" +version = "0.23.0" source = { editable = "." } dependencies = [ { name = "jmespath" },