Code review and use isodate library to handle durations for robustly

This commit is contained in:
Dion Moult
2021-05-04 12:52:14 +10:00
parent 8a8bdeeaa3
commit f63479c823
3 changed files with 35 additions and 46 deletions
@@ -1,50 +1,28 @@
import datetime
from re import findall
# https://gist.github.com/spatialtime/c1924a3b178b4fe721fe406e0bf1a1dc
import re
from datetime import timedelta
def format_duration(td):
s = td.seconds
ms = td.microseconds
if ms != 0: # Round microseconds to milliseconds.
ms /= 1000000
ms = round(ms,3)
s += ms
return "P{}DT{}S".format(td.days,s)
def parse_duration(iso_duration):
m = re.match(r'^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:.\d+)?)S)?$',
iso_duration)
if m is None:
raise ValueError("invalid ISO 8601 duration string")
days = 0
hours = 0
minutes = 0
seconds = 0.0
if m[3]:
days = int(m[3])
if m[4]:
hours = int(m[4])
if m[5]:
minutes = int(m[5])
if m[6]:
seconds = float(m[6])
return timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)
def duration2dict(duration):
results = {}
for number, unit in findall("(?P<number>\d+)(?P<period>S|M|H|D|W|Y)", duration):
results[unit] = number
return results
try:
import isodate
except:
pass # Duration parsing not supported
def ifc2datetime(element):
if isinstance(element, str) and element[0] == "P": # IfcDuration
return parse_duration(element)
duration = isodate.parse_duration(element)
if isinstance(duration, datetime.timedelta):
components = {
"days": getattr(duration, "days", 0),
"hours": 0,
"minutes": 0,
"seconds": getattr(duration, "seconds", 0),
}
if components["seconds"]:
components["hours"], components["minutes"], components["seconds"] = [
int(i) for i in str(datetime.timedelta(seconds=components["seconds"])).split(":")
]
duration = isodate.Duration(**components)
return duration
elif isinstance(element, str) and element[2] == ":": # IfcTime
return datetime.time.fromisoformat(element)
elif isinstance(element, str) and ":" in element: # IfcDateTime
@@ -72,11 +50,14 @@ def ifc2datetime(element):
def datetime2ifc(dt, ifc_type):
if isinstance(dt, str) and ifc_type != "IfcDuration":
if isinstance(dt, str):
if ifc_type == "IfcDuration":
return dt
dt = datetime.datetime.fromisoformat(dt)
if ifc_type == "IfcDuration":
return format_duration(dt)
if ifc_type == "IfcTimeStamp":
return isodate.duration_isoformat(dt)
elif ifc_type == "IfcTimeStamp":
return int(dt.timestamp())
elif ifc_type == "IfcDateTime":
if isinstance(dt, datetime.datetime):