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