Skip to content

Commit 2a21d64

Browse files
author
Christopher Doris
committed
feat(numpydates): add constructors and conversions for delta64 types
Implement constructors, accessors, and display for TimeDelta64 and InlineTimeDelta64. Add conversions from Dates.Period and NaT strings. Unify NaT handling via NAT sentinel. Replace unit access with unitpair and store units as (Unit, Cint). Provide defaultunit and a showvalue helper; update isnan to use NAT. Changing units between instances is not yet implemented (except for NaT). BREAKING CHANGE: TimeDelta64 fields changed from unit_base/unit_scale to unit::Tuple{Unit,Cint}. The unit(...) accessor is removed; use unitpair(d) instead. The generic Dates.value(::AbstractTimeDelta64) method was removed; use Dates.value on concrete types.
1 parent 8e6a7dc commit 2a21d64

File tree

3 files changed

+212
-9
lines changed

3 files changed

+212
-9
lines changed
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
abstract type AbstractTimeDelta64 <: Dates.Period end
22

3-
Dates.value(d::AbstractTimeDelta64) = d.value
4-
53
function Base.isnan(d::AbstractTimeDelta64)
6-
value(d) == typemin(Int64)
4+
value(d) == NAT
5+
end
6+
7+
function showvalue(io::IO, d::AbstractTimeDelta64)
8+
u, m = unitpair(d)
9+
if isnan(d)
10+
show(io, "NaT")
11+
else
12+
show(io, value(d))
13+
end
14+
nothing
15+
end
16+
17+
function defaultunit(d::AbstractTimeDelta64)
18+
unitpair(d)
719
end
Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,56 @@
1+
# type
2+
3+
"""
4+
InlineTimeDelta64{unit}(value)
5+
InlineTimeDelta64(value, [unit])
6+
7+
Construct an `InlineTimeDelta64` with the given value and unit.
8+
"""
19
struct InlineTimeDelta64{U} <: AbstractTimeDelta64
210
value::Int64
11+
function InlineTimeDelta64{U}(value::Int64) where {U}
12+
U isa Unit ||
13+
U isa Tuple{Unit,Int} ||
14+
error("U must be a Unit or a Tuple{Unit,Int}")
15+
new{U}(value)
16+
end
17+
end
18+
19+
# accessors
20+
21+
function Dates.value(d::InlineTimeDelta64)
22+
d.value
23+
end
24+
25+
function unitpair(::InlineTimeDelta64{U}) where {U}
26+
unitpair(U)
327
end
428

5-
function unit(::InlineTimeDelta64{U}) where {U}
6-
_unit(U)
29+
# constructors
30+
31+
function InlineTimeDelta64{U}(
32+
v::Union{AbstractTimeDelta64,AbstractString,Dates.Period},
33+
) where {U}
34+
InlineTimeDelta64{U}(value(TimeDelta64(v, U)))
35+
end
36+
37+
function InlineTimeDelta64(
38+
v::Union{AbstractTimeDelta64,AbstractString,Dates.Period},
39+
u::UnitArg = defaultunit(v),
40+
)
41+
InlineTimeDelta64{unitparam(u)}(v)
42+
end
43+
44+
# show
45+
46+
function Base.show(io::IO, d::InlineTimeDelta64)
47+
if get(io, :typeinfo, Any) == typeof(d)
48+
showvalue(io, d)
49+
else
50+
show(io, typeof(d))
51+
print(io, "(")
52+
showvalue(io, d)
53+
print(io, ")")
54+
end
55+
nothing
756
end

src/NumpyDates/TimeDelta64.jl

Lines changed: 146 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,151 @@
1+
# type
2+
3+
"""
4+
TimeDelta64(value, [unit])
5+
6+
Construct a `TimeDelta64` with the given value and unit.
7+
"""
18
struct TimeDelta64 <: AbstractTimeDelta64
29
value::Int64
3-
unit_base::Unit
4-
unit_scale::Cint
10+
unit::Tuple{Unit,Cint}
11+
function TimeDelta64(value::Integer, unit::UnitArg)
12+
new(Int(value), unitpair(unit))
13+
end
14+
end
15+
16+
# accessors
17+
18+
function Dates.value(d::TimeDelta64)
19+
d.value
20+
end
21+
22+
function unitpair(d::TimeDelta64)
23+
d.unit
24+
end
25+
26+
# constructors
27+
28+
# (outer value/unit constructor is unnecessary; inner constructor handles UnitArg)
29+
30+
function TimeDelta64(d::AbstractTimeDelta64, unit::UnitArg = defaultunit(d))
31+
unit = unitpair(unit)
32+
if unit == unitpair(d)
33+
TimeDelta64(value(d), unit)
34+
elseif isnan(d)
35+
TimeDelta64(NAT, unit)
36+
else
37+
error(
38+
"not implemented: changing units: $(unitparam(unitpair(d))) to $(unitparam(unit))",
39+
)
40+
end
541
end
642

7-
function unit(d::TimeDelta64)
8-
(d.unit_base, d.unit_scale)
43+
function TimeDelta64(s::AbstractString, unit::UnitArg = defaultunit(s))
44+
unit = unitpair(unit)
45+
if s in NAT_STRINGS
46+
TimeDelta64(NAT, unit)
47+
else
48+
error(
49+
"Cannot construct TimeDelta64 from string '$s'. Only NaT variants are supported.",
50+
)
51+
end
52+
end
53+
54+
# Convert Dates.Period to TimeDelta64
55+
function TimeDelta64(p::Dates.Period, unit::UnitArg = defaultunit(p))
56+
u, m = unitpair(unit)
57+
if u == YEARS
58+
if p isa Dates.Year
59+
v = value(p)
60+
return TimeDelta64(v ÷ m, unit)
61+
else
62+
error("cannot convert $(typeof(p)) to years")
63+
end
64+
elseif u == MONTHS
65+
if p isa Dates.Month
66+
v = value(p)
67+
return TimeDelta64(v ÷ m, unit)
68+
elseif p isa Dates.Year
69+
v = mul(value(p), 12)
70+
return TimeDelta64(v ÷ m, unit)
71+
else
72+
error("cannot convert $(typeof(p)) to months")
73+
end
74+
elseif u == PICOSECONDS || u == FEMTOSECONDS || u == ATTOSECONDS
75+
# sub-nanosecond units: expand from ns
76+
ns = _period_to_ns(p)
77+
scale = u == PICOSECONDS ? 1_000 : u == FEMTOSECONDS ? 1_000_000 : 1_000_000_000
78+
ns_scaled = mul(ns, scale)
79+
return TimeDelta64(ns_scaled ÷ m, unit)
80+
else
81+
# weeks..nanoseconds: convert via nanoseconds
82+
ns = _period_to_ns(p)
83+
unit_ns = _unit_to_ns(u)
84+
denom = mul(unit_ns, Int64(m))
85+
return TimeDelta64(ns ÷ denom, unit)
86+
end
87+
end
88+
89+
# helpers
90+
91+
# number of nanoseconds per unit (except sub-ns which are handled separately)
92+
function _unit_to_ns(u::Unit)::Int64
93+
if u == WEEKS
94+
mul(mul(mul(mul(Int64(7), Int64(24)), Int64(60)), Int64(60)), Int64(1_000_000_000))
95+
elseif u == DAYS
96+
mul(mul(mul(Int64(24), Int64(60)), Int64(60)), Int64(1_000_000_000))
97+
elseif u == HOURS
98+
mul(mul(Int64(60), Int64(60)), Int64(1_000_000_000))
99+
elseif u == MINUTES
100+
mul(Int64(60), Int64(1_000_000_000))
101+
elseif u == SECONDS
102+
Int64(1_000_000_000)
103+
elseif u == MILLISECONDS
104+
Int64(1_000_000)
105+
elseif u == MICROSECONDS
106+
Int64(1_000)
107+
elseif u == NANOSECONDS
108+
Int64(1)
109+
else
110+
error("Unsupported or sub-nanosecond unit for ns mapping: $u")
111+
end
112+
end
113+
114+
# convert a Dates.Period into total nanoseconds (disallow calendar Year/Month here)
115+
function _period_to_ns(p::Dates.Period)::Int64
116+
if p isa Dates.Week
117+
mul(value(p), _unit_to_ns(WEEKS))
118+
elseif p isa Dates.Day
119+
mul(value(p), _unit_to_ns(DAYS))
120+
elseif p isa Dates.Hour
121+
mul(value(p), _unit_to_ns(HOURS))
122+
elseif p isa Dates.Minute
123+
mul(value(p), _unit_to_ns(MINUTES))
124+
elseif p isa Dates.Second
125+
mul(value(p), _unit_to_ns(SECONDS))
126+
elseif p isa Dates.Millisecond
127+
mul(value(p), _unit_to_ns(MILLISECONDS))
128+
elseif p isa Dates.Microsecond
129+
mul(value(p), _unit_to_ns(MICROSECONDS))
130+
elseif p isa Dates.Nanosecond
131+
value(p)
132+
elseif p isa Dates.Month || p isa Dates.Year
133+
error("cannot convert $(typeof(p)) to time-based units")
134+
else
135+
error("unsupported period type: $(typeof(p))")
136+
end
137+
end
138+
139+
# show
140+
141+
function Base.show(io::IO, d::TimeDelta64)
142+
if get(io, :typeinfo, Any) == typeof(d)
143+
showvalue(io, d)
144+
else
145+
show(io, typeof(d))
146+
print(io, "(")
147+
showvalue(io, d)
148+
print(io, ", ", unitparam(unitpair(d)), ")")
149+
end
150+
nothing
9151
end

0 commit comments

Comments
 (0)