@@ -257,12 +257,31 @@ end
257
257
258
258
# ISO, YMD
259
259
_DEFAULT_TYPE_MAP[:timestamptz ] = ZonedDateTime
260
- const TIMESTAMPTZ_FORMATS = (
261
- dateformat " y-m-d HH:MM:SSz" ,
262
- dateformat " y-m-d HH:MM:SS.sz" ,
263
- dateformat " y-m-d HH:MM:SS.ssz" ,
264
- dateformat " y-m-d HH:MM:SS.sssz" ,
260
+ const TIMESTAMPTZ_FORMATS = Dict (
261
+ ZonedDateTime => (
262
+ dateformat " y-m-d HH:MM:SSz" ,
263
+ dateformat " y-m-d HH:MM:SS.sz" ,
264
+ dateformat " y-m-d HH:MM:SS.ssz" ,
265
+ dateformat " y-m-d HH:MM:SS.sssz" ,
266
+ ),
267
+ UTCDateTime => (
268
+ dateformat " y-m-d HH:MM:SS" ,
269
+ dateformat " y-m-d HH:MM:SS.s" ,
270
+ dateformat " y-m-d HH:MM:SS.ss" ,
271
+ dateformat " y-m-d HH:MM:SS.sss" ,
272
+ ),
265
273
)
274
+
275
+ function _pqparse (:: Type{T} , str:: AbstractString ) where T<: Union{UTCDateTime, ZonedDateTime}
276
+ formats = TIMESTAMPTZ_FORMATS[T]
277
+ for fmt in formats[1 : (end - 1 )]
278
+ parsed = tryparse (T, str, fmt)
279
+ parsed != = nothing && return parsed
280
+ end
281
+
282
+ return parse (T, _trunc_seconds (str), formats[end ])
283
+ end
284
+
266
285
function pqparse (:: Type{ZonedDateTime} , str:: AbstractString )
267
286
if str == " infinity"
268
287
depwarn_timetype_inf ()
@@ -272,12 +291,23 @@ function pqparse(::Type{ZonedDateTime}, str::AbstractString)
272
291
return ZonedDateTime (typemin (DateTime), tz " UTC" )
273
292
end
274
293
275
- for fmt in TIMESTAMPTZ_FORMATS[1 : (end - 1 )]
276
- parsed = tryparse (ZonedDateTime, str, fmt)
277
- parsed != = nothing && return parsed
294
+ return _pqparse (ZonedDateTime, str)
295
+ end
296
+
297
+ function pqparse (:: Type{UTCDateTime} , str:: AbstractString )
298
+ if str == " infinity"
299
+ depwarn_timetype_inf ()
300
+ return UTCDateTime (typemax (DateTime))
301
+ elseif str == " -infinity"
302
+ depwarn_timetype_inf ()
303
+ return UTCDateTime (typemin (DateTime))
278
304
end
279
305
280
- return parse (ZonedDateTime, _trunc_seconds (str), TIMESTAMPTZ_FORMATS[end ])
306
+ # Postgres should give us strings ending with +00, +00:00, -00:00
307
+ # We use the regex below to strip these character off before parsing, iff,
308
+ # the values after the `-`/`+` are `0` or `:`. This means parsing will fail if
309
+ # we're asked to parse a non-UTC string like +04:00.
310
+ return _pqparse (UTCDateTime, replace (str, r" [-|\+ ][0|:]*$" => " " ))
281
311
end
282
312
283
313
_DEFAULT_TYPE_MAP[:date ] = Date
@@ -331,6 +361,10 @@ function Base.parse(::Type{ZonedDateTime}, pqv::PQValue{PQ_SYSTEM_TYPES[:int8]})
331
361
return TimeZones. unix2zdt (parse (Int64, pqv))
332
362
end
333
363
364
+ function Base. parse (:: Type{UTCDateTime} , pqv:: PQValue{PQ_SYSTEM_TYPES[:int8]} )
365
+ return UTCDateTime (unix2datetime (parse (Int64, pqv)))
366
+ end
367
+
334
368
# All postgresql timestamptz are stored in UTC time with the epoch of 2000-01-01.
335
369
const POSTGRES_EPOCH_DATE = Date (" 2000-01-01" )
336
370
const POSTGRES_EPOCH_DATETIME = DateTime (" 2000-01-01" )
@@ -351,6 +385,19 @@ function pqparse(::Type{ZonedDateTime}, ptr::Ptr{UInt8})
351
385
return ZonedDateTime (dt, tz " UTC" ; from_utc= true )
352
386
end
353
387
388
+ function pqparse (:: Type{UTCDateTime} , ptr:: Ptr{UInt8} )
389
+ value = ntoh (unsafe_load (Ptr {Int64} (ptr)))
390
+ if value == typemax (Int64)
391
+ depwarn_timetype_inf ()
392
+ return UTCDateTime (typemax (DateTime))
393
+ elseif value == typemin (Int64)
394
+ depwarn_timetype_inf ()
395
+ return UTCDateTime (typemin (DateTime))
396
+ end
397
+ dt = POSTGRES_EPOCH_DATETIME + Microsecond (value)
398
+ return UTCDateTime (dt)
399
+ end
400
+
354
401
function pqparse (:: Type{DateTime} , ptr:: Ptr{UInt8} )
355
402
value = ntoh (unsafe_load (Ptr {Int64} (ptr)))
356
403
if value == typemax (Int64)
0 commit comments