2121# really be a big deal.
2222reDuration = re .compile (r'^([0-9]{1,5}(h|m|s|ms)){1,4}$' )
2323
24+ # maxDuration_ms is the maximum duration that GEP-2257 can support, in
25+ # milliseconds.
26+ maxDuration_ms = (((99999 * 3600 ) + (59 * 60 ) + 59 ) * 1_000 ) + 999
27+
28+
2429def parse_duration (duration ) -> datetime .timedelta :
2530 """
2631 Parse GEP-2257 Duration format to a datetime.timedelta object.
@@ -32,17 +37,47 @@ def parse_duration(duration) -> datetime.timedelta:
3237 See https://gateway-api.sigs.k8s.io/geps/gep-2257/ for more details.
3338
3439 Input: duration: string
35-
3640 Returns: datetime.timedelta
3741
3842 Raises: ValueError on invalid or unknown input
43+
44+ Examples:
45+ >>> parse_duration("1h")
46+ datetime.timedelta(seconds=3600)
47+ >>> parse_duration("1m")
48+ datetime.timedelta(seconds=60)
49+ >>> parse_duration("1s")
50+ datetime.timedelta(seconds=1)
51+ >>> parse_duration("1ms")
52+ datetime.timedelta(microseconds=1000)
53+ >>> parse_duration("1h1m1s")
54+ datetime.timedelta(seconds=3661)
55+ >>> parse_duration("10s30m1h")
56+ datetime.timedelta(seconds=5410)
57+
58+ Units are always required.
59+ >>> parse_duration("1")
60+ Traceback (most recent call last):
61+ ...
62+ ValueError: Invalid duration format: 1
63+
64+ Floating-point and negative durations are not valid.
65+ >>> parse_duration("1.5m")
66+ Traceback (most recent call last):
67+ ...
68+ ValueError: Invalid duration format: 1.5m
69+ >>> parse_duration("-1m")
70+ Traceback (most recent call last):
71+ ...
72+ ValueError: Invalid duration format: -1m
3973 """
4074
4175 if not reDuration .match (duration ):
4276 raise ValueError ("Invalid duration format: {}" .format (duration ))
4377
4478 return durationpy .from_str (duration )
4579
80+
4681def format_duration (delta : datetime .timedelta ) -> str :
4782 """
4883 Format a datetime.timedelta object to GEP-2257 Duration format.
@@ -59,12 +94,48 @@ def format_duration(delta: datetime.timedelta) -> str:
5994
6095 Raises: ValueError if the timedelta given cannot be expressed as a
6196 GEP-2257 Duration.
97+
98+ Examples:
99+ >>> format_duration(datetime.timedelta(seconds=3600))
100+ '1h'
101+ >>> format_duration(datetime.timedelta(seconds=60))
102+ '1m'
103+ >>> format_duration(datetime.timedelta(seconds=1))
104+ '1s'
105+ >>> format_duration(datetime.timedelta(microseconds=1000))
106+ '1ms'
107+ >>> format_duration(datetime.timedelta(seconds=5410))
108+ '1h30m10s'
109+
110+ The zero duration is always "0s".
111+ >>> format_duration(datetime.timedelta(0))
112+ '0s'
113+
114+ Sub-millisecond precision is not allowed.
115+ >>> format_duration(datetime.timedelta(microseconds=100))
116+ Traceback (most recent call last):
117+ ...
118+ ValueError: Cannot express sub-millisecond precision in GEP-2257: 0:00:00.000100
119+
120+ Negative durations are not allowed.
121+ >>> format_duration(datetime.timedelta(seconds=-1))
122+ Traceback (most recent call last):
123+ ...
124+ ValueError: Cannot express negative durations in GEP-2257: -1 day, 23:59:59
62125 """
63126
64127 # Short-circuit if we have a zero delta.
65128 if delta == datetime .timedelta (0 ):
66129 return "0s"
67130
131+ # Check range early.
132+ if delta < datetime .timedelta (0 ):
133+ raise ValueError ("Cannot express negative durations in GEP-2257: {}" .format (delta ))
134+
135+ if delta > datetime .timedelta (milliseconds = maxDuration_ms ):
136+ raise ValueError (
137+ "Cannot express durations longer than 99999h59m59s999ms in GEP-2257: {}" .format (delta ))
138+
68139 # durationpy.to_str() is happy to use floating-point seconds, which
69140 # GEP-2257 is _not_ happy with. So start by peeling off any microseconds
70141 # from our delta.
@@ -90,8 +161,4 @@ def format_duration(delta: datetime.timedelta) -> str:
90161
91162 delta_str += f"{ delta_ms } ms"
92163
93- if not reDuration .match (delta_str ):
94- raise ValueError ("Invalid duration format: {}" .format (durationpy .to_str (delta )))
95-
96164 return delta_str
97-
0 commit comments