Skip to content

Commit 89c76e6

Browse files
authored
handle datetimes with more than millisec precision (#24)
OpenAPI spec seems to [suggest](https://swagger.io/docs/specification/data-models/data-types/) that the format should be [RFC3339](https://www.rfc-editor.org/rfc/rfc3339#section-5.6). But we do encounter APIs that send out date-time data with millisecond or nanosecond precision date-time. The default date time packages in Julia (TimeZones.jl & Dates) do not support parsing sub-millisecond formats. It seems okay (since the specifications mention only rfc3339) to discard some of the precision while parsing such strings.So until there is a good way to handle it, in this PR we change the date-time parsing to discard some precision and not error out with such data. Also added more date-time related tests.
1 parent 24bb16c commit 89c76e6

File tree

3 files changed

+47
-7
lines changed

3 files changed

+47
-7
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ keywords = ["Swagger", "OpenAPI", "REST"]
44
license = "MIT"
55
desc = "OpenAPI server and client helper for Julia"
66
authors = ["Tanmay Mohapatra <[email protected]>", "JuliaHub"]
7-
version = "0.1.8"
7+
version = "0.1.9"
88

99
[deps]
1010
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

src/datetime.jl

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
11
const DATETIME_FORMATS = [
22
Dates.DateFormat("yyyy-mm-dd"),
3+
Dates.DateFormat("yyyy-mm-ddz"),
34
Dates.DateFormat("yyyy-mm-dd HH:MM:SS"),
45
Dates.DateFormat("yyyy-mm-ddTHH:MM:SS"),
5-
Dates.DateFormat("yyyy-mm-dd HH:MM:SSzzz"),
6-
Dates.DateFormat("yyyy-mm-ddTHH:MM:SSzzz"),
6+
Dates.DateFormat("yyyy-mm-dd HH:MM:SSz"),
7+
Dates.DateFormat("yyyy-mm-ddTHH:MM:SSz"),
78
Dates.DateFormat("yyyy-mm-dd HH:MM:SS.sss"),
89
Dates.DateFormat("yyyy-mm-ddTHH:MM:SS.sss"),
9-
Dates.DateFormat("yyyy-mm-dd HH:MM:SS.sssZ"),
10-
Dates.DateFormat("yyyy-mm-ddTHH:MM:SS.sssZ"),
11-
Dates.DateFormat("yyyy-mm-dd HH:MM:SS.ssszzz"),
12-
Dates.DateFormat("yyyy-mm-ddTHH:MM:SS.ssszzz"),
10+
Dates.DateFormat("yyyy-mm-dd HH:MM:SS.sssz"),
11+
Dates.DateFormat("yyyy-mm-ddTHH:MM:SS.sssz"),
1312
]
1413

14+
const rxdatetime = r"([0-9]{4}-[0-9]{2}-[0-9]{2}[T\s][0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]{1,3})?)[0-9]*([+\-Z][:\.0-9]*)"
15+
function reduce_to_ms_precision(datetimestr::String)
16+
matches = match(rxdatetime, datetimestr)
17+
if matches === nothing
18+
return datetimestr
19+
elseif length(matches.captures) == 2
20+
return matches.captures[1] * matches.captures[2]
21+
else
22+
return matches.captures[1]
23+
end
24+
end
25+
1526
str2zoneddatetime(bytes::Vector{UInt8}) = str2zoneddatetime(String(bytes))
1627
function str2zoneddatetime(str::String)
28+
str = reduce_to_ms_precision(str)
1729
for fmt in DATETIME_FORMATS
1830
try
1931
return ZonedDateTime(str, fmt)
@@ -27,6 +39,7 @@ str2zoneddatetime(datetime::DateTime) = ZonedDateTime(datetime, localzone())
2739

2840
str2datetime(bytes::Vector{UInt8}) = str2datetime(String(bytes))
2941
function str2datetime(str::String)
42+
str = reduce_to_ms_precision(str)
3043
for fmt in DATETIME_FORMATS
3144
try
3245
return DateTime(str, fmt)

test/client/utilstests.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ function test_date()
2121
dt = OpenAPI.str2date(convert(Vector{UInt8}, codeunits(dt_string)))
2222
@test dt == OpenAPI.str2date(Date(dt_now))
2323
@test dt_string == string(dt)
24+
25+
dates = ["2017-11-14", "2020-01-01"]
26+
timesep = [" ", "T"]
27+
times = ["11:03:53", "11:03:53.123", "11:03:53.123456", "11:03:53.123456789"]
28+
timezones = ["", "+10:00", "-10:00", "Z"]
29+
30+
for date in dates
31+
d = OpenAPI.str2date(date)
32+
for sep in timesep
33+
for time in times
34+
t = (length(time) > 12) ? Time(SubString(time, 1, 12)) : Time(time)
35+
for tz in timezones
36+
dt_string = date * sep * time * tz
37+
zdt = OpenAPI.str2zoneddatetime(convert(Vector{UInt8}, codeunits(dt_string)))
38+
dt = OpenAPI.str2datetime(convert(Vector{UInt8}, codeunits(dt_string)))
39+
@test d == Date(zdt)
40+
@test d == Date(dt)
41+
@test t == Time(zdt)
42+
@test t == Time(dt)
43+
end
44+
end
45+
end
46+
end
47+
48+
for tz in timezones
49+
@test OpenAPI.str2date("2017-11-14"*tz) == Date(2017, 11, 14)
50+
end
2451
end
2552

2653
function as_taskfailedexception(ex)

0 commit comments

Comments
 (0)