Skip to content

Commit 2f600fb

Browse files
authored
Add parsing for datetime.time (#1069)
* Add parsing for datetime.time Using same patterns as elsewhere in this file (and avoiding timezones like in DateTime), add support for parsing times from the Python datetime package to Julia's Dates.Time struct * test roundtripeq for Dates.Time * satisfy coverage checker
1 parent 3bb0e19 commit 2f600fb

File tree

2 files changed

+32
-0
lines changed

2 files changed

+32
-0
lines changed

src/pydates.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ const PyDate_HEAD = sizeof(Int)+sizeof(PyPtr)+sizeof(Py_hash_t)+1
6060
const DateType = Ref{PyPtr}(0)
6161
const DateTimeType = Ref{PyPtr}(0)
6262
const DeltaType = Ref{PyPtr}(0)
63+
const TimeType = Ref{PyPtr}(0)
6364
const Date_FromDate = Ref{Ptr{Cvoid}}(0)
6465
const DateTime_FromDateAndTime = Ref{Ptr{Cvoid}}(0)
6566
const Delta_FromDelta = Ref{Ptr{Cvoid}}(0)
67+
const Time_FromTime = Ref{Ptr{Cvoid}}(0)
6668
function init_datetime()
6769
# emulate PyDateTime_IMPORT:
6870
PyDateTimeAPI = unsafe_load(@pycheckn ccall((@pysym :PyCapsule_Import),
@@ -71,9 +73,11 @@ function init_datetime()
7173
DateType[] = PyDateTimeAPI.DateType
7274
DateTimeType[] = PyDateTimeAPI.DateTimeType
7375
DeltaType[] = PyDateTimeAPI.DeltaType
76+
TimeType[] = PyDateTimeAPI.TimeType
7477
Date_FromDate[] = PyDateTimeAPI.Date_FromDate
7578
DateTime_FromDateAndTime[] = PyDateTimeAPI.DateTime_FromDateAndTime
7679
Delta_FromDelta[] = PyDateTimeAPI.Delta_FromDelta
80+
Time_FromTime[] = PyDateTimeAPI.Time_FromTime
7781
end
7882

7983
PyObject(d::Dates.Date) =
@@ -99,6 +103,13 @@ PyDelta_FromDSU(days, seconds, useconds) =
99103

100104
PyObject(p::Dates.Day) = PyDelta_FromDSU(Dates.value(p), 0, 0)
101105

106+
PyObject(t::Dates.Time) =
107+
PyObject(@pycheckn ccall(Time_FromTime[], PyPtr,
108+
(Cint, Cint, Cint, Cint, PyPtr, PyPtr),
109+
Dates.hour(t), Dates.minute(t), Dates.second(t),
110+
Dates.millisecond(t) * 1000,
111+
pynothing[], TimeType[]))
112+
102113
function PyObject(p::Dates.Second)
103114
# normalize to make Cint overflow less likely
104115
s = Dates.value(p)
@@ -120,12 +131,15 @@ end
120131
PyDate_Check(o::PyObject) = pyisinstance(o, DateType[])
121132
PyDateTime_Check(o::PyObject) = pyisinstance(o, DateTimeType[])
122133
PyDelta_Check(o::PyObject) = pyisinstance(o, DeltaType[])
134+
PyTime_Check(o::PyObject) = pyisinstance(o, TimeType[])
123135

124136
function pydate_query(o::PyObject)
125137
if PyDate_Check(o)
126138
return PyDateTime_Check(o) ? Dates.DateTime : Dates.Date
127139
elseif PyDelta_Check(o)
128140
return Dates.Millisecond
141+
elseif PyTime_Check(o)
142+
return Dates.Time
129143
else
130144
return Union{}
131145
end
@@ -163,6 +177,20 @@ function convert(::Type{Dates.Date}, o::PyObject)
163177
end
164178
end
165179

180+
function convert(::Type{Dates.Time}, o::PyObject)
181+
if PyTime_Check(o)
182+
GC.@preserve o let dt = convert(Ptr{UInt8}, PyPtr(o)) + PyDate_HEAD
183+
Dates.Time(unsafe_load(dt,1), unsafe_load(dt,2), # hour, minute
184+
unsafe_load(dt,3), # second
185+
div((UInt(unsafe_load(dt,4)) << 16) |
186+
(UInt(unsafe_load(dt,5)) << 8) |
187+
unsafe_load(dt,6), 1000)) # μs ÷ 1000
188+
end
189+
else
190+
throw(ArgumentError("unknown Time type $o"))
191+
end
192+
end
193+
166194
function delta_dsμ(o::PyObject)
167195
PyDelta_Check(o) || throw(ArgumentError("$o is not a timedelta instance"))
168196
p = GC.@preserve o unsafe_load(convert(Ptr{PyDateTime_Delta{Py_hash_t}}, PyPtr(o)))

test/runtests.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,11 @@ const PyInt = pyversion < v"3" ? Int : Clonglong
167167
end
168168

169169
@test roundtripeq(Dates.Date(2012,3,4))
170+
@test_throws ArgumentError convert(Dates.Date, PyObject(42))
170171
@test roundtripeq(Dates.DateTime(2012,3,4, 7,8,9,11))
172+
@test_throws ArgumentError convert(Dates.DateTime, PyObject(42))
173+
@test roundtripeq(Dates.Time(7,8,9,11))
174+
@test_throws ArgumentError convert(Dates.Time, PyObject(42))
171175
@test roundtripeq(Dates.Millisecond(typemax(Int32)))
172176
@test roundtripeq(Dates.Millisecond(typemin(Int32)))
173177
@test roundtripeq(Dates.Second, Dates.Second(typemax(Int32)))

0 commit comments

Comments
 (0)