Skip to content

Commit

Permalink
deprecate Dates.recur in favour of filter (JuliaLang#19288)
Browse files Browse the repository at this point in the history
* deprecate Dates.recur in favour of filter
  • Loading branch information
simonbyrne authored Nov 17, 2016
1 parent 5310bd6 commit 01ef3c3
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 60 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Deprecated or removed

* infix operator `$` has been deprecated in favor of infix `` or function `xor()` ([#18977]).

* `Dates.recur` has been deprecated in favor of `filter` ([#19288])

Julia v0.5.0 Release Notes
==========================

Expand Down Expand Up @@ -702,3 +704,4 @@ Language tooling improvements
[#18839]: https://github.com/JuliaLang/julia/issues/18839
[#19018]: https://github.com/JuliaLang/julia/issues/19018
[#19233]: https://github.com/JuliaLang/julia/issues/19233
[#19288]: https://github.com/JuliaLang/julia/issues/19288
2 changes: 1 addition & 1 deletion base/dates/Dates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export Period, DatePeriod, TimePeriod,
firstdayofmonth, lastdayofmonth,
firstdayofyear, lastdayofyear,
firstdayofquarter, lastdayofquarter,
adjust, tonext, toprev, tofirst, tolast, recur,
adjust, tonext, toprev, tofirst, tolast,
# io.jl
ISODateTimeFormat, ISODateFormat, DateFormat, RFC1123Format

Expand Down
26 changes: 0 additions & 26 deletions base/dates/adjusters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -260,29 +260,3 @@ function tolast(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month
dt = of <: Month ? lastdayofmonth(dt) : lastdayofyear(dt)
return adjust(ISDAYOFWEEK[dow], dt, Day(-1), 366)
end

function recur{T<:TimeType}(fun::Function, start::T, stop::T; step::Period=Day(1), negate::Bool=false, limit::Int=10000)
((start != stop) & ((step > zero(step)) != (stop > start))) && return T[]
a = T[]
check = start <= stop ? 1 : -1
df = Dates.DateFunction(fun, negate, start)
while true
next = Dates.adjust(df, start, step, limit)
cmp(next, stop) == check && break
push!(a, next)
start = next + step
end
return a
end

"""
recur{T<:TimeType}(func::Function,dr::StepRange{T};negate=false,limit=10000) -> Vector{T}
`func` takes a single TimeType argument and returns a `Bool` indicating whether the input
should be "included" in the final set. `recur` applies `func` over each element in the range
of `dr`, including those elements for which `func` returns `true` in the resulting Array,
unless `negate=true`, then only elements where `func` returns `false` are included.
"""
function recur{T<:TimeType}(fun::Function, dr::StepRange{T};negate::Bool=false, limit::Int=10000)
return recur(fun, first(dr), last(dr); step=step(dr), negate=negate, limit=limit)
end
13 changes: 13 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1116,4 +1116,17 @@ Filesystem.stop_watching(stream::Filesystem._FDWatcher) = depwarn("stop_watching
@deprecate takebuf_array take!
@deprecate takebuf_string(b) String(take!(b))

# #19288
eval(Base.Dates, quote
function recur{T<:TimeType}(fun::Function, dr::StepRange{T}; negate::Bool=false, limit::Int=10000)
depwarn("Dates.recur is deprecated, use filter instead.",:recur)
if negate
filter(x -> !f(x), dr)
else
filter(f, dr)
end
end
recur{T<:TimeType}(fun::Function, start::T, stop::T; step::Period=Day(1), negate::Bool=false, limit::Int=10000) = recur(fun, start:step:stop; negate=negate)
end)

# End deprecations scheduled for 0.6
33 changes: 29 additions & 4 deletions doc/manual/dates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,33 @@ What's going on there? In the first line, we're adding 1 day to January 29th, wh

Tricky? Perhaps. What is an innocent :mod:`Dates` user to do? The bottom line is to be aware that explicitly forcing a certain associativity, when dealing with months, may lead to some unexpected results, but otherwise, everything should work as expected. Thankfully, that's pretty much the extent of the odd cases in date-period arithmetic when dealing with time in UT (avoiding the "joys" of dealing with daylight savings, leap seconds, etc.).

As a bonus, all period arithmetic objects work directly with ranges::

julia> dr = Date(2014,1,29):Date(2014,2,3)
2014-01-29:1 day:2014-02-03

julia> collect(dr)
6-element Array{Date,1}:
2014-01-29
2014-01-30
2014-01-31
2014-02-01
2014-02-02
2014-02-03

julia> dr = Date(2014,1,29):Dates.Month(1):Date(2014,07,29)
2014-01-29:1 month:2014-07-29

julia> collect(dr)
7-element Array{Date,1}:
2014-01-29
2014-02-28
2014-03-29
2014-04-29
2014-05-29
2014-06-29
2014-07-29


Adjuster Functions
------------------
Expand Down Expand Up @@ -305,14 +332,12 @@ This is useful with the do-block syntax for more complex temporal expressions::
end
2014-11-27

The final method in the adjuster API is the :func:`recur` function. :func:`recur` vectorizes the adjustment process by taking a start and stop date (optionally specificed by a :class:`StepRange`), along with a :class:`DateFunction` to specify all valid dates/moments to be returned in the specified range. In this case, the :class:`DateFunction` is often referred to as the "inclusion" function because it specifies (by returning ``true``) which dates/moments should be included in the returned vector of dates.

::
The :func:`Base.filter` method can be used to obtain all valid dates/moments in a specified range::

# Pittsburgh street cleaning; Every 2nd Tuesday from April to November
# Date range from January 1st, 2014 to January 1st, 2015
julia> dr = Dates.Date(2014):Dates.Date(2015);
julia> recur(dr) do x
julia> filter(dr) do x
Dates.dayofweek(x) == Dates.Tue &&
Dates.April <= Dates.month(x) <= Dates.Nov &&
Dates.dayofweekofmonth(x) == 2
Expand Down
55 changes: 26 additions & 29 deletions test/dates/adjusters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -305,36 +305,34 @@ dt = Dates.Date(2014,5,21)

@test Dates.tolast(Dates.Date(0),Dates.Mon) == Dates.Date(0,1,31)

# recur
# filter (was recur)
startdate = Dates.Date(2014,1,1)
stopdate = Dates.Date(2014,2,1)
@test length(Dates.recur(x->true,startdate:stopdate)) == 32
@test length(Dates.recur(x->true,stopdate:Dates.Day(-1):startdate)) == 32
@test length(filter(x->true,startdate:stopdate)) == 32
@test length(filter(x->true,stopdate:Dates.Day(-1):startdate)) == 32

Januarymondays2014 = [Dates.Date(2014,1,6),Dates.Date(2014,1,13),Dates.Date(2014,1,20),Dates.Date(2014,1,27)]
@test Dates.recur(Dates.ismonday,startdate,stopdate) == Januarymondays2014
@test Dates.recur(Dates.ismonday,startdate:stopdate) == Januarymondays2014
@test Dates.recur(x->!Dates.ismonday(x),startdate,stopdate;negate=true) == Januarymondays2014
@test filter(Dates.ismonday,startdate:stopdate) == Januarymondays2014

@test_throws MethodError Dates.recur((x,y)->x+y,Dates.Date(2013):Dates.Date(2014))
@test_throws MethodError filter((x,y)->x+y,Dates.Date(2013):Dates.Date(2014))
@test_throws MethodError Dates.DateFunction((x,y)->x+y, false, Date(0))
@test_throws ArgumentError Dates.DateFunction((dt)->2, false, Date(0))
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,2))) == 32
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,1))) == 1
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,2))) == 2
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,3))) == 3
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,4))) == 4
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,5))) == 5
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,6))) == 6
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,7))) == 7
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2013,1,8))) == 8
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Month(1):Dates.Date(2013,1,1))) == 1
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Day(-1):Dates.Date(2012,1,1))) == 367
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,2))) == 32
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,1))) == 1
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,2))) == 2
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,3))) == 3
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,4))) == 4
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,5))) == 5
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,6))) == 6
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,7))) == 7
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2013,1,8))) == 8
@test length(filter(x->true,Dates.Date(2013):Dates.Month(1):Dates.Date(2013,1,1))) == 1
@test length(filter(x->true,Dates.Date(2013):Dates.Day(-1):Dates.Date(2012,1,1))) == 367
# Empty range
@test length(Dates.recur(x->true,Dates.Date(2013):Dates.Date(2012,1,1))) == 0
@test length(filter(x->true,Dates.Date(2013):Dates.Date(2012,1,1))) == 0

# All leap days in 20th century
@test length(Dates.recur(Dates.Date(1900):Dates.Date(2000)) do x
@test length(filter(Dates.Date(1900):Dates.Date(2000)) do x
Dates.month(x) == Dates.Feb && Dates.day(x) == 29
end) == 24

Expand All @@ -355,7 +353,7 @@ end == Dates.Date(2013,11,28)

# Pittsburgh street cleaning
dr = Dates.Date(2014):Dates.Date(2015)
@test length(Dates.recur(dr) do x
@test length(filter(dr) do x
Dates.dayofweek(x) == Dates.Tue &&
Dates.April < Dates.month(x) < Dates.Nov &&
Dates.dayofweekofmonth(x) == 2
Expand Down Expand Up @@ -412,7 +410,7 @@ const HOLIDAYS = x->isnewyears(x) || isindependenceday(x) ||
ismemorialday(x) || islaborday(x) ||
iscolumbusday(x) || isthanksgiving(x)

@test length(Dates.recur(HOLIDAYS,dr)) == 11
@test length(filter(HOLIDAYS,dr)) == 11

const OBSERVEDHOLIDAYS = x->begin
# If the holiday is on a weekday
Expand All @@ -429,18 +427,17 @@ const OBSERVEDHOLIDAYS = x->begin
end
end

observed = Dates.recur(OBSERVEDHOLIDAYS,Dates.Date(1999):Dates.Date(2000))
observed = filter(OBSERVEDHOLIDAYS,Dates.Date(1999):Dates.Date(2000))
@test length(observed) == 11
@test observed[10] == Dates.Date(1999,12,24)
@test observed[11] == Dates.Date(1999,12,31)

# Get all business/working days of 2014
# Since we have already defined observed holidays,
# we just look at weekend days and use the "negate" keyword of recur
# validate with http://www.workingdays.us/workingdays_holidays_2014.htm
@test length(Dates.recur(Dates.Date(2014):Dates.Date(2015);negate=true) do x
OBSERVEDHOLIDAYS(x) ||
Dates.dayofweek(x) > 5
# we just look at weekend days and negate the result
@test length(filter(Dates.Date(2014):Dates.Date(2015)) do x
!(OBSERVEDHOLIDAYS(x) ||
Dates.dayofweek(x) > 5)
end) == 251

# First day of the next month for each day of 2014
Expand All @@ -450,7 +447,7 @@ end) == 251
# From those goofy email forwards claiming a "special, lucky month"
# that has 5 Fridays, 5 Saturdays, and 5 Sundays and that it only
# occurs every 823 years
@test length(Dates.recur(Date(2000):Dates.Month(1):Date(2016)) do dt
@test length(filter(Date(2000):Dates.Month(1):Date(2016)) do dt
sum = 0
for i = 1:7
sum += Dates.dayofweek(dt) > 4 ? Dates.daysofweekinmonth(dt) : 0
Expand Down

0 comments on commit 01ef3c3

Please sign in to comment.