Skip to content

Commit 2f5bb9c

Browse files
committed
feat: Add timestamp argument to STEP export
Allow passing a timestamp value when export STEP files, to generate STEP files with a specific timestamp value in the file header. For example, a null timestamp (`0000-00-00T00:00:00`), or a static timestamp can be used when generated files should be equal if there are no visual changes, such as for file versioning. This commit extends the `#export_step` function, to accept a `timestamp` keyword argument, that can be a string or a `datetime` object. A `datetime` is easier to use from Python.
1 parent 57e71f7 commit 2f5bb9c

File tree

2 files changed

+41
-5
lines changed

2 files changed

+41
-5
lines changed

src/build123d/exporters3d.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
# pylint has trouble with the OCP imports
3030
# pylint: disable=no-name-in-module, import-error
3131

32-
from io import BytesIO
32+
from datetime import datetime
3333
import warnings
34+
from io import BytesIO
3435
from os import PathLike, fsdecode, fspath
35-
from typing import Union
3636

3737
import OCP.TopAbs as ta
3838
from anytree import PreOrderIter
@@ -47,7 +47,11 @@
4747
from OCP.STEPCAFControl import STEPCAFControl_Controller, STEPCAFControl_Writer
4848
from OCP.STEPControl import STEPControl_Controller, STEPControl_StepModelType
4949
from OCP.StlAPI import StlAPI_Writer
50-
from OCP.TCollection import TCollection_AsciiString, TCollection_ExtendedString, TCollection_HAsciiString
50+
from OCP.TCollection import (
51+
TCollection_AsciiString,
52+
TCollection_ExtendedString,
53+
TCollection_HAsciiString,
54+
)
5155
from OCP.TColStd import TColStd_IndexedDataMapOfStringString
5256
from OCP.TDataStd import TDataStd_Name
5357
from OCP.TDF import TDF_Label
@@ -262,6 +266,8 @@ def export_step(
262266
unit: Unit = Unit.MM,
263267
write_pcurves: bool = True,
264268
precision_mode: PrecisionMode = PrecisionMode.AVERAGE,
269+
*, # Too many positional arguments
270+
timestamp: str | datetime | None = None,
265271
) -> bool:
266272
"""export_step
267273
@@ -302,6 +308,11 @@ def export_step(
302308
header = APIHeaderSection_MakeHeader(writer.Writer().Model())
303309
if to_export.label:
304310
header.SetName(TCollection_HAsciiString(to_export.label))
311+
if timestamp is not None:
312+
if isinstance(timestamp, datetime):
313+
header.SetTimeStamp(TCollection_HAsciiString(timestamp.isoformat()))
314+
else:
315+
header.SetTimeStamp(TCollection_HAsciiString(timestamp))
305316
# consider using e.g. the non *Value versions instead
306317
# header.SetAuthorValue(1, TCollection_HAsciiString("Volker"));
307318
# header.SetOrganizationValue(1, TCollection_HAsciiString("myCompanyName"));

tests/test_exporters3d.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,18 @@
3030
import os
3131
import re
3232
import unittest
33-
from typing import Optional
33+
from datetime import datetime
3434
from pathlib import Path
35+
from typing import Optional
36+
from zoneinfo import ZoneInfo
3537

3638
import pytest
3739

3840
from build123d.build_common import GridLocations
3941
from build123d.build_enums import Unit
4042
from build123d.build_line import BuildLine
4143
from build123d.build_sketch import BuildSketch
42-
from build123d.exporters3d import export_gltf, export_step, export_brep, export_stl
44+
from build123d.exporters3d import export_brep, export_gltf, export_step, export_stl
4345
from build123d.geometry import Color, Pos, Vector, VectorLike
4446
from build123d.objects_curve import Line
4547
from build123d.objects_part import Box, Sphere
@@ -144,6 +146,29 @@ def test_export_step_unknown(self):
144146
os.chmod("box_read_only.step", 0o777) # Make the file read/write
145147
os.remove("box_read_only.step")
146148

149+
def test_export_step_timestamp_datetime(self):
150+
b = Box(1, 1, 1)
151+
t = datetime(2025, 5, 6, 21, 30, 25, tzinfo=ZoneInfo("Europe/Amsterdam"))
152+
self.assertTrue(export_step(b, "box.step", timestamp=t))
153+
with open("box.step", "r") as file:
154+
step_data = file.read()
155+
os.remove("box.step")
156+
self.assertEqual(
157+
re.findall("FILE_NAME\\('[^']*','([^']*)'", step_data),
158+
["2025-05-06T21:30:25+02:00"],
159+
)
160+
161+
def test_export_step_timestamp_str(self):
162+
b = Box(1, 1, 1)
163+
self.assertTrue(export_step(b, "box.step", timestamp="0000-00-00T00:00:00"))
164+
with open("box.step", "r") as file:
165+
step_data = file.read()
166+
os.remove("box.step")
167+
self.assertEqual(
168+
re.findall("FILE_NAME\\('[^']*','([^']*)'", step_data),
169+
["0000-00-00T00:00:00"],
170+
)
171+
147172

148173
class TestExportGltf(DirectApiTestCase):
149174
def test_export_gltf(self):

0 commit comments

Comments
 (0)