Skip to content

Commit af40bd3

Browse files
committed
fix(openapi-generator): solve rfc3339 conform dates
1 parent f156098 commit af40bd3

1 file changed

Lines changed: 150 additions & 3 deletions

File tree

tools/openapi-templates/python/api.mustache

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
{{>partial_header}}
44
import warnings
55
import urllib
6+
from datetime import (
7+
datetime,
8+
date,
9+
timedelta,
10+
)
11+
import time
612
from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt
713
from typing import Any, Generator, Dict, List, Optional, Tuple, Union
814
from typing_extensions import Annotated
@@ -125,12 +131,11 @@ class {{classname}}:
125131
if {{paramName}} is not None:
126132
{{#isDateTime}}
127133
if isinstance({{paramName}}, datetime):
134+
formatted_datetime = rfc3339format({{paramName}})
128135
_query_params.append(
129136
(
130137
'{{baseName}}',
131-
{{paramName}}.strftime(
132-
self.api_client.configuration.datetime_format
133-
)
138+
formatted_datetime
134139
)
135140
)
136141
else:
@@ -243,3 +248,145 @@ class {{classname}}:
243248

244249
{{/operation}}
245250
{{/operations}}
251+
252+
253+
254+
def _timezone(utc_offset):
255+
'''
256+
Return a string representing the timezone offset.
257+
258+
>>> _timezone(0)
259+
'+00:00'
260+
>>> _timezone(3600)
261+
'+01:00'
262+
>>> _timezone(-28800)
263+
'-08:00'
264+
>>> _timezone(-8 * 60 * 60)
265+
'-08:00'
266+
>>> _timezone(-30 * 60)
267+
'-00:30'
268+
'''
269+
# Python's division uses floor(), not round() like in other languages:
270+
# -1 / 2 == -1 and not -1 / 2 == 0
271+
# That's why we use abs(utc_offset).
272+
hours = abs(utc_offset) // 3600
273+
minutes = abs(utc_offset) % 3600 // 60
274+
sign = (utc_offset < 0 and '-') or '+'
275+
return '%c%02d:%02d' % (sign, hours, minutes)
276+
277+
def _timedelta_to_seconds(td):
278+
'''
279+
>>> _timedelta_to_seconds(timedelta(hours=3))
280+
10800
281+
>>> _timedelta_to_seconds(timedelta(hours=3, minutes=15))
282+
11700
283+
>>> _timedelta_to_seconds(timedelta(hours=-8))
284+
-28800
285+
'''
286+
return int((td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6)
287+
288+
def _utc_offset(timestamp, use_system_timezone):
289+
'''
290+
Return the UTC offset of `timestamp`. If `timestamp` does not have any `tzinfo`, use
291+
the timezone informations stored locally on the system.
292+
293+
>>> if time.localtime().tm_isdst:
294+
... system_timezone = -time.altzone
295+
... else:
296+
... system_timezone = -time.timezone
297+
>>> _utc_offset(datetime.now(), True) == system_timezone
298+
True
299+
>>> _utc_offset(datetime.now(), False)
300+
0
301+
'''
302+
if (isinstance(timestamp, datetime) and
303+
timestamp.tzinfo is not None):
304+
return _timedelta_to_seconds(timestamp.utcoffset())
305+
elif use_system_timezone:
306+
if timestamp.year < 1970:
307+
# We use 1972 because 1970 doesn't have a leap day (feb 29)
308+
t = time.mktime(timestamp.replace(year=1972).timetuple())
309+
else:
310+
t = time.mktime(timestamp.timetuple())
311+
if time.localtime(t).tm_isdst: # pragma: no cover
312+
return -time.altzone
313+
else:
314+
return -time.timezone
315+
else:
316+
return 0
317+
318+
def _string(d, timezone):
319+
return ('%04d-%02d-%02dT%02d:%02d:%02d%s' %
320+
(d.year, d.month, d.day, d.hour, d.minute, d.second, timezone))
321+
322+
def _string_milliseconds(d, timezone):
323+
return ('%04d-%02d-%02dT%02d:%02d:%02d.%03d%s' %
324+
(d.year, d.month, d.day, d.hour, d.minute, d.second, d.microsecond / 1000, timezone))
325+
326+
def _string_microseconds(d, timezone):
327+
return ('%04d-%02d-%02dT%02d:%02d:%02d.%06d%s' %
328+
(d.year, d.month, d.day, d.hour, d.minute, d.second, d.microsecond, timezone))
329+
330+
def _format(timestamp, string_format, utc, use_system_timezone):
331+
# Try to convert timestamp to datetime
332+
try:
333+
if use_system_timezone:
334+
timestamp = datetime.fromtimestamp(timestamp)
335+
else:
336+
timestamp = datetime.utcfromtimestamp(timestamp)
337+
except TypeError:
338+
pass
339+
340+
if not isinstance(timestamp, date):
341+
raise TypeError('Expected timestamp or date object. Got %r.' %
342+
type(timestamp))
343+
344+
if not isinstance(timestamp, datetime):
345+
timestamp = datetime(*timestamp.timetuple()[:3])
346+
utc_offset = _utc_offset(timestamp, use_system_timezone)
347+
if utc:
348+
# local time -> utc
349+
return string_format(timestamp - timedelta(seconds=utc_offset), 'Z')
350+
else:
351+
return string_format(timestamp , _timezone(utc_offset))
352+
353+
def rfc3339format(timestamp, utc=False, use_system_timezone=True):
354+
'''
355+
Return a string formatted according to the :RFC:`3339`. If called with
356+
`utc=True`, it normalizes `timestamp` to the UTC date. If `timestamp` does
357+
not have any timezone information, uses the local timezone::
358+
359+
>>> d = datetime(2008, 4, 2, 20)
360+
>>> rfc3339(d, utc=True, use_system_timezone=False)
361+
'2008-04-02T20:00:00Z'
362+
>>> rfc3339(d) # doctest: +ELLIPSIS
363+
'2008-04-02T20:00:00...'
364+
365+
If called with `use_system_timezone=False` don't use the local timezone if
366+
`timestamp` does not have timezone informations and consider the offset to UTC
367+
to be zero::
368+
369+
>>> rfc3339(d, use_system_timezone=False)
370+
'2008-04-02T20:00:00+00:00'
371+
372+
`timestamp` must be a `datetime`, `date` or a timestamp as
373+
returned by `time.time()`::
374+
375+
>>> rfc3339(0, utc=True, use_system_timezone=False)
376+
'1970-01-01T00:00:00Z'
377+
>>> rfc3339(date(2008, 9, 6), utc=True,
378+
... use_system_timezone=False)
379+
'2008-09-06T00:00:00Z'
380+
>>> rfc3339(date(2008, 9, 6),
381+
... use_system_timezone=False)
382+
'2008-09-06T00:00:00+00:00'
383+
>>> rfc3339('foo bar') # doctest: +ELLIPSIS
384+
Traceback (most recent call last):
385+
...
386+
TypeError: Expected timestamp or date object. Got <... 'str'>.
387+
388+
For dates before January 1st 1970, the timezones will be the ones used in
389+
1970. It might not be accurate, but on most sytem there is no timezone
390+
information before 1970.
391+
'''
392+
return _format(timestamp, _string, utc, use_system_timezone)

0 commit comments

Comments
 (0)