Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions stix2/test/v21/test_timestamp_precision.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ def test_stix_datetime():

@pytest.mark.parametrize(
"us, precision, precision_constraint, expected_truncated_us", [
(123456789, Precision.ANY, PrecisionConstraint.EXACT, 123456),
(123456789, Precision.SECOND, PrecisionConstraint.EXACT, 0),
(123456789, Precision.SECOND, PrecisionConstraint.MIN, 123456),
(123456789, Precision.MILLISECOND, PrecisionConstraint.EXACT, 123000),
(123456789, Precision.MILLISECOND, PrecisionConstraint.MIN, 123456),
(123456, Precision.ANY, PrecisionConstraint.EXACT, 123456),
(123456, Precision.SECOND, PrecisionConstraint.EXACT, 0),
(123456, Precision.SECOND, PrecisionConstraint.MIN, 123456),
Expand Down
28 changes: 28 additions & 0 deletions stix2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,32 @@ def format_datetime(dttm):
return ts


def adjust_nanoseconds(value):
"""
This function checks if the timestamp has exactly 9 digits after the decimal
and ends with 'Z'. If it does, it adjusts to microseconds precision (6 digits).
Otherwise, it returns the value unchanged.
"""

# Return the value if there is no decimal or
# if the frac_seconds_part length is not 10
if '.' not in value:
return value

seconds_part, frac_seconds_part = value.split('.', 1)

if len(frac_seconds_part) != 10:
return value

# Ensure the fractional part has exactly 10 characters (9 digits + 1 'Z')
if frac_seconds_part[:9].isdigit() and frac_seconds_part[9] == 'Z':
# Adjust precision to microseconds (6 digits)
microseconds_part = frac_seconds_part[:6]
value = f'{seconds_part}.{microseconds_part}Z'

return value


def parse_into_datetime(
value, precision=Precision.ANY,
precision_constraint=PrecisionConstraint.EXACT,
Expand Down Expand Up @@ -245,6 +271,8 @@ def parse_into_datetime(
else:
# value isn't a date or datetime object so assume it's a string
fmt = _TIMESTAMP_FORMAT_FRAC if "." in value else _TIMESTAMP_FORMAT
# adjust value precision from nanoseconds to microseconds if applicable
value = adjust_nanoseconds(value)
try:
parsed = dt.datetime.strptime(value, fmt)
except (TypeError, ValueError):
Expand Down
Loading