See #2783. Duration parsing no longer uses isodate to remain faithful to formatting.

This commit is contained in:
Dion Moult
2025-04-21 22:39:23 +10:00
parent c1513190d4
commit d1b1b08174
2 changed files with 21 additions and 28 deletions
@@ -18,7 +18,6 @@
import datetime import datetime
import isodate import isodate
import isodate.duration
from re import findall from re import findall
from dateutil import parser from dateutil import parser
from typing import Literal, Union, Any, overload from typing import Literal, Union, Any, overload
@@ -70,32 +69,26 @@ def ifc2datetime(element):
) )
def readable_ifc_duration(string: str) -> str: def readable_ifc_duration(duration):
"""Convert ISO duration to more readable string format. if "T" in duration:
period_duration, time_duration = duration.split("T")
period_duration = period_duration[1:]
else:
period_duration = duration[1:]
time_duration = ""
Examples: result = []
- "P2Y3M1W4DT5H45M30S" -> "2 y 3 m 1 w 4 d 5 h 45 m 30 s" for designator in ("Y", "M", "W", "D"):
- "P2Y3MT30S" -> "2 y 3 m 30 s" if designator in period_duration:
""" value, period_duration = period_duration.split(designator)
duration = isodate.parse_duration(string, as_timedelta_if_possible=False) result.append(f"{value}{designator}")
assert isinstance(duration, isodate.duration.Duration)
final_string = "" if time_duration:
final_string += f"{duration.years} y " if duration.years else "" for designator in ("H", "M", "S"):
final_string += f"{duration.months} m " if duration.months else "" if designator in time_duration:
value, time_duration = time_duration.split(designator)
# Duration stores all other components as timedelta. result.append(f"{value}{designator.lower()}")
tdelta = duration.tdelta return " ".join(result)
weeks, days = divmod(tdelta.days, 7)
final_string += f"{weeks} w " if weeks else ""
final_string += f"{days} d " if days else ""
hours, seconds = divmod(tdelta.seconds, 3600)
minutes, seconds = divmod(seconds, 60)
final_string += f"{hours} h " if hours else ""
final_string += f"{minutes} m " if minutes else ""
final_string += f"{seconds} s" if seconds else ""
return final_string
@overload @overload
@@ -21,7 +21,7 @@ import test.bootstrap
import ifcopenshell.util.date as subject import ifcopenshell.util.date as subject
class TestIsoDuration: class TestReadableIFCDuration():
def test_run(self): def test_run(self):
assert subject.readable_ifc_duration("P2Y3M1W4DT5H45M30S") == "2 y 3 m 1 w 4 d 5 h 45 m 30 s" assert subject.readable_ifc_duration("P2Y3M1W4DT5H45M30S") == "2Y 3M 1W 4D 5h 45m 30s"
assert subject.readable_ifc_duration("P2Y3MT30S") == "2 y 3 m 30 s" assert subject.readable_ifc_duration("PT40H") == "40h"