Skip to content

Commit 992b3c4

Browse files
committed
datetime versioning scheme
Signed-off-by: Kunz, Immanuel <[email protected]>
1 parent a5ed602 commit 992b3c4

File tree

6 files changed

+88
-0
lines changed

6 files changed

+88
-0
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ pyparsing==2.4.7
44
semantic-version==2.8.5
55
semver==2.13.0
66
isort==5.10.1
7+
python-dateutil==2.9.0.post0

src/univers/datetime.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#
2+
# SPDX-License-Identifier: MIT
3+
#
4+
# Visit https://aboutcode.org and https://github.com/aboutcode-org/univers for support and download.
5+
6+
import re
7+
from dateutil.parser import isoparse
8+
9+
class DatetimeVersion:
10+
"""
11+
datetime version.
12+
13+
The timestamp must be RFC3339-compliant, i.e., a subset of ISO8601, where the date AND time are always specified. Therefore, we can use dateutil's ISO-parser but have to check for compliance with the RFC format first via a regex.
14+
"""
15+
16+
VERSION_PATTERN = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$')
17+
18+
def __init__(self, version):
19+
if not self.is_valid(version):
20+
raise InvalidVersionError(version)
21+
22+
version = str(version).strip()
23+
self.original = version
24+
self.parsed_stamp = isoparse(version)
25+
26+
def __eq__(self, other):
27+
return self.parsed_stamp == other.parsed_stamp
28+
29+
def __lt__(self, other):
30+
return self.parsed_stamp < other.parsed_stamp
31+
32+
def __le__(self, other):
33+
return self.parsed_stamp <= other.parsed_stamp
34+
35+
def __gt__(self, other):
36+
return self.parsed_stamp > other.parsed_stamp
37+
38+
def __ge__(self, other):
39+
return self.parsed_stamp >= other.parsed_stamp
40+
41+
@classmethod
42+
def is_valid(cls, string):
43+
return cls.VERSION_PATTERN.match(string)
44+
45+
class InvalidVersionError(ValueError):
46+
pass

src/univers/version_range.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,11 @@ class IntdotVersionRange(VersionRange):
973973
version_class = versions.IntdotVersion
974974

975975

976+
class DatetimeVersionRange(VersionRange):
977+
scheme = "datetime"
978+
version_class = versions.DatetimeVersion
979+
980+
976981
class GenericVersionRange(VersionRange):
977982
scheme = "generic"
978983
version_class = versions.SemverVersion
@@ -1446,6 +1451,7 @@ def build_range_from_snyk_advisory_string(scheme: str, string: Union[str, List])
14461451
"all": AllVersionRange,
14471452
"none": NoneVersionRange,
14481453
"intdot": IntdotVersionRange,
1454+
"datetime": DatetimeVersionRange,
14491455
}
14501456

14511457
PURL_TYPE_BY_GITLAB_SCHEME = {

src/univers/versions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from packaging import version as packaging_version
1010

1111
from univers import arch
12+
from univers import datetime
1213
from univers import debian
1314
from univers import gem
1415
from univers import gentoo
@@ -156,6 +157,16 @@ def is_valid(cls, string):
156157
return intdot.IntdotVersion.is_valid(string)
157158

158159

160+
class DatetimeVersion(Version):
161+
@classmethod
162+
def is_valid(cls, string):
163+
return datetime.DatetimeVersion.is_valid(string)
164+
165+
@classmethod
166+
def build_value(self, string):
167+
return datetime.DatetimeVersion(string)
168+
169+
159170
class GenericVersion(Version):
160171
@classmethod
161172
def is_valid(cls, string):
@@ -726,4 +737,5 @@ def bump(self, index):
726737
LegacyOpensslVersion,
727738
AlpineLinuxVersion,
728739
IntdotVersion,
740+
DatetimeVersion,
729741
]

tests/test_version_range.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,16 @@ def test_version_range_intdot():
366366
assert IntdotVersion("1.3.3alpha") in intdot_range
367367
assert IntdotVersion("1.2.2.pre") not in intdot_range
368368
assert IntdotVersion("1010.23.234203.0") in IntdotVersionRange.from_string("vers:intdot/*")
369+
370+
371+
def test_version_range_datetime():
372+
assert DatetimeVersion("2000-01-01T01:02:03.1234Z") in DatetimeVersionRange.from_string("vers:datetime/*")
373+
assert DatetimeVersion("2021-05-05T01:02:03Z") in DatetimeVersionRange.from_string("vers:datetime/>2021-01-01T01:02:03.1234Z|<2022-01-01T01:02:03.1234Z")
374+
datetime_constraints = DatetimeVersionRange(
375+
constraints=(
376+
VersionConstraint(comparator=">", version=DatetimeVersion(string="2000-01-01T01:02:03Z")),
377+
VersionConstraint(comparator="<", version=DatetimeVersion(string="2002-01-01T01:02:03Z")),
378+
)
379+
)
380+
assert DatetimeVersion("2001-01-01T01:02:03Z") in datetime_constraints
381+

tests/test_versions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from univers.versions import AlpineLinuxVersion
99
from univers.versions import ArchLinuxVersion
1010
from univers.versions import ComposerVersion
11+
from univers.versions import DatetimeVersion
1112
from univers.versions import DebianVersion
1213
from univers.versions import EnhancedSemanticVersion
1314
from univers.versions import GentooVersion
@@ -230,3 +231,12 @@ def test_intdot_version():
230231
assert IntdotVersion("1.2.3.4.6-pre") <= IntdotVersion("2.2.3.4.5.pre")
231232
assert IntdotVersion("1.2.3.4.6-pre") <= IntdotVersion("2.2.3.4.5-10")
232233
assert IntdotVersion("1.2.3.4.6-pre") <= IntdotVersion("2.2.3.4.5-10")
234+
235+
236+
def test_datetime_version():
237+
assert DatetimeVersion("2023-10-28T18:30:00Z") == DatetimeVersion("2023-10-28T18:30:00Z")
238+
assert DatetimeVersion("2023-01-11T10:10:10Z") > DatetimeVersion("2023-01-10T10:10:10Z")
239+
assert DatetimeVersion("2022-10-28T18:30:00Z") < DatetimeVersion("2023-10-28T18:30:00Z")
240+
assert DatetimeVersion("2022-10-28T18:30:00Z") <= DatetimeVersion("2023-10-28T18:30:00Z")
241+
assert DatetimeVersion("2024-10-28T18:30:00Z") > DatetimeVersion("2023-10-28T18:30:00Z")
242+
assert DatetimeVersion("2023-10-28T19:30:00+01:00") == DatetimeVersion("2023-10-28T18:30:00Z")

0 commit comments

Comments
 (0)